I’ve been working on porting WAFFLE to Java/ JNA. As usual I have modest and practical goals with that project: to provide a working example for everything InitializeSecurityContext and AcceptSecurityContext and to replace the .NET -> COM -> Java bridge for our application. So lets get started.
You’ve got a client and a server. For example, a browser and a web server. Both are joined to the same Active Directory domain. When you navigate to the website on your web server you don’t get prompted for credentials. How can this happen?
On Windows, this works because of the Security Support Provider Interface, aka SSPI. SSPI is a well-defined API for obtaining integrated security services for, among other things, authentication for any distributed application protocol. A client-server conversation is an example of such an application. SSPI is a Microsoft proprietary implementation of GSSAPI, an IETF standard. Who cares about standards, we just want it to work, right?
When a client wants to authenticate to a server, it needs to supply credentials and send them to the server. The server needs to validate this, reply that the credentials were kosher and possibly continue executing code on behalf of the client.
Credentials can come in a variety of forms, such as a username and password or a notarized birth certificate from City Hall. Sending those to the server needs to be secure: you don’t want to send credentials to the wrong server, the server wants to make sure you’re really who you claim to be and nobody should be able to intercept this data on the wire and reuse it. The how part of this is the job of the authentication protocol, such as, for example, NTLM or Kerberos.
Because there’re many protocols, SSPI exchanges so called tokens, opaque blobs of data. the protocol can put anything in the blobs.
Protocols often require several exchanges. For example, I may need to obtain the server’s public key, encrypt credentials, send them with my public key and receive an encrypted confirmation of success. Therefore both client and server maintain a so called security context during this conversation.
SSPI allows you to do all this with any protocol or SSPI provider. There’s an NTLM SSPI provider, Kerberos SSPI provider, etc. SSPI describes three important calls that doo all of the above.
We’re going to do this in JNA. First, some structure definitions.
A security handle is a pointer that holds credentials, context, etc. The SecHandle, CtxtHandle and CredHandle are all the same thing.
A security buffer holds a single token. It has a size and a type. For example, a security token will be of MAX_TOKEN_SIZE size and SECBUFFER_TOKEN type.
Notice the infamous ByReference inner class, a handy way in JNA to have a ByReference and a non-By-Reference class that do the same thing.
An array of buffers is called a SecBufferDesc.
Notice the way an array of inner buffers is declared ByReference and created with a toArray call. This tells JNA about a nested structure over a contiguous block of memory. It will be read and written automagically in and out of function calls.
The API also needs a timestamp.
The above-mentioned functions are implemented in Secur32.dll.
We can do both client and server in the same code for the currently logged on user. In the real world you would only need the client or the server part and you would have to translate the pbClientToken and pbServerToken objects into bytes and send/receive them.
com.sun.jna.platform.win32.Secur32 and com.sun.jna.platform.win32.Sspi were committed to JNA. The example code is similar to com.sun.jna.platform.win32.Secur32Test.testAcceptSecurityContext.