1 Reply Latest reply: Jan 30, 2012 12:56 PM by 911935 RSS

    JNDI, Active Directory and Smart Card Authentication

    911935
      Hi,

      I am trying to use a keystore from a smart card to bind to Active Directory. I can get the keystore containing one user certificate from the card. However, when I try to bind with this keystore it fails with the following exception:
      javax.naming.AuthenticationNotSupportedException: [LDAP: error code 7 - 00002027: LdapErr: DSID-0C090499, comment: Invalid Authentication method, data 0, vece
           at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3058)
           at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3013)
           at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2815)
           at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2729)
           at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:296)
           at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175)
           at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193)
           at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136)
           at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66)
           at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
           at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
           at javax.naming.InitialContext.init(InitialContext.java:223)
           at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:134)
           at com.cerner.genesis.authentication.smartcard.KeyStoreAuth.main(KeyStoreAuth.java:35)
      Here is simplified version of the code I am using. I wrote out the certificate on to a keystore file so it would be easier to evaluate.
      import java.util.Hashtable;
      
      import javax.naming.Context;
      import javax.naming.NamingEnumeration;
      import javax.naming.NamingException;
      import javax.naming.directory.Attribute;
      import javax.naming.directory.Attributes;
      import javax.naming.ldap.InitialLdapContext;
      import javax.naming.ldap.LdapContext;
      
      public class KeyStoreAuth {
          public static void main(final String[] args) {
      
              final Hashtable<String, String> env = new Hashtable<String, String>();
      
              // access my user certificate, should be the first one.
              System.setProperty("java.net.ssl.keyStore", "myKeystore");
              System.setProperty("java.net.ssl.keyStorePassword", "myPass");
      
              env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      
              // connect to my domain controller
              env.put(Context.PROVIDER_URL, "ldaps://ldap01.ad:636/");
              env.put(Context.SECURITY_PROTOCOL, "ssl");
              env.put(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
              // env.put(Context.SECURITY_PRINCIPAL,
              // "cn=test user, ou=other, ou=Users, dc=ldap01, dc=ad");
              // env.put(Context.SECURITY_CREDENTIALS, "Pass*123");
      
              try {
      
                  // Create the initial directory context
                  final LdapContext ctx = new InitialLdapContext(env, null);
      
                  // Get all the attributes
                  final Attributes attrs = ctx
                          .getAttributes("cn=test user, ou=other, ou=Users, dc=ldap01, dc=ad");
      
                  if (attrs == null) {
                      System.out.println("No attributes");
                  } else {
                      /* Print each attribute */
                      try {
                          for (final NamingEnumeration<?> ae = attrs.getAll(); ae.hasMore();) {
                              final Attribute attr = (Attribute) ae.next();
                              System.out.println("attribute: " + attr.getID());
      
                              /* print each value */
                              for (final NamingEnumeration<?> e = attr.getAll(); e.hasMore(); System.out.println("value: "
                                      + e.next())) {
                                  ;
                              }
                          }
                      } catch (final NamingException e) {
                          e.printStackTrace();
                      }
                  }
                  ctx.close();
      
              } catch (final NamingException e) {
                  e.printStackTrace();
              }
          }
      }
      The code above binds if I were to use the actual credentials of the user. For example, if I un-comment the following two lines
              // env.put(Context.SECURITY_PRINCIPAL,
              // "cn=test user, ou=other, ou=Users, dc=ldap01, dc=ad");
              // env.put(Context.SECURITY_CREDENTIALS, "Pass*123");
      and modify the following line to do a simple bind instead:
      env.put(Context.SECURITY_AUTHENTICATION, "simple");
      It will print out the attributes of the user.

      I have attempted to use the following threads as a reference and still no luck:
      JNDI, Active Directory and Authentication (Part 4) (SASL EXTERNAL)
      Exception while trying TLS with Active Directory

      Any help is appreciated.

      Thanks.
        • 1. Re: JNDI, Active Directory and Smart Card Authentication
          911935
          I finally got certificate authentication to LDAP working with a smart card and I thought I might share with anyone else who might be interested.
          import java.security.KeyStore;
          import java.util.Hashtable;
          
          import javax.naming.Context;
          import javax.naming.NamingEnumeration;
          import javax.naming.NamingException;
          import javax.naming.directory.Attribute;
          import javax.naming.directory.Attributes;
          import javax.naming.ldap.InitialLdapContext;
          import javax.naming.ldap.LdapContext;
          import javax.naming.ldap.StartTlsRequest;
          import javax.naming.ldap.StartTlsResponse;
          
          public class KeyStoreAuth {
              public static void main(final String[] args) {
          
                  // Allows the smart card to be used as the keystore.
                  System.setProperty("javax.net.ssl.keyStore", "NONE");
                  System.setProperty("javax.net.ssl.keyStoreType", "PKCS11");
          
                  final Hashtable<String, String> env = new Hashtable<String, String>();
          
                  env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
          
                  // connect to my domain controller
                  env.put(Context.PROVIDER_URL, "ldap://ldap01.ad:389");
          
                  // Set up debug mode to see the ssl handshake.
                  // System.setProperty("javax.net.debug", "all");
          
                  try {
                      /*
                       * This will only work if the java.security file located at $JAVA_HOME/jre/lib/security/java.security points
                       * to a PKCS11 configuration file. (e.g. security.provider.10=sun.security.pkcs11.SunPKCS11 c:/pkcs11.cfg)
                       * The PKCS11 configuration file must contain the following: 
                       * name=<name of the library> 
                       * library=<path to pkcs11 library>
                       * Otherwise this must be done programmatically(There are several examples out there if you google it). 
                       */
                      final KeyStore keystore = KeyStore.getInstance("PKCS11");
                      keystore.load(null, "0000".toCharArray());
          
                      // Create the initial directory context
                      final LdapContext ctx = new InitialLdapContext(env, null);
          
                      // Start TLS
                      final StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
                      tls.negotiate();
          
                      // Perform client authentication using TLS credential
                      ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
          
                      // Get all the attributes
                      final Attributes attrs = ctx.getAttributes("cn=test user, ou=other, ou=Users, dc=ldap01, dc=ad");
          
                      if (attrs == null) {
                          System.out.println("No attributes");
                      } else {
                          // Print each attribute
                          try {
                              for (final NamingEnumeration<?> ae = attrs.getAll(); ae.hasMore();) {
                                  final Attribute attr = (Attribute) ae.next();
                                  System.out.println("attribute: " + attr.getID());
          
                                  // print each value
                                  for (final NamingEnumeration<?> e = attr.getAll(); e.hasMore(); System.out.println("value: "
                                          + e.next())) {
                                      ;
                                  }
                              }
                          } catch (final NamingException e) {
                              e.printStackTrace();
                          }
                      }
                      ctx.close();
          
                  } catch (final Exception e) {
                      e.printStackTrace();
                  }
              }
          }