keycloak-memoizeit

Changes

distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/sun/jdk/jgss/main/module.xml 35(+0 -35)

pom.xml 7(+5 -2)

Details

diff --git a/common/src/main/java/org/keycloak/common/constants/KerberosConstants.java b/common/src/main/java/org/keycloak/common/constants/KerberosConstants.java
index 4089a0e..b644f16 100644
--- a/common/src/main/java/org/keycloak/common/constants/KerberosConstants.java
+++ b/common/src/main/java/org/keycloak/common/constants/KerberosConstants.java
@@ -17,6 +17,9 @@
 
 package org.keycloak.common.constants;
 
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.Oid;
+
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
@@ -31,19 +34,33 @@ public class KerberosConstants {
     /**
      * OID of SPNEGO mechanism. See http://www.oid-info.com/get/1.3.6.1.5.5.2
      */
-    public static final String SPNEGO_OID = "1.3.6.1.5.5.2";
+    private static final String SPNEGO_OID_STR = "1.3.6.1.5.5.2";
+    public static final Oid SPNEGO_OID;
 
 
     /**
      * OID of Kerberos v5 mechanism. See http://www.oid-info.com/get/1.2.840.113554.1.2.2
      */
-    public static final String KRB5_OID = "1.2.840.113554.1.2.2";
+    private static final String KRB5_OID_STR = "1.2.840.113554.1.2.2";
+    public static final Oid KRB5_OID;
 
 
     /**
      * OID of Kerberos v5 name. See http://www.oid-info.com/get/1.2.840.113554.1.2.2.1
      */
-    public static final String KRB5_NAME_OID = "1.2.840.113554.1.2.2.1";
+    private static final String KRB5_NAME_OID_STR = "1.2.840.113554.1.2.2.1";
+    public static final Oid KRB5_NAME_OID;
+
+
+    static {
+        try {
+            KRB5_OID = new Oid(KerberosConstants.KRB5_OID_STR);
+            KRB5_NAME_OID = new Oid(KerberosConstants.KRB5_NAME_OID_STR);
+            SPNEGO_OID = new Oid(KerberosConstants.SPNEGO_OID_STR);
+        } catch (GSSException e) {
+            throw new RuntimeException(e);
+        }
+    }
 
 
     /**
diff --git a/common/src/main/java/org/keycloak/common/util/KerberosJdkProvider.java b/common/src/main/java/org/keycloak/common/util/KerberosJdkProvider.java
new file mode 100644
index 0000000..00a5f12
--- /dev/null
+++ b/common/src/main/java/org/keycloak/common/util/KerberosJdkProvider.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.common.util;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.keycloak.common.constants.KerberosConstants;
+
+/**
+ * Provides abstraction to handle differences between various JDK vendors (Sun, IBM)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class KerberosJdkProvider {
+
+    public abstract Configuration createJaasConfigurationForServer(String keytab, String serverPrincipal, boolean debug);
+    public abstract Configuration createJaasConfigurationForUsernamePasswordLogin(boolean debug);
+
+    public abstract KerberosTicket gssCredentialToKerberosTicket(KerberosTicket kerberosTicket, GSSCredential gssCredential);
+
+
+
+    public GSSCredential kerberosTicketToGSSCredential(KerberosTicket kerberosTicket) {
+        return kerberosTicketToGSSCredential(kerberosTicket, GSSCredential.DEFAULT_LIFETIME, GSSCredential.INITIATE_ONLY);
+    }
+
+    // Actually same on both JDKs
+    public GSSCredential kerberosTicketToGSSCredential(KerberosTicket kerberosTicket, final int lifetime, final int usage) {
+        try {
+            final GSSManager gssManager = GSSManager.getInstance();
+
+            KerberosPrincipal kerberosPrincipal = kerberosTicket.getClient();
+            String krbPrincipalName = kerberosTicket.getClient().getName();
+            final GSSName gssName = gssManager.createName(krbPrincipalName, KerberosConstants.KRB5_NAME_OID);
+
+            Set<KerberosPrincipal> principals = Collections.singleton(kerberosPrincipal);
+            Set<GSSName> publicCreds = Collections.singleton(gssName);
+            Set<KerberosTicket> privateCreds = Collections.singleton(kerberosTicket);
+            Subject subject = new Subject(false, principals, publicCreds, privateCreds);
+
+            return Subject.doAs(subject, new PrivilegedExceptionAction<GSSCredential>() {
+
+                @Override
+                public GSSCredential run() throws Exception {
+                    return gssManager.createCredential(gssName, lifetime, KerberosConstants.KRB5_OID, usage);
+                }
+
+            });
+        } catch (Exception e) {
+            throw new KerberosSerializationUtils.KerberosSerializationException("Unexpected exception during convert KerberosTicket to GSSCredential", e);
+        }
+    }
+
+
+    public static KerberosJdkProvider getProvider() {
+        if (KerberosSerializationUtils.JAVA_INFO.contains("IBM")) {
+            return new IBMJDKProvider();
+        } else {
+            return new SunJDKProvider();
+        }
+    }
+
+
+    // IMPL Subclasses
+
+
+    // Works for Oracle and OpenJDK
+    private static class SunJDKProvider extends KerberosJdkProvider {
+
+
+        @Override
+        public Configuration createJaasConfigurationForServer(final String keytab, final String serverPrincipal, final boolean debug) {
+            return new Configuration() {
+
+                @Override
+                public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                    Map<String, Object> options = new HashMap<>();
+                    options.put("storeKey", "true");
+                    options.put("doNotPrompt", "true");
+                    options.put("isInitiator", "false");
+                    options.put("useKeyTab", "true");
+
+                    options.put("keyTab", keytab);
+                    options.put("principal", serverPrincipal);
+                    options.put("debug", String.valueOf(debug));
+                    AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
+                    return new AppConfigurationEntry[] { kerberosLMConfiguration };
+                }
+            };
+        }
+
+
+        @Override
+        public Configuration createJaasConfigurationForUsernamePasswordLogin(final boolean debug) {
+            return new Configuration() {
+
+                @Override
+                public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                    Map<String, Object> options = new HashMap<>();
+                    options.put("storeKey", "true");
+                    options.put("debug", String.valueOf(debug));
+                    AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
+                    return new AppConfigurationEntry[] { kerberosLMConfiguration };
+                }
+            };
+        }
+
+
+        // Note: input kerberosTicket is null for Sun based JDKs
+        @Override
+        public KerberosTicket gssCredentialToKerberosTicket(KerberosTicket kerberosTicket, GSSCredential gssCredential) {
+            try {
+                Class<?> gssUtil = Class.forName("com.sun.security.jgss.GSSUtil");
+                Method createSubject = gssUtil.getMethod("createSubject", GSSName.class, GSSCredential.class);
+                Subject subject = (Subject) createSubject.invoke(null, null, gssCredential);
+                Set<KerberosTicket> kerberosTickets = subject.getPrivateCredentials(KerberosTicket.class);
+                Iterator<KerberosTicket> iterator = kerberosTickets.iterator();
+                if (iterator.hasNext()) {
+                    return iterator.next();
+                } else {
+                    throw new KerberosSerializationUtils.KerberosSerializationException("Not available kerberosTicket in subject credentials. Subject was: " + subject.toString());
+                }
+            } catch (KerberosSerializationUtils.KerberosSerializationException ke) {
+                throw ke;
+            } catch (Exception e) {
+                throw new KerberosSerializationUtils.KerberosSerializationException("Unexpected error during convert GSSCredential to KerberosTicket", e);
+            }
+        }
+
+    }
+
+
+    // Works for IBM JDK
+    private static class IBMJDKProvider extends KerberosJdkProvider {
+
+        @Override
+        public Configuration createJaasConfigurationForServer(String keytab, final String serverPrincipal, final boolean debug) {
+            final String keytabUrl = getKeytabURL(keytab);
+
+            return new Configuration() {
+
+                @Override
+                public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                    Map<String, Object> options = new HashMap<>();
+                    options.put("noAddress", "true");
+                    options.put("credsType","acceptor");
+                    options.put("useKeytab", keytabUrl);
+                    options.put("principal", serverPrincipal);
+                    options.put("debug", String.valueOf(debug));
+
+                    AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
+                    return new AppConfigurationEntry[] { kerberosLMConfiguration };
+                }
+            };
+        }
+
+        private String getKeytabURL(String keytab) {
+            try {
+                return new File(keytab).toURI().toURL().toString();
+            } catch (MalformedURLException mfe) {
+                System.err.println("Invalid keytab location specified in configuration: " + keytab);
+                mfe.printStackTrace();
+                return keytab;
+            }
+        }
+
+
+        @Override
+        public Configuration createJaasConfigurationForUsernamePasswordLogin(final boolean debug) {
+            return new Configuration() {
+
+                @Override
+                public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                    Map<String, Object> options = new HashMap<>();
+                    options.put("credsType","initiator");
+                    options.put("noAddress", "true");
+                    options.put("debug", String.valueOf(debug));
+                    AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
+                    return new AppConfigurationEntry[] { kerberosLMConfiguration };
+                }
+            };
+        }
+
+
+        // For IBM, kerberosTicket was set on JAAS Subject, so we can just return it
+        @Override
+        public KerberosTicket gssCredentialToKerberosTicket(KerberosTicket kerberosTicket, GSSCredential gssCredential) {
+            if (kerberosTicket == null) {
+                throw new KerberosSerializationUtils.KerberosSerializationException("Not available kerberosTicket in subject credentials in IBM JDK");
+            } else {
+                return kerberosTicket;
+            }
+        }
+    }
+}
diff --git a/common/src/main/java/org/keycloak/common/util/KerberosSerializationUtils.java b/common/src/main/java/org/keycloak/common/util/KerberosSerializationUtils.java
index bc9d174..17a11e4 100644
--- a/common/src/main/java/org/keycloak/common/util/KerberosSerializationUtils.java
+++ b/common/src/main/java/org/keycloak/common/util/KerberosSerializationUtils.java
@@ -25,22 +25,13 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
-import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Set;
 
+import javax.security.auth.Subject;
 import javax.security.auth.kerberos.KerberosTicket;
 
 import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.Oid;
-import org.keycloak.common.constants.KerberosConstants;
-import org.keycloak.common.util.reflections.Reflections;
-import sun.security.jgss.GSSCredentialImpl;
-import sun.security.jgss.GSSManagerImpl;
-import sun.security.jgss.krb5.Krb5InitCredential;
-import sun.security.jgss.krb5.Krb5NameElement;
-import sun.security.jgss.spi.GSSCredentialSpi;
-import sun.security.krb5.Credentials;
 
 /**
  * Provides serialization/deserialization of kerberos {@link org.ietf.jgss.GSSCredential}, so it can be transmitted from auth-server to the application
@@ -50,18 +41,9 @@ import sun.security.krb5.Credentials;
  */
 public class KerberosSerializationUtils {
 
-    public static final Oid KRB5_OID;
-    public static final Oid KRB5_NAME_OID;
     public static final String JAVA_INFO;
 
     static {
-        try {
-            KRB5_OID = new Oid(KerberosConstants.KRB5_OID);
-            KRB5_NAME_OID = new Oid(KerberosConstants.KRB5_NAME_OID);
-        } catch (GSSException e) {
-            throw new RuntimeException(e);
-        }
-
         String javaVersion = System.getProperty("java.version");
         String javaRuntimeVersion = System.getProperty("java.runtime.version");
         String javaVendor = System.getProperty("java.vendor");
@@ -72,50 +54,17 @@ public class KerberosSerializationUtils {
     private KerberosSerializationUtils() {
     }
 
-    public static String serializeCredential(GSSCredential gssCredential) throws KerberosSerializationException {
+    public static String serializeCredential(KerberosTicket kerberosTicket, GSSCredential gssCredential) throws KerberosSerializationException {
         try {
             if (gssCredential == null) {
                 throw new KerberosSerializationException("Null credential given as input");
             }
 
-            if (!(gssCredential instanceof GSSCredentialImpl)) {
-                throw new KerberosSerializationException("Unknown credential type: " + gssCredential.getClass());
-            }
-
-            GSSCredentialImpl gssCredImpl = (GSSCredentialImpl) gssCredential;
-            Oid[] mechs = gssCredImpl.getMechs();
-
-            for (Oid oid : mechs) {
-                if (oid.equals(KRB5_OID)) {
-                    int usage = gssCredImpl.getUsage(oid);
-                    boolean initiate = (usage == GSSCredential.INITIATE_ONLY || usage == GSSCredential.INITIATE_AND_ACCEPT);
-
-                    GSSCredentialSpi credentialSpi = gssCredImpl.getElement(oid, initiate);
-                    if (credentialSpi instanceof Krb5InitCredential) {
-                        Krb5InitCredential credential = (Krb5InitCredential) credentialSpi;
-                        KerberosTicket kerberosTicket = new KerberosTicket(credential.getEncoded(),
-                                credential.getClient(),
-                                credential.getServer(),
-                                credential.getSessionKey().getEncoded(),
-                                credential.getSessionKeyType(),
-                                credential.getFlags(),
-                                credential.getAuthTime(),
-                                credential.getStartTime(),
-                                credential.getEndTime(),
-                                credential.getRenewTill(),
-                                credential.getClientAddresses());
-                        return serialize(kerberosTicket);
-                    } else {
-                        throw new KerberosSerializationException("Unsupported type of credentialSpi: " + credentialSpi.getClass());
-                    }
-                }
-            }
+            kerberosTicket = KerberosJdkProvider.getProvider().gssCredentialToKerberosTicket(kerberosTicket, gssCredential);
 
-            throw new KerberosSerializationException("Kerberos credential not found. Available mechanisms: " + mechs);
+            return serialize(kerberosTicket);
         } catch (IOException e) {
-            throw new KerberosSerializationException("Exception occured", e);
-        } catch (GSSException e) {
-            throw new KerberosSerializationException("Exception occured", e);
+            throw new KerberosSerializationException("Unexpected exception when serialize GSSCredential", e);
         }
     }
 
@@ -132,32 +81,12 @@ public class KerberosSerializationUtils {
             }
 
             KerberosTicket ticket = (KerberosTicket) deserializedCred;
-            String fullName = ticket.getClient().getName();
-
-            Method getInstance = Reflections.findDeclaredMethod(Krb5NameElement.class, "getInstance", String.class, Oid.class);
-            Krb5NameElement krb5Name = Reflections.invokeMethod(true, getInstance, Krb5NameElement.class, null, fullName, KRB5_NAME_OID);
-
-            Credentials krb5CredsInternal = new Credentials(
-                    ticket.getEncoded(),
-                    ticket.getClient().getName(),
-                    ticket.getServer().getName(),
-                    ticket.getSessionKey().getEncoded(),
-                    ticket.getSessionKeyType(),
-                    ticket.getFlags(),
-                    ticket.getAuthTime(),
-                    ticket.getStartTime(),
-                    ticket.getEndTime(),
-                    ticket.getRenewTill(),
-                    ticket.getClientAddresses()
-            );
-
-            Method getInstance2 = Reflections.findDeclaredMethod(Krb5InitCredential.class, "getInstance", Krb5NameElement.class, Credentials.class);
-            Krb5InitCredential initCredential = Reflections.invokeMethod(true, getInstance2, Krb5InitCredential.class, null, krb5Name, krb5CredsInternal);
-
-            GSSManagerImpl manager = (GSSManagerImpl) GSSManager.getInstance();
-            return new GSSCredentialImpl(manager, initCredential);
+
+            return KerberosJdkProvider.getProvider().kerberosTicketToGSSCredential(ticket);
+        } catch (KerberosSerializationException ke) {
+            throw ke;
         } catch (Exception ioe) {
-            throw new KerberosSerializationException("Exception occured", ioe);
+            throw new KerberosSerializationException("Unexpected exception when deserialize GSSCredential", ioe);
         }
     }
 
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
index 0d05dc2..4afb80d 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
index 7821396..1d1be60 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
@@ -33,7 +33,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
index 0d05dc2..4afb80d 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
index ab0b69a..f40e1e3 100755
--- a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
@@ -33,7 +33,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
index 0d05dc2..4afb80d 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
index ab0b69a..f40e1e3 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
@@ -33,7 +33,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-common/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-common/main/module.xml
index 04f1e61..81578ae 100755
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-core/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-core/main/module.xml
index 0ca522d..885b944 100755
--- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-core/main/module.xml
@@ -33,7 +33,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
index 04f1e61..81578ae 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
index ca35e20..40b0e69 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml
@@ -33,7 +33,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
index 0d05dc2..4afb80d 100755
--- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
index 0d05dc2..4afb80d 100755
--- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
+++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -28,7 +28,6 @@
         <module name="javax.api"/>
         <module name="javax.activation.api"/>
         <module name="sun.jdk" optional="true" />
-        <module name="sun.jdk.jgss" optional="true" />
     </dependencies>
 
 </module>
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml b/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
index d60c1cd..d1d5838 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/client-registration.xml
@@ -155,7 +155,7 @@ Authorization: basic BASE64(client-id + ':' + client-secret)
         </para>
         <para>
             To retrieve the Adapter Configuration then do a HTTP GET to:
-            <literal>&lt;KEYCLOAK URL&gt;//realms/&lt;realm&gt;/clients-registrations/installation/&lt;client id&gt;</literal>
+            <literal>&lt;KEYCLOAK URL&gt;//realms/&lt;realm&gt;/clients-registrations/install/&lt;client id&gt;</literal>
         </para>
         <para>
             No authentication is required for public clients. This means that for the JavaScript adapter you can
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
index c2eccde..4d3bf62 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/CommonKerberosConfig.java
@@ -52,7 +52,7 @@ public abstract class CommonKerberosConfig {
         return getConfig().get(KerberosConstants.KEYTAB);
     }
 
-    public boolean getDebug() {
+    public boolean isDebug() {
         return Boolean.valueOf(getConfig().get(KerberosConstants.DEBUG));
     }
 
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosServerSubjectAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosServerSubjectAuthenticator.java
index 2a155ed..aeaa074 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosServerSubjectAuthenticator.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosServerSubjectAuthenticator.java
@@ -17,16 +17,18 @@
 
 package org.keycloak.federation.kerberos.impl;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.io.IOException;
 
 import javax.security.auth.Subject;
-import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.KerberosJdkProvider;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
 
 /**
@@ -36,20 +38,32 @@ public class KerberosServerSubjectAuthenticator {
 
     private static final Logger logger = Logger.getLogger(KerberosServerSubjectAuthenticator.class);
 
+    private static final CallbackHandler NO_CALLBACK_HANDLER = new CallbackHandler() {
+
+        @Override
+        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            throw new UnsupportedCallbackException(callbacks[0]);
+        }
+    };
+
+
     private final CommonKerberosConfig config;
     private LoginContext loginContext;
 
+
     public KerberosServerSubjectAuthenticator(CommonKerberosConfig config) {
         this.config = config;
     }
 
+
     public Subject authenticateServerSubject() throws LoginException {
         Configuration config = createJaasConfiguration();
-        loginContext = new LoginContext("does-not-matter", null, null, config);
+        loginContext = new LoginContext("does-not-matter", null, NO_CALLBACK_HANDLER, config);
         loginContext.login();
         return loginContext.getSubject();
     }
 
+
     public void logoutServerSubject() {
         if (loginContext != null) {
             try {
@@ -60,24 +74,9 @@ public class KerberosServerSubjectAuthenticator {
         }
     }
 
+
     protected Configuration createJaasConfiguration() {
-        return new Configuration() {
-
-            @Override
-            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-                Map<String, Object> options = new HashMap<String, Object>();
-                options.put("storeKey", "true");
-                options.put("doNotPrompt", "true");
-                options.put("isInitiator", "false");
-                options.put("useKeyTab", "true");
-
-                options.put("keyTab", config.getKeyTab());
-                options.put("principal", config.getServerPrincipal());
-                options.put("debug", String.valueOf(config.getDebug()));
-                AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
-                return new AppConfigurationEntry[] { kerberosLMConfiguration };
-            }
-        };
+        return KerberosJdkProvider.getProvider().createJaasConfigurationForServer(config.getKeyTab(), config.getServerPrincipal(), config.isDebug());
     }
 
 }
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
index f18cf2c..2254a6e 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/KerberosUsernamePasswordAuthenticator.java
@@ -33,6 +33,7 @@ import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.KerberosJdkProvider;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
 import org.keycloak.models.ModelException;
 
@@ -58,7 +59,7 @@ public class KerberosUsernamePasswordAuthenticator {
      * @return true if user available
      */
     public boolean isUserAvailable(String username) {
-        logger.debug("Checking existence of user: " + username);
+        logger.debugf("Checking existence of user: %s", username);
         try {
             String principal = getKerberosPrincipal(username);
             loginContext = new LoginContext("does-not-matter", null,
@@ -70,7 +71,7 @@ public class KerberosUsernamePasswordAuthenticator {
             throw new IllegalStateException("Didn't expect to end here");
         } catch (LoginException le) {
             String message = le.getMessage();
-            logger.debug("Message from kerberos: " + message);
+            logger.debugf("Message from kerberos: %s", message);
 
             checkKerberosServerAvailable(le);
 
@@ -128,6 +129,7 @@ public class KerberosUsernamePasswordAuthenticator {
         return loginContext.getSubject();
     }
 
+
     public void logoutSubject() {
         if (loginContext != null) {
             try {
@@ -139,7 +141,6 @@ public class KerberosUsernamePasswordAuthenticator {
     }
 
 
-
     protected String getKerberosPrincipal(String username) throws LoginException {
         if (username.contains("@")) {
             String[] tokens = username.split("@");
@@ -156,6 +157,7 @@ public class KerberosUsernamePasswordAuthenticator {
         return username + "@" + config.getKerberosRealm();
     }
 
+
     protected CallbackHandler createJaasCallbackHandler(final String principal, final String password) {
         return new CallbackHandler() {
 
@@ -176,17 +178,8 @@ public class KerberosUsernamePasswordAuthenticator {
         };
     }
 
-    protected Configuration createJaasConfiguration() {
-        return new Configuration() {
 
-            @Override
-            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-                Map<String, Object> options = new HashMap<String, Object>();
-                options.put("storeKey", "true");
-                options.put("debug", String.valueOf(config.getDebug()));
-                AppConfigurationEntry kerberosLMConfiguration = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);
-                return new AppConfigurationEntry[] { kerberosLMConfiguration };
-            }
-        };
+    protected Configuration createJaasConfiguration() {
+        return KerberosJdkProvider.getProvider().createJaasConfigurationForUsernamePasswordLogin(config.isDebug());
     }
 }
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
index f09ea6e..c2b928e 100644
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/impl/SPNEGOAuthenticator.java
@@ -19,15 +19,21 @@ package org.keycloak.federation.kerberos.impl;
 
 import java.io.IOException;
 import java.security.PrivilegedExceptionAction;
+import java.util.Iterator;
+import java.util.Set;
 
 import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosTicket;
 
+import org.ietf.jgss.Oid;
+import org.keycloak.common.constants.KerberosConstants;
 import org.keycloak.common.util.Base64;
 import org.ietf.jgss.GSSContext;
 import org.ietf.jgss.GSSCredential;
 import org.ietf.jgss.GSSException;
 import org.ietf.jgss.GSSManager;
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.KerberosJdkProvider;
 import org.keycloak.federation.kerberos.CommonKerberosConfig;
 import org.keycloak.common.util.KerberosSerializationUtils;
 
@@ -45,6 +51,7 @@ public class SPNEGOAuthenticator {
     private boolean authenticated = false;
     private String authenticatedKerberosPrincipal = null;
     private GSSCredential delegationCredential;
+    private KerberosTicket kerberosTicket;
     private String responseToken = null;
 
     public SPNEGOAuthenticator(CommonKerberosConfig kerberosConfig, KerberosServerSubjectAuthenticator kerberosSubjectAuthenticator, String spnegoToken) {
@@ -61,6 +68,14 @@ public class SPNEGOAuthenticator {
         try {
             Subject serverSubject = kerberosSubjectAuthenticator.authenticateServerSubject();
             authenticated = Subject.doAs(serverSubject, new AcceptSecContext());
+
+            // kerberosTicketis available in IBM JDK in case that GSSContext supports delegated credentials
+            Set<KerberosTicket> kerberosTickets = serverSubject.getPrivateCredentials(KerberosTicket.class);
+            Iterator<KerberosTicket> iterator = kerberosTickets.iterator();
+            if (iterator.hasNext()) {
+                kerberosTicket = iterator.next();
+            }
+
         } catch (Exception e) {
             log.warn("SPNEGO login failed", e);
         } finally {
@@ -89,7 +104,7 @@ public class SPNEGOAuthenticator {
             if (log.isTraceEnabled()) {
                 log.trace("Serializing credential " + delegationCredential);
             }
-            return KerberosSerializationUtils.serializeCredential(delegationCredential);
+            return KerberosSerializationUtils.serializeCredential(kerberosTicket, delegationCredential);
         } catch (KerberosSerializationUtils.KerberosSerializationException kse) {
             log.warn("Couldn't serialize credential: " + delegationCredential, kse);
             return null;
@@ -150,7 +165,10 @@ public class SPNEGOAuthenticator {
 
     protected GSSContext establishContext() throws GSSException, IOException {
         GSSManager manager = GSSManager.getInstance();
-        GSSContext gssContext = manager.createContext((GSSCredential) null);
+
+        Oid[] supportedMechs = new Oid[] { KerberosConstants.KRB5_OID, KerberosConstants.SPNEGO_OID };
+        GSSCredential gssCredential = manager.createCredential(null, GSSCredential.INDEFINITE_LIFETIME, supportedMechs, GSSCredential.ACCEPT_ONLY);
+        GSSContext gssContext = manager.createContext(gssCredential);
 
         byte[] inputToken = Base64.decode(spnegoToken);
         byte[] respToken = gssContext.acceptSecContext(inputToken, 0, inputToken.length);

pom.xml 7(+5 -2)

diff --git a/pom.xml b/pom.xml
index 8486817..a736a45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,8 +72,7 @@
         <undertow.version>1.3.15.Final</undertow.version>
         <wildfly.core.version>2.0.10.Final</wildfly.core.version>
         <wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
-        <!--<xmlsec.version>2.0.5</xmlsec.version>--> <!-- WILDFLY VERSION -->
-        <xmlsec.version>1.5.1</xmlsec.version>
+        <xmlsec.version>2.0.5</xmlsec.version>
 
         <!-- Others -->
         <apacheds.version>2.0.0-M17</apacheds.version>
@@ -377,6 +376,10 @@
                         <groupId>javax.servlet</groupId>
                         <artifactId>servlet-api</artifactId>
                     </exclusion>
+                    <exclusion>
+                        <groupId>org.codehaus.woodstox</groupId>
+                        <artifactId>woodstox-core-asl</artifactId>
+                    </exclusion>
                 </exclusions>
             </dependency>
             <dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
index 16e302e..12f0f5e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
@@ -192,6 +192,10 @@ public abstract class AbstractKerberosTest {
         loginPage.login("jduke", "theduke");
         changePasswordPage.assertCurrent();
 
+        // Bad existing password
+        changePasswordPage.changePassword("theduke-invalid", "newPass", "newPass");
+        Assert.assertTrue(driver.getPageSource().contains("Invalid existing password."));
+
         // Change password is not possible as editMode is READ_ONLY
         changePasswordPage.changePassword("theduke", "newPass", "newPass");
         Assert.assertTrue(driver.getPageSource().contains("You can't update your password as your account is read only"));
diff --git a/testsuite/integration/src/test/resources/kerberos/kerberos-ldap-connection.properties b/testsuite/integration/src/test/resources/kerberos/kerberos-ldap-connection.properties
index a0ea537..3170d6c 100644
--- a/testsuite/integration/src/test/resources/kerberos/kerberos-ldap-connection.properties
+++ b/testsuite/integration/src/test/resources/kerberos/kerberos-ldap-connection.properties
@@ -31,4 +31,4 @@ idm.test.kerberos.allow.kerberos.authentication=true
 idm.test.kerberos.realm=KEYCLOAK.ORG
 idm.test.kerberos.server.principal=HTTP/localhost@KEYCLOAK.ORG
 idm.test.kerberos.debug=false
-idm.test.kerberos.use.kerberos.for.password.authentication=false
\ No newline at end of file
+idm.test.kerberos.use.kerberos.for.password.authentication=true
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/kerberos/test-krb5.conf b/testsuite/integration/src/test/resources/kerberos/test-krb5.conf
index a775b47..6989b7f 100644
--- a/testsuite/integration/src/test/resources/kerberos/test-krb5.conf
+++ b/testsuite/integration/src/test/resources/kerberos/test-krb5.conf
@@ -1,8 +1,8 @@
 [libdefaults]
     default_realm = KEYCLOAK.ORG
-    default_tgs_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
-    default_tkt_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
-    permitted_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac
+    default_tgs_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
+    default_tkt_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
+    permitted_enctypes = des3-cbc-sha1-kd aes256-cts-hmac-sha1-96 rc4-hmac aes128-cts-hmac-sha1-96
     kdc_timeout = 30000
     dns_lookup_realm = false
     dns_lookup_kdc = false
diff --git a/util/embedded-ldap/src/main/java/org/keycloak/util/ldap/KerberosEmbeddedServer.java b/util/embedded-ldap/src/main/java/org/keycloak/util/ldap/KerberosEmbeddedServer.java
index 1f8fe07..d7f868f 100644
--- a/util/embedded-ldap/src/main/java/org/keycloak/util/ldap/KerberosEmbeddedServer.java
+++ b/util/embedded-ldap/src/main/java/org/keycloak/util/ldap/KerberosEmbeddedServer.java
@@ -19,8 +19,11 @@ package org.keycloak.util.ldap;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Properties;
 import java.util.Set;
 
@@ -43,13 +46,7 @@ import org.apache.directory.server.protocol.shared.transport.UdpTransport;
 import org.apache.directory.shared.kerberos.KerberosTime;
 import org.apache.directory.shared.kerberos.KerberosUtils;
 import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
 import org.jboss.logging.Logger;
-import org.keycloak.common.util.KerberosSerializationUtils;
-import sun.security.jgss.GSSNameImpl;
-import sun.security.jgss.krb5.Krb5NameElement;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -113,15 +110,8 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
         this.kdcEncryptionTypes = readProperty(PROPERTY_KDC_ENCTYPES, DEFAULT_KDC_ENCRYPTION_TYPES);
 
         if (ldapSaslPrincipal == null || ldapSaslPrincipal.isEmpty()) {
-            try {
-                // Same algorithm like sun.security.krb5.PrincipalName constructor
-                GSSName gssName = GSSManager.getInstance().createName("ldap@" + bindHost, GSSName.NT_HOSTBASED_SERVICE);
-                GSSNameImpl gssName1 = (GSSNameImpl) gssName;
-                Krb5NameElement krb5NameElement = (Krb5NameElement) gssName1.getElement(KerberosSerializationUtils.KRB5_OID);
-                this.ldapSaslPrincipal = krb5NameElement.getKrb5PrincipalName().toString();
-            } catch (GSSException uhe) {
-                throw new RuntimeException(uhe);
-            }
+            String hostname = getHostnameForSASLPrincipal(bindHost);
+            this.ldapSaslPrincipal = "ldap/" + hostname + "@" + this.kerberosRealm;
         }
     }
 
@@ -219,6 +209,31 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
     }
 
 
+    // Forked from sun.security.krb5.PrincipalName constructor
+    private String getHostnameForSASLPrincipal(String hostName) {
+        try {
+            // RFC4120 does not recommend canonicalizing a hostname.
+            // However, for compatibility reason, we will try
+            // canonicalize it and see if the output looks better.
+
+            String canonicalized = (InetAddress.getByName(hostName)).
+                    getCanonicalHostName();
+
+            // Looks if canonicalized is a longer format of hostName,
+            // we accept cases like
+            //     bunny -> bunny.rabbit.hole
+            if (canonicalized.toLowerCase(Locale.ENGLISH).startsWith(
+                    hostName.toLowerCase(Locale.ENGLISH)+".")) {
+                hostName = canonicalized;
+            }
+        } catch (UnknownHostException | SecurityException e) {
+            // not canonicalized or no permission to do so, use old
+        }
+        return hostName.toLowerCase(Locale.ENGLISH);
+    }
+
+
+
     /**
      * Replacement of apacheDS KdcServer class with disabled ticket replay cache.
      *