Using JDNI to change Password in Active Directory

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

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.

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.