Java JNDI, Active Directory, and LDAP_CONTROL_PWEXPIRING

There are many variations on Interop. Many of the posts on this blog discuss application-to-application connections, where a .NET app and a Java app can exchange messages or share data, but there are other options, such as application-to-infrastructure connections.

This post provides an example: a Java-based console application that uses JNDI to query ActiveDirectory. It finds the maximum password age for a domain, and the last time the password was set for a user, and uses that to calculate and display the time remaining on the current password.

Catalyst

A customer was having trouble running their JNDI app against AD. After some investigation, it seems the app was relying on an extension to LDAP that is specific to Netscape Directory Server and its descendants, in particular the LDAP_CONTROL_PWEXPIRING control, OID=2.16.840.1.113730.3.4.5, where the LDAP server tells the LDAP client that a password is "near expiring".

Turns out that AD doesn't provide that Netscape extension. Go figure. iPlanet supports it. And SunOne. But they are all just descendants of Netscape Directory Server.  I don't know if the LDAP servers from Novell or IBM (SecureWay) support this Netscape-y OID.

What can the poor developer do? The code included here shows how an LDAP client using ActiveDirectory can get similar behavior, can find out if a user's password is expiring "soon".

Using JNDI against ActiveDirectory is nothing new. What is novel here is the combination of :

  • querying the domain for maxPwdAge
  • querying the user for pwdLastSet
  • doing the date arithmetic to come up with time remaining

SSL

There was just one twist - getting SSL to work between Java and AD. This isn't strictly required, because you can authenticate from Java to AD without SSL. But if you find that the password is nearly expired, and you want to then update the password that is just-about-expired, then you'll need an SSL connection. In theory, getting an SSL connection between Java and AD should be simple - just make sure the set of certs required is in the cacerts file.

I had a set of x509 certs listed in the Windows Certicate manager (rundll32.exe c:\windows\system32\inetcpl.cpl,LaunchSiteCertDialog). Some of these certs needed to be in the Java trustStore (cacerts) in order to get SSL to work. To do this for a single cert, you need to :

  • export the cert from the cert manager, into a CER file (DER encoding is fine)
  • add it into the trustStore with Java's keytool
    keytool -import -alias Whatever -storepass changeme -keystore myCaCerts.jks -file MyExportedCert.cer

But I needed the entire chain of certs. I did not find a way to mechanically determine exactly the set of certs needed to complete the chain. Basically what I did was follow the above two (manual) steps to export and import all the corporate certs. What a pain. If anyone has a tool that does either of these things - analyzing which certs you need in the chain, and automating the export and import , please drop me a line.

Get the source

As always, I hope this is helpful for you. Happy inter-connecting!

-Dino