The Mysterious CLIENT-CERT
Note: This post is about one of the web application authentication methods defined by the Java Servlet specification. You can also read a larger article I wrote describing how all of the authentication methods work. It includes the content below.
The CLIENT-CERT authentication method is typically used for identifying the user by his X.509 certificate. This is a special usage of the Secure Sockets Layer (SSL) where both the server and the user are identified by their own certificates. As a result, this is a very strong form of authentication. But there’s more to CLIENT-CERT than you might realize.
Encounters with SSL on the Internet are almost always one-way authentication where only the server presents a certificate to the client. Think of ordering on Amazon.com, for example. The protocol switches from http to https and the little lock symbol appears on your browser. This signifies that the link is encrypted and the certificate presented by Amazon was trusted since your browser trusts certificates issued by Verisign. (Note: Public Key Infrastructure (PKI) and trust are beyond the scope of this post.) Even so, the server still does not know the identity of the user. User authentication is left to another mechanism such as username/password.
On the other hand, two-way SSL retains the server authentication part but adds the requirement that the client present a certificate that the server trusts. Assuming that both sides agree, the encrypted link is established and both sides are mutually authenticated via SSL. This technique is rare on the Internet since most people don’t have personal certificates. However, there is traction for two-way SSL on corporate intranets and in B2B scenarios. Once a mutually authenticated link is established, there is no further need for user authentication since the certificate already positively identified the user.
Whew, that was a lot of backstory on two-way SSL. Hopefully, though, the purpose of the CLIENT-CERT auth-method is now clear. Using CLIENT-CERT typically means that your application wants to authenticate the user via two-way SSL.
But wait, there’s more…
Configuring CLIENT-CERT
Specifying CLIENT-CERT in web.xml is not sufficient for actually having the whole process work. I won’t go into details, but here are the additional configuration items you need to handle:
- Ensure that your server has a server certificate
- Ensure that your users trust your server certificate’s issuer (the CA certificate)
- Ensure that your server is listening on the SSL port
- Ensure that your users have certificates
- Ensure that your server trusts the user certificate issuer(s)
- (WebLogic) Ensure that an Identity Asserter handles the X.509 certificate and can map the certificate to a known user
The list above describes in general terms what must be configured for mutual authentication to work. In WebLogic, it’s an identity assertion security provider that handles X.509 certificates. The identity asserter isn’t triggered unless CLIENT-CERT is present in web.xml.
Once all of this is properly configured, the stars line up just so, and you dispose of the hair you pulled out trying to get it right, you’re ready to enjoy authentication via two-way SSL.
But wait, there’s more…
Perimeter Authentication Tokens
According to the servlet specification, the intention of the CLIENT-CERT authentication method was for two-way SSL as described above. However, BEA WebLogic not only supports the spec but expands upon it to support any type of perimeter authentication token.
With WebLogic, X.509 certificates, SAML tokens, Kerberos tickets, etc., are all examples of perimeter authentication tokens. Of the examples presented, the WebLogic 8.1 security framework only supports X.509 out of the box, but you can write or buy custom identity asserters that handle any type of token that can be presented as string. That’s pretty nifty.
And it all starts with the mysterious CLIENT-CERT authentication method.
Excellent article! Question, though, regarding identity assertion: Do X.509 certificates necessarily have to map to WL users? Example: My custom role mapper assigns roles to users based on their DN. My intention is to control users’ access to web services based on those roles. I’d like to issue lots of client certs without having to create a corresponding WL user (especially since I don’t know what it buys me!). Also, it seems like the job of managing WL users would grow with the number of client certs.
Anyway, thanks for demystifying WL’s security framework.
I can’t tell you how much your blog has already helped my understanding.
-kevin
Comment by Kevin Moran — October 20, 2006 @ 10:05 pm
Kevin,
No, there doesn’t have to be a WL user stored somewhere. This article is talking about the common case where there is a user account stored somewhere and the cert has to be mapped to it.
To not have a “backing user” so to speak, you’ll probably need a custom authenticator with a custom login module. This login module will do nothing more than add a WLSUser object to the Subject. This value can be anything you want but I’d imagine you’d take some piece of the DN and stick it there. This value represents the user from WLS’ perspective, so if you do an HttpServletRequest.getRemoteUser() you’ll get that value back (as a simple String, of course).
Check the sample security providers for an example of a custom authenticator.
Hope this helps,
Mike
Comment by Mike Fleming — October 22, 2006 @ 8:09 pm
Say you have an LDAP X509 Identity Asserter and an LDAP Authenticator. How do the two interact to produce an authenticated user + groups? Confusingly, the order they are listed in the realm’s Authentication node doesn’t seem to matter. How does WebLogic know to hit the X509 Identity Asserter first?
Comment by JW — July 12, 2007 @ 1:37 am
JW,
Identity asserters and authenticators work together to identify the user. Identity asserters check for the presence of tokens in the request such as an X.509 cert. When it finds one, it tries to map that token to a user known to the realm. In the case of a cert, the asserter might use the whole DN or just the CN as the user name. It does this via a username mapper class associated with the asserter.
Once the token has been mapped to a username, that username is passed to the authenticator(s) to see if the user exists. If an authenticator can find that username, the user has authenticated successfully (depending upon the control flags in the case of multiple authenticators). The authenticator can then do its other job of pulling the user’s groups.
As for the order, asserters always have to come first so that they can extract a username to pass along to the authenticators. An authenticator wouldn’t know what to do with the token.
However, as you know, the order of authentication providers is significant.
HTH,
Mike
Comment by Mike Fleming — July 12, 2007 @ 9:00 pm
So it sounds like each one would have to hit the LDAP server separately. And since you configure the connection info separately for each one, they probably aren’t sharing an LDAP Connection pool either.
(In case you’re wondering, I asked originally because I’m trying to speed up certificate-based login.)
Comment by JW — July 16, 2007 @ 1:57 pm
JW,
My bad. I missed the “LDAP” distinction in your original question. What I described above is for the default identity asserter set to handle X.509 certs. There is no comparison of the provided cert to one in LDAP like the LDAP X.509 identity asserter does.
Whether you want to compare certs is up to you. The default identity asserter wouldn’t cause two hits to LDAP, though.
Comment by Mike Fleming — July 16, 2007 @ 7:51 pm
JW,
Something I forgot in my last comment. If you don’t compare certs, ensure that your trusted CAs are limited to just the one(s) that you accept. The default Java trust store contains a ton of CAs…
Comment by Mike Fleming — July 16, 2007 @ 8:25 pm