Bug 37869 - Cannot obtain client certificate with SSL / client certificate authentication using APR components
Cannot obtain client certificate with SSL / client certificate authentication...
Status: RESOLVED FIXED
Product: Tomcat 5
Classification: Unclassified
Component: Connector:HTTP
5.5.12
PC Windows XP
: P2 normal with 3 votes (vote)
: ---
Assigned To: Tomcat Developers Mailing List
:
: 41382 (view as bug list)
Depends on:
Blocks:
  Show dependency tree
 
Reported: 2005-12-12 11:49 UTC by sergio
Modified: 2009-07-09 01:23 UTC (History)
2 users (show)



Attachments
Patch to look for client's certificate in Http11AprProcessor (1.12 KB, patch)
2006-11-29 04:11 UTC, Grzegorz Grzybek
Details | Diff
A patch making the behavior consistent with Servlet spec 2.3 (for v5.5.20) (5.03 KB, patch)
2007-01-17 15:15 UTC, Christophe Pierret
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description sergio 2005-12-12 11:49:58 UTC
I configured tomcat to use SSL client-certificate authentication and i need to
access the client certificate contained in the request. To do this, i use:

Object cert = request.getAttribute("javax.servlet.request.X509Certificate");

If i do not use the APR components, this works correctly.
If i use the APR components, instead, the object returned by the previous
statement is always null.
The authentication itself works correctly (for instance if CA not valid,
certificate expired, etc..) in both cases.

For use the APR components, i just put the binary files downloaded at:

http://tomcat.heanet.ie/native/1.1.1/binaries/win32/

in the bin directory of my tomcat installation, according the guide found on
tomcat site:

http://tomcat.apache.org/tomcat-5.5-doc/apr.html

This is the SSL HTTP connector extract from my server.xml (non APR version):

<Connector port="443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="want" 
sslProtocol="TLS"
truststoreFile="./tomcat5keystore.jks" truststorePass="xxx"
keystoreFile="./tomcat5keystore.jks" keystorePass="xxx"                 
/>


This is the SSL HTTP connector extract from my server.xml (non APR version):

<Connector port="443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="want" 
SSLEngine="on" 
SSLVerifyClient="optional"
SSLCertificateFile="${catalina.base}/cert.crt"
SSLCertificateKeyFile="${catalina.base}/key.pem"
SSLCACertificateFile="${catalina.base}/ca_postecom.crt"
SSLPassword="xxx"
/>
Comment 1 Mladen Turk 2005-12-12 12:20:52 UTC
Is it working with
SSLVerifyClient="require"

Comment 2 sergio 2005-12-12 12:44:08 UTC
(In reply to comment #1)
> Is it working with
> SSLVerifyClient="require"

No, same behaviour described before.
Non-APR: client-certificate authentication works, certificate obtained in request.
APR: client-certificate authentication works, but no certificate in request.


Comment 3 Mohamed Sadok MOUHA 2006-01-17 12:08:24 UTC
I have the same problem on Apache 2.054, Fedora 4, Tomcat 5.0.30-5jpp_6fc and
mod_jk.i386 1.2.6-3jpp_4fc

All these packages are installed via yum
When I try to get the client cert from the request
(javax.servlet.request.X509Certificate) it fails and I get this message in
catalina.out :
org.apache.jk.server.JkCoyoteHandler action(org.apache.coyote.ActionCode,
java.lang.Object)
SEVERE: Certificate convertion failed

I have found no work arround

Comment 4 Sailoan 2006-07-14 18:25:05 UTC
yes, i got this behaviour too.
i tryed with 
all SSLVerifyClient options and SSLVerifyDepth but still certifcate dont get
forwared to request.

i guess problem is in: org.apache.coyote.http11.Http11AprProcessor 
else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE)
else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE)
Comment 5 Grzegorz Grzybek 2006-11-28 07:35:25 UTC
I have the same problem (Tomcat 6.0.2, Tomcat-Native 1.1.7-win32, Windows XPSP2,
OpenSSL 0.9.8d, APR from Apache-2.2.3).

1) when I HTTP-GET some sample servlet from browser (IE, FireFox),
Http11APRProcessor doesn't insert user certificate into request object

2) when I use "openssl s_client", everything's fine

Simple debug reveals one problem - in request from browser, there is no data in:
int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN)

and so there is no cert chain in:
byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);

but there IS client's certificate in (my added sample line):
SSLSocket.getInfoB(socket, org.apache.tomcat.jni.SSL.SSL_INFO_CLIENT_CERT)

and it is the client's certificate.

I think the problem is with not looking for data under key
"org.apache.tomcat.jni.SSL.SSL_INFO_CLIENT_CERT" but only under
"org.apache.tomcat.jni.SSL.SSL_INFO_CLIENT_CERT_CHAIN"...

with regards
Grzegorz Grzybek

Comment 6 Remy Maucherat 2006-11-28 09:04:23 UTC
If you have tested a patch to also consider SSL_INFO_CLIENT_CERT if the chain is
empty, you can submit it.
Comment 7 Grzegorz Grzybek 2006-11-29 04:11:12 UTC
Created attachment 19196 [details]
Patch to look for client's certificate in Http11AprProcessor

This patch searches for client's certificate when there is no client's
certificate chain under SSL.SSL_INFO_CLIENT_CERT_CHAIN key.
When SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN) returns 0 (not
-1), client's cert is retrieved using SSLSocket.getInfoB(socket,
SSL.SSL_INFO_CLIENT_CERT).
Comment 8 Grzegorz Grzybek 2006-11-29 04:17:00 UTC
I forgot - this patch is for tomcat 6.0.2, it could also be applied to e.g.
5.5.20 (with changed line numbers).

Grzegorz Grzybek
Comment 9 Mark Thomas 2006-12-18 03:55:23 UTC
Re-opening since the patch has yet to be applied.
Comment 10 Christophe Pierret 2007-01-17 15:09:01 UTC
Seems like this one
http://issues.apache.org/bugzilla/show_bug.cgi?id=41382
is the same issue.

The patch given with #41382 was designed for 5.5.20 and fixes also the
bug-replication (bug copy/paste?) found a few lines down in the code. It has
been tested at Sparus Software with various scenarios (e.g.: SSL client does not
give a cert chain, gives a cert chain, etc)

If I am not mistaken, the patch given with #37869 seems incorrect as it replaces
an incorrect behaviour (only giving certification chain excluding client
certificate) with another incorrect behaviour (only giving client cert but not
the certificate chain, see servlet spec for details of what the correct behavior is)

Comment 11 Christophe Pierret 2007-01-17 15:12:45 UTC
*** Bug 41382 has been marked as a duplicate of this bug. ***
Comment 12 Christophe Pierret 2007-01-17 15:15:19 UTC
Created attachment 19425 [details]
A patch making the behavior consistent with Servlet spec 2.3 (for v5.5.20)

Same as patch in #41382
Fixes issues in patch "19196: Patch to look for client's certificate in
Http11AprProcessor"
Comment 13 Marcin.Burdyk 2007-01-20 18:46:48 UTC
Comment on attachment 19196 [details]
Patch to look for client's certificate in Http11AprProcessor

Nigdy nie my&#347;l&#281; o przysz&#322;o&#347;ci. Nadchodzi ona
wystarczaj&#261;co szybko.
Comment 14 Marcin.Burdyk 2007-01-21 15:49:00 UTC
Comment on attachment 19425 [details]
A patch making the behavior consistent with Servlet spec 2.3 (for v5.5.20)

>Index: connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
>===================================================================
>--- Http11AprProcessor.java	(revision 496746)
>+++ Http11AprProcessor.java	(working copy)
>@@ -1095,16 +1095,29 @@
>                             (AprEndpoint.CIPHER_SUITE_KEY, sslO);
>                     }
>                     // Client certificate chain if present
>+                    //////////////////////////////////////////////////
>+                    // CP: does not include client certificate, only CA certificates
>+                    // see documentation of SSL_get_peer_cert_chain() in openssl and sslinfo.c in native APR code
>+                    // SSL_get_peer_cert_chain() returns a pointer to STACKOF(X509) certificates forming the certificate chain of the peer.
>+                    // If called on the client side, the stack also contains the peer's certificate;
>+                    // if called on the server side, the peer's certificate must be obtained separately using SSL_get_peer_certificate(3).
>+                    // If the peer did not present a certificate, NULL is returned.
>+                    //
>                     int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
>+                    // retrieve client certificate
>+                    byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
>                     X509Certificate[] certs = null;
>-                    if (certLength > 0) {
>-                        certs = new X509Certificate[certLength];
>+                    if (clientCert != null) {
>+                        certs = new X509Certificate[certLength+1]; // add one for the client certificate
>+
>+                        CertificateFactory cf =
>+                        CertificateFactory.getInstance("X.509");
>+
>+                        certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
>+
>                         for (int i = 0; i < certLength; i++) {
>                             byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
>-                            CertificateFactory cf =
>-                                CertificateFactory.getInstance("X.509");
>-                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
>-                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
>+                            certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
>                         }
>                     }
>                     if (certs != null) {
>@@ -1142,16 +1155,29 @@
>                     // Renegociate certificates
>                     SSLSocket.renegotiate(socket);
>                     // Client certificate chain if present
>+                    //////////////////////////////////////////////////
>+                    // CP: does not include client certificate, only CA certificates
>+                    // see documentation of SSL_get_peer_cert_chain() in openssl and sslinfo.c in native APR code
>+                    // SSL_get_peer_cert_chain() returns a pointer to STACKOF(X509) certificates forming the certificate chain of the peer.
>+                    // If called on the client side, the stack also contains the peer's certificate;
>+                    // if called on the server side, the peer's certificate must be obtained separately using SSL_get_peer_certificate(3).
>+                    // If the peer did not present a certificate, NULL is returned.
>+                    //
>                     int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
>+                    // retrieve client certificate
>+                    byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
>                     X509Certificate[] certs = null;
>-                    if (certLength > 0) {
>-                        certs = new X509Certificate[certLength];
>+                    if (clientCert != null) {
>+                        certs = new X509Certificate[certLength+1]; // add one for the client certificate
>+
>+                        CertificateFactory cf =
>+                        CertificateFactory.getInstance("X.509");
>+
>+                        certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
>+
>                         for (int i = 0; i < certLength; i++) {
>                             byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
>-                            CertificateFactory cf =
>-                                CertificateFactory.getInstance("X.509");
>-                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
>-                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
>+							certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
>                         }
>                     }
>                     if (certs != null) {
Comment 15 André Cruz 2008-04-23 03:41:12 UTC
I see that 6.0 was patched. Will this be applied to 5.5 as well?
Comment 16 Mark Thomas 2009-07-05 07:35:41 UTC
*** Bug 39637 has been marked as a duplicate of this bug. ***
Comment 17 Mark Thomas 2009-07-09 01:23:07 UTC
This has been fixed in 5.5.x and will be included in 5.5.28 onwards