Using JDNI to change Password in Active Directory

From Null-pointer

Jump to: navigation, search

To be able to change a password in active directory, it must be running using SSL.

Contents

Setting up Ldap

Env settting for ldap.

env.put( Context.INITIAL_CONTEXT_FACTORY,
   "com.sun.jndi.ldap.LdapCtxFactory" );
env.put( Context.REFERRAL, "follow" );
env.put( Context.SECURITY_AUTHENTICATION, "SIMPLE" );
env.put( Context.SECURITY_PRINCIPAL, ADMIN_SECURITY_PRINCIPAL );
env.put( Context.SECURITY_CREDENTIALS, SECURITY_CREDENTIALS );

Setting up SSL

636 is the default ldaps port. If you specify ldaps, you do not have to state "ssl" in the security protocol.

public static final String SSL_LDAP_SERVICE         = "ldap://localhost:636";
env.put( Context.PROVIDER_URL, SSL_LDAP_SERVICE );
env.put( Context.SECURITY_PROTOCOL, "ssl" );

Keystore

You need to point the code to the keystore. The keystore needs to include the certificate used by the Active directory.

keytool

Use keytool to import certificates into the store.

[pkane@LONDON|c:\\_projects\\active directory\\certs]keytool 
  -import -alias london -file certnew.cer -keystore keystore.ks

Java

String keystore = "C:/_projects/active directory/certs/keystore.ks";
System.setProperty( "javax.net.ssl.trustStore", keystore );
System.setProperty( "javax.net.ssl.trustStorePassword", "changeit" );
// System.setProperty( "javax.net.debug", "all" );

Changing the Password

I found logging in to ldap as an administrator, and then changing to required user the most reliable way of changing password.

 public static final void changePassword(DirContext ctx, String argRDN, String argNewPassword)
   throws NamingException
 {

   ModificationItem[] modificationItem = new ModificationItem[1];
   try
   {
     modificationItem[0] = new ModificationItem( DirContext.REPLACE_ATTRIBUTE,
       new BasicAttribute( "unicodePwd", encodePassword( argNewPassword ) ) );
     ctx.modifyAttributes( argRDN, modificationItem );
   }
   catch (UnsupportedEncodingException e1)
   {
     throw new RuntimeException( e1.toString() );
   }
   catch (NamingException e1)
   {
     throw e1;
   }
 }

AD Password Format

For changing attribute unicodePwd. This is a modify only attribute.

private static byte[] encodePassword(String pass)
  throws UnsupportedEncodingException
{
  final String ATT_ENCODING = "UTF-16LE";
  String pwd = "\\"" + pass + "\\"";
  byte bytes[] = pwd.getBytes( ATT_ENCODING );

  return bytes;
}

Calling the methods

The glue.

DirContext sslCtx = new InitialDirContext( env );
changePassword( sslCtx, userToChange,  passwordChangeTo );

Username Formats

All are valid username formats.

ADMIN_SECURITY_PRINCIPAL = "CN=Paul Kane,OU=People,DC=domain,DC=example,DC=com";
ADMIN_SECURITY_PRINCIPAL = "DOMAIN\\\\pkane";
ADMIN_SECURITY_PRINCIPAL = "pkane@domain.example.com";

Reading the pwdLastSet field

This field contain the value of the last time the password was set. It is in the number of seconds that have passed since Jan 1, 1601.

  • Example: pwdLastSet: 128232747798495876

To find out this time in java use the following method:

 private static Calendar getTime(long pwdLastSet)
 {
   long javaTime = pwdLastSet - 0x19db1ded53e8000L;
   javaTime /= 10000;

   Calendar cal = Calendar.getInstance();
   cal.setTimeInMillis(javaTime);
   return cal;
 }

I have no idea how this works, but from all the examples, this one was the shortest, and it also worked.

Taken from this java sun forum.

Error Codes

49

LDAP: error code 49 - 80090308: LdapErr: DSID-0C090334, 
comment: AcceptSecurityContext error, data 52e
  • if data = 52e it means the password was wrong
  • if data = 525 it means the username is wrong

53

LDAP: error code 53 - 00002077: SvcErr: DSID-03190DC9, 
problem 5003 (WILL_NOT_PERFORM), data 0
  • The modification that was attempt was not allowed. Normally mean you have not complied to some condition AD expects.

See Also

Personal tools