Bug 34643 - document how to use certificate-based "clientAuth" on a per user or per session basis also with self-signed/expired client certs
Summary: document how to use certificate-based "clientAuth" on a per user or per sessi...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 5
Classification: Unclassified
Component: Connector:Coyote (show other bugs)
Version: Nightly Build
Hardware: Other All
: P2 enhancement (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-04-27 13:12 UTC by Ralf Hauser
Modified: 2009-11-07 08:09 UTC (History)
1 user (show)



Attachments
patched SSLAuthenticator.java (8.47 KB, application/octet-stream)
2005-05-11 15:29 UTC, Ralf Hauser
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ralf Hauser 2005-04-27 13:12:34 UTC
http://jakarta.apache.org/tomcat/tomcat-5.5-doc/ssl-howto.html#Edit%20the%20Tomcat%20Configuration%20File
nicely documents how to enable "clientAuth" on a global basis.
A web application may well have a mixed user community, some authenticate by
means of a password or other authenticators, others have a certificate for
authentication.

The goal of this RFE is to amend the documentation with how the clientAuth
mechanism can be triggered and enforced on a per user basis.

Interim results of my own little research:
- if I request org.apache.catalina.Globals.SSL_CERTIFICATE_ATTR,
org.apache.coyote.tomcat4.CoyoteRequest.getAttribute triggers the
org.apache.coyote.ActionCode.ACTION_REQ_SSL_CERTIFICATE re-handshake
- to enforce this for a given user, I guess I could store that certificate in
the session and for every subsequent request test whether the cert is stored or
otherwise trigger the re-handshake by asking for SSL_CERTIFICATE_ATTR.

Open issues I haven't mastered so far:
1) If the application allows for self-signed certificates the user uploads into
the DB i.e. her profile, is there a way to use a non-global trustStore to
validate? Otherwise, with an increasing user-basis, I foresee scalability
problems if I had to import all such certificates into a global trust store?
2) javax.net.ssl.SSLServerSocket.setNeedClientAuth in
org.apache.tomcat.util.net.jsse.JSSE14SocketFactory.configureClientAuth might be
the basis for an alternative approach, but I wouldn't know how to set that (or
probably rather
org.apache.tomcat.util.net.ServerSocketFactory.setAttribute("clientAuth", true)
before the org.apache.tomcat.util.net.jsse.JSSESocketFactory.acceptSocket has
already occurred?

related mailing list threads probably are:
http://marc.theaimsgroup.com/?l=tomcat-user&m=105300671215744&w=2 and
http://marc.theaimsgroup.com/?l=tomcat-user&m=104581231518394&w=2
Comment 1 william.barker 2005-04-27 21:37:54 UTC
(In reply to comment #0)
> Interim results of my own little research:
> - if I request org.apache.catalina.Globals.SSL_CERTIFICATE_ATTR,
> org.apache.coyote.tomcat4.CoyoteRequest.getAttribute triggers the
> org.apache.coyote.ActionCode.ACTION_REQ_SSL_CERTIFICATE re-handshake

This works in 4.1 & 5.0, but has been removed from 5.5.  You would need your 
own custom Valve to do this in 5.5.

> Open issues I haven't mastered so far:
> 1) If the application allows for self-signed certificates the user uploads 
into
> the DB i.e. her profile, is there a way to use a non-global trustStore to
> validate? Otherwise, with an increasing user-basis, I foresee scalability
> problems if I had to import all such certificates into a global trust store?

You probably want an LDAP-based trustStore (e.g. 
java.security.cert.LDAPCertStoreParameters).  Not hard to implement (at least 
for JDK 1.5), but so far there hasn't been much demand for it.

> 2) javax.net.ssl.SSLServerSocket.setNeedClientAuth in
> org.apache.tomcat.util.net.jsse.JSSE14SocketFactory.configureClientAuth 
might be
> the basis for an alternative approach, but I wouldn't know how to set that 
(or
> probably rather
> org.apache.tomcat.util.net.ServerSocketFactory.setAttribute("clientAuth", 
true)
> before the org.apache.tomcat.util.net.jsse.JSSESocketFactory.acceptSocket has
> already occurred?

This is where the clientAuth attribute on the <Connector> eventually ends 
up :).

Comment 2 Mario Ivankovits 2005-04-27 22:07:56 UTC
> You probably want an LDAP-based trustStore (e.g. 
> java.security.cert.LDAPCertStoreParameters).  Not hard to implement (at least 
> for JDK 1.5), but so far there hasn't been much demand for it.

I have already done this
http://issues.apache.org/bugzilla/show_bug.cgi?id=7831
but I dont know how well this version works.
Though I still use it and can reattach my latest version if wanted.

I dont understand why there is so little demand for it, but maybe the time for
it might come.
Comment 3 Ralf Hauser 2005-05-09 23:49:02 UTC
it appears that if the client certificate shall not be asked from all users and
not upon the first contact with the given connector, the "clientAuth" attribute
isn't it, irrespective of whether set to "true" or "want".

The approach appears to be rather to direct the user to a protected jsp/struts
action at the point in time you want the client cert auth to happen with 
<security-constraint>
        <web-resource-collection>
            <web-resource-name>Client Certificate Auth</web-resource-name>
            <url-pattern>/certBasedAuth.do</url-pattern>
        </web-resource-collection>
        <auth-constraint />
    </security-constraint>
    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
    </login-config>

Being flexible with respect to the used certificate remains tricky:
1) if already knowing the certificate to be expected e.g. by virtue of it being
in a LDAP truststore, it appears to be impossible to allow for mismatches of the
certs[0].getSubjectDN().getName() without replacing the current
org.apache.catalina.authenticator.SSLAuthenticator
- one might for example rather use the SHA1 fingerprint to match?
2) similarly, if you want to be more tolerant on certificate
expiration/not-yet-valid, I haven't figured how to get the "validate=false" for
org.apache.catalina.realm.RealmBase.authenticate(X509Certificate[] certs) from a
config file without altering sources.
3) As for self-signed certificates, I see two approaches 
a) be able to create a mini-trust-store on the fly before the ssl-handshake
occurs in org.apache.tomcat.util.net.jsse.JSSESupport.handShake()  - I have no
clue how late in an SSLSocket's life its truststore can be altered? A nice
example how this can be done a per thread basis (albeit before creating the
socket and not when it's already in operation) is Oleg's
org.apache.commons.httpclient.contrib.ssl.AuthSSLProtocolSocketFactory (see Bug
34391) - I guess this implies that the web-app programmer in some way (by means
of an enhanced LoginConfig or an extra parameter of the method or a session
attribute?) might tell
org.apache.catalina.authenticator.SSLAuthenticator.authenticate() the
self-signed cert it should expect when that method starts
b) alternatively, a less rigid TrustManager might be used in
org.apache.tomcat.util.net.jsse.JSSE15SocketFactory.getTrustManagers(String 
 keystoreType, String algorithm) - some ideas are in
http://forum.java.sun.com/thread.jspa?forumID=2&threadID=411937
Comment 4 Ralf Hauser 2005-05-11 15:29:15 UTC
Created attachment 14994 [details]
patched SSLAuthenticator.java

Thanks to Bill for the help to get it kind of working without the need to
configure a realm - how to do this:
A) compile the attached and place it in
tomcat/server/classes/org/apache/catalina/authenticator/ such that the
class-loader finds it before the same one in the catalina.jar
B) the web.xml's <auth-constraint> better has a nested
     <role-name>*</role-name>
otherwise despite a successful hand-shake, the access control will fail the
process
C) Till now, my browsers (MSIE6 and firefox1.0.3) after one successful login
can always login again despite configuring my CSP to prompt for password upon
each access to the private key. I attempted to force a re-handshake every time
and tried to avoid a server-side caching of the principal/certs, but I didn't
succeed so far. Closing the browser after logoff avoids the problem.
No clue - perhaps the server is clean and it is the browsers that cache the
certs as long as the socket lives due to the HTTP-keep-alive. If it were the
servers fault, I guess it would be adequate that a session.invalidate() would
also wipe the certs from a previous cert-based authentication.
D) If you have multiple private keys and corresponding certificates in your
brower's certificate store, the browsers do not offer any self-signed certs to
use for client-cert-auth. Haven't tried the remedies brain-stormed in comment 3
item 3, but my suspicion is that this is rather due to browser side
implementation.
Comment 5 Ralf Hauser 2005-05-11 18:28:49 UTC
as per comment 4 item D), got (pseudo-)self signed certs working (it is not the
browser's fault), i.e. I first created a self-signed (root9cert), signed the
user-cert with it and imported the root-cert into the
$JAVA_HOME/jre/lib/security/cacerts for tomcat. Since this doesn't scale as
mentioned in  and in the described in item 3 GUI/application control flow, the
session may well exist before the CLIENT-CERT is executed:
i) register trust-store with user-self-signed cert in session for the user to be
authenticated as per Bug 34868
ii) response.sendRedirect(/certBasedAuth.do);
    now that trust-store would be effective upon the subsequent request doing
the auth
Comment 6 jack 2005-12-31 17:07:17 UTC
Ralf said that <role-name>*</role-name> should be add into the <auth-
constraint> element. The inserted element authorizes all the right to access 
the resource to  the roles defined in this web application.

He is right, but I think that he is not absolutely right. It is obvious that we 
should not be forced to authorize the right to all the roles.

I put some information here, I hope that I can help some people to understand 
the stuff. Let's look at the bigger picture:
when the requested resource is constrained with different roles, we need a map 
between the user & the role to access the resource. Even when the resource is 
constrained with transfering, since the specification starts there is no access 
if no role is defined. In such a case, as Ralf said, we use "*" to indicate 
there is no role constraint.

We know that only the right persons are allowed to access the constrained 
resource. In a web application, we use two maps to define the right persons:
One map (1) is in application descriptor, in the security constraint. the map 
is between the resource & the roles to access it. Another map (2) is in tomcat-
users.xml, between persons identified by usernames & the roles.

When we use Basic, Digest, & Form, the user inputs his/her username & password 
for a specific resource. If the input password is same as the one stored with 
us, which is stored in tomcat-users.xml, too. we regard the user who is 
requiring the resource is the person defined in tomcat-users.xml. Furthermore, 
we can know his/her roles according to map 2, and we can know whether the user 
has the right to access the resource. In these cases, actually there is one 
more map (3) which is between the username & its password.

For whatever reasons, When we use "client-authentication", the relationship is 
not so clear. The most possible reason is the complication of this 
authentication. However, authentication is just a method to verify the user is 
the person he/she claimed to be. In those above cases, as long as the input 
password is same as the one stored with us, we regard the user as the person 
defined in tomcat-users.xml. Whether the person has the right to access the 
required resource, we will have to check the first two maps mentioned (map 1 & 
map 2).

In the case of "client-auth", as long as the certificate is verified, we 
believe that the user is the person. Now we can see, this authentication 
eliminate the neccessity for map 3. However, we still need map 2, the map 
between persons & roles. At present, map 2 is undefined.

In other methods, we use a username to identify a person. When using a 
certificate, it is not a simple thing to indentify a person (in certificate, 
they use the term "subject", in Java & tomcat, we use the term "principal"),
since the certificate system has to cover all human beings.
And it seems that the certificate system also introduce a map (4) between 
persons/subjects & certificates. So here, we need map 2 (between persons & 
roles) & map 4 (between persons & certificates). 

However, since certificates include usage information. So, it is reasonable to 
map certificates to roles. If we go with this direction, we might lose some 
flexibility, but more security control. Let's name this map as map 5. For 
tomcat, I believe that finally, both of them will be supported.

When the system has only a few users, then there is no problem no matter where 
we put all the certificates. When the system has many users, then we have to 
plan where to put these certificates, and how to verify them in order to have a 
better performance.
We have at least the following 5 options:
1. put in the keystore where we put our server authentication certificate.
2. put into a serie of xml file.
3. put into a local Database on the same machine or different machine.
4. put into LDAP on the same machine or different machine.
5. leave them on the internet
6. combination of these, for example ca certificate on local, others on the 
internet.

The team has developed many realms for us, and here are some extra information.

http://issues.apache.org/bugzilla/attachment.cgi?id=6666
implemented a class com.ops.webcontrol.tomcat.JNDIRealmCertAD

http://issues.apache.org/bugzilla/show_bug.cgi?id=7831
JDBCRealm

http://issues.apache.org/bugzilla/show_bug.cgi?id=34643

http://issues.apache.org/bugzilla/attachment.cgi?id=1499
org.apache.catalina.realm.JNDIRealm

http://issues.apache.org/bugzilla/attachment.cgi?id=14994

About how complicated it is to identify a subject, please refer to 
http://www.ietf.org/rfc/rfc2459.txt for more information.
For example, in a X509 certificate, (I am not sure whether there is any other 
kind of certificate system), The "subject" field is unique for that the CA 
issuing the certificate, but it might be empty. The "subjectUniqueID" field 
might be also only unique for this specific CA.

For his item C in comment #4, I guess the "clear SSL states" button which is 
besides the "certificates" button might help.
(In reply to comment #0)
> http://jakarta.apache.org/tomcat/tomcat-5.5-doc/ssl-howto.html#Edit%20the%
20Tomcat%20Configuration%20File
> nicely documents how to enable "clientAuth" on a global basis.
> A web application may well have a mixed user community, some authenticate by
> means of a password or other authenticators, others have a certificate for
> authentication.
> The goal of this RFE is to amend the documentation with how the clientAuth
> mechanism can be triggered and enforced on a per user basis.
> Interim results of my own little research:
> - if I request org.apache.catalina.Globals.SSL_CERTIFICATE_ATTR,
> org.apache.coyote.tomcat4.CoyoteRequest.getAttribute triggers the
> org.apache.coyote.ActionCode.ACTION_REQ_SSL_CERTIFICATE re-handshake
> - to enforce this for a given user, I guess I could store that certificate in
> the session and for every subsequent request test whether the cert is stored 
or
> otherwise trigger the re-handshake by asking for SSL_CERTIFICATE_ATTR.
> Open issues I haven't mastered so far:
> 1) If the application allows for self-signed certificates the user uploads 
into
> the DB i.e. her profile, is there a way to use a non-global trustStore to
> validate? Otherwise, with an increasing user-basis, I foresee scalability
> problems if I had to import all such certificates into a global trust store?
> 2) javax.net.ssl.SSLServerSocket.setNeedClientAuth in
> org.apache.tomcat.util.net.jsse.JSSE14SocketFactory.configureClientAuth might 
be
> the basis for an alternative approach, but I wouldn't know how to set that (or
> probably rather
> org.apache.tomcat.util.net.ServerSocketFactory.setAttribute("clientAuth", 
true)
> before the org.apache.tomcat.util.net.jsse.JSSESocketFactory.acceptSocket has
> already occurred?
> related mailing list threads probably are:
> http://marc.theaimsgroup.com/?l=tomcat-user&m=105300671215744&w=2 and
> http://marc.theaimsgroup.com/?l=tomcat-user&m=104581231518394&w=2

Comment 7 jack 2005-12-31 17:08:55 UTC
Ralf said that <role-name>*</role-name> should be add into the <auth-
constraint> element. The inserted element authorizes all the right to access 
the resource to  the roles defined in this web application.

He is right, but I think that he is not absolutely right. It is obvious that we 
should not be forced to authorize the right to all the roles.

I put some information here, I hope that I can help some people to understand 
the stuff. Let's look at the bigger picture:
when the requested resource is constrained with different roles, we need a map 
between the user & the role to access the resource. Even when the resource is 
constrained with transfering, since the specification starts there is no access 
if no role is defined. In such a case, as Ralf said, we use "*" to indicate 
there is no role constraint.

We know that only the right persons are allowed to access the constrained 
resource. In a web application, we use two maps to define the right persons:
One map (1) is in application descriptor, in the security constraint. the map 
is between the resource & the roles to access it. Another map (2) is in tomcat-
users.xml, between persons identified by usernames & the roles.

When we use Basic, Digest, & Form, the user inputs his/her username & password 
for a specific resource. If the input password is same as the one stored with 
us, which is stored in tomcat-users.xml, too. we regard the user who is 
requiring the resource is the person defined in tomcat-users.xml. Furthermore, 
we can know his/her roles according to map 2, and we can know whether the user 
has the right to access the resource. In these cases, actually there is one 
more map (3) which is between the username & its password.

For whatever reasons, When we use "client-authentication", the relationship is 
not so clear. The most possible reason is the complication of this 
authentication. However, authentication is just a method to verify the user is 
the person he/she claimed to be. In those above cases, as long as the input 
password is same as the one stored with us, we regard the user as the person 
defined in tomcat-users.xml. Whether the person has the right to access the 
required resource, we will have to check the first two maps mentioned (map 1 & 
map 2).

In the case of "client-auth", as long as the certificate is verified, we 
believe that the user is the person. Now we can see, this authentication 
eliminate the neccessity for map 3. However, we still need map 2, the map 
between persons & roles. At present, map 2 is undefined.

In other methods, we use a username to identify a person. When using a 
certificate, it is not a simple thing to indentify a person (in certificate, 
they use the term "subject", in Java & tomcat, we use the term "principal"),
since the certificate system has to cover all human beings.
And it seems that the certificate system also introduce a map (4) between 
persons/subjects & certificates. So here, we need map 2 (between persons & 
roles) & map 4 (between persons & certificates). 

However, since certificates include usage information. So, it is reasonable to 
map certificates to roles. If we go with this direction, we might lose some 
flexibility, but more security control. Let's name this map as map 5. For 
tomcat, I believe that finally, both of them will be supported.

When the system has only a few users, then there is no problem no matter where 
we put all the certificates. When the system has many users, then we have to 
plan where to put these certificates, and how to verify them in order to have a 
better performance.
We have at least the following 5 options:
1. put in the keystore where we put our server authentication certificate.
2. put into a serie of xml file.
3. put into a local Database on the same machine or different machine.
4. put into LDAP on the same machine or different machine.
5. leave them on the internet
6. combination of these, for example ca certificate on local, others on the 
internet.

The team has developed many realms for us, and here are some extra information.

http://issues.apache.org/bugzilla/attachment.cgi?id=6666
implemented a class com.ops.webcontrol.tomcat.JNDIRealmCertAD

http://issues.apache.org/bugzilla/show_bug.cgi?id=7831
JDBCRealm

http://issues.apache.org/bugzilla/show_bug.cgi?id=34643

http://issues.apache.org/bugzilla/attachment.cgi?id=1499
org.apache.catalina.realm.JNDIRealm

http://issues.apache.org/bugzilla/attachment.cgi?id=14994

About how complicated it is to identify a subject, please refer to 
http://www.ietf.org/rfc/rfc2459.txt for more information.
For example, in a X509 certificate, (I am not sure whether there is any other 
kind of certificate system), The "subject" field is unique for that the CA 
issuing the certificate, but it might be empty. The "subjectUniqueID" field 
might be also only unique for this specific CA.

For his item C in comment #4, I guess the "clear SSL states" button which is 
besides the "certificates" button might help.
Comment 8 Ralf Hauser 2006-08-08 15:25:19 UTC
Jack, 

thanks for your explanations.

"Clear SSL State" appears to be a MSIE feature - is there a FireFox equivalent ?
How about opera, konqueror, etc.

Ralf

P.S.: Just FYI, option B of comment 4 no longer works (5.5.17) and apparently is
against the specs (bug 39364)
Comment 9 Yoav Shapira 2006-12-24 08:31:17 UTC
Ralf and jack: thank you for these great comments and examples.  I've updated
the SSL HowTo, and connector configuration reference on clientAuth, and the
header comment on SSLAuthenticator.java to link here so that others can benefit
from these insights.  Ralf, since you're a committer now, if you feel like
further documentation changes are required, you can reopen this issue and assign
it to yourself for further work.
Comment 10 Ralf Hauser 2007-03-19 01:30:57 UTC
see also bug 41883 
Comment 11 Ralf Hauser 2009-11-07 08:09:50 UTC
see also Bug 48157 and Bug 48158