Setting up two-way (mutual) SSL with Tomcat on Java5 is easy!
I recently wanted to set up Tomcat for two-way (mutual) SSL. It turns out this is fairly easy, particularly with Java5 since it now provides writes, and not just reads, of PKCS#12 keystores directly, so you don't actually need to use openssl anymore, as most of the instructions I found online about this suggest. I needed this for a test of something, and wasn't interested in using a Certificate Authority (CA) etc. All I wanted was an easy set up with Tomcat (v5.5.15/17 used) for testing; other people were going to worry about CA and issuing procedures and all later. Here is how:
1) Create the key & cert for the Tomcat server:
%JAVA_HOME%\bin\keytool -genkey -v -alias tomcat -keyalg RSA -validity 3650 -keystore /path/to/my/tomcat.keystore -dname "CN=localhost, OU=MYOU, O=MYORG, L=MYCITY, ST=MYSTATE, C=MY" -storepass=password -keypass=password
Note that the storepass and keypass *HAS* to be the same here. The CN should be the host/machine name that will appear in the HTTPS URL when accessing this Tomcat, so e.g. localhost, or also *.domain.com I believe.
2) Enable the SSL connector in Tomcat's conf/server.xml:
<connector port="8443" maxhttpheadersize="8192" maxthreads="150" minsparethreads="25" maxsparethreads="75" enablelookups="false" disableuploadtimeout="true" acceptcount="100" scheme="https" secure="true" sslprotocol="TLS" clientauth="true" keystorefile="path/to/my/tomcat.keystore" keystorepass="password" truststorefile="path/to/my/tomcat.keystore" truststorepass="password">
In keystoreFile you can specify an absolute pathname, or a relative pathname that is resolved against the Tomcat root installation directory. You also have to specify the truststore. (The stores don't have to be in a user home directory.)
If all you wanted is well-known one-way SSL with no client certificate, you would have to change the above to clientAuth="false" and stop here, else do as above and keep reading for the interesting part.
3) Create the client (yours, or a machine's) key & cert:
keytool -genkey -v -alias vorburgerKey -keyalg RSA -storetype PKCS12 -keystore vorburger.p12 -dname "CN=Michael Vorburger NEU, OU=DerTest, O=DieOrg, L=Lausanne, ST=VD, C=CH" -storepass mypassword -keypass mypassword
This looks and is similar to 1) above, but we are storing this into a different keystore (because the server/Tomcat doesn't have your private key) of storetype PKCS12. On Windows, you can double-click and import this *.p12 file into IE, or add it to Firefox via Tools / Options, Security, Certificates, View Certificates, Import. You'll have to type in the mypassword (above). BTW, again the storepass and keypass *HAS* to be the same here, else Windows/IE or Mozilla Certificate importing will fail.
4) Now we need to add the certificate (containing the public key) to the Tomcat keystore so that it recognizes this client certificate, by first exporting it from the keystore from step 3 and then importing it into the keystore from step 1: (Typically you probably would not import each client cert but a CA root cert and then sign each client cert with that one, but as said, I am showing a simple setup for tests here.)
keytool -export -alias vorburgerCert -keystore vorburger.p12 -storetype PKCS12 -storepass mypassword -rfc -file vorburger.cer
keytool -import -v -file vorburger.cer -keystore tomcat.keystore -storepass password
On Windows, you can double-click and see the vorburger.cer. You may also would like to list the contents of the tomcat.keystore via this command, and notice how the entry for the 'tomcat' alias is a keyEntry, but the entry for the 'vorburgerCert' is a trustedCertEntry here - makes sense?
keytool -list -keystore ..\tomcat-server.keystore -storepass password
5) Just like with BASIC authentication, you still have to add this user to the usual conf/tomcat-users.xml, by specifying that dname from step 3) as username, and any password (will be ignored) and the roles you'd like that user to have:
<user username="CN=Michael Vorburger NEU, OU=DerTest, O=DieOrg, L=Lausanne, ST=VD, C=CH" password="null" roles="admin">
That's it! You should now be able to access e.g. https://localhost:8443, the browser should ask you for, or automatically send, the client certificate which the server used to 'strongly' authenticate you - done!
Some instructions also suggest to set <login-config><auth-method>CLIENT-CERT</auth-method> in web.xml, but apparently that's not actually needed on Tomcat with clientAuth="true" in server.xml.
Some notes for testing/debugging/trying that may be useful if you would like to try this too: IE just says "Cannot find server" if the server does not accept the client cert - not very helpful! Firefox says "Could not establish an encrypted connection because your certificate was rejected by localhost.", better. Also, if for test you remove a cert from IE again, you have to close it, all windows, shut it down - else it's still in memory although it does not appear in respective dialog anymore.
PS: Some copy/pasted background information that may be of interest... "The PKCS#12 (Personal Information Exchange Syntax Standard, specifies a portable format for storage and/or transport of a user's private keys, certificates, miscellaneous secrets, and other items. The SunJSSE provider supplies a complete implementation of the PKCS12 java.security.KeyStore format for reading and write pkcs12 files. This format is also supported by other toolkits and applications for importing and exporting keys and certificates, such as Netscape/Mozilla, Microsoft's Internet Explorer, and OpenSSL. For example, these implementations can export client certificates and keys into a file using the ".p12" filename extension.
J2SE 1.4.x provided read-only support for PKCS#12 keystores, and a small number of protection algorithms. The enhanced PKCS#12 keystore in J2SE 5 supports reads and writes of PKCS#12 keystores, and provides more protection algorithms (such as those supported by popular browsers). This improves interoperability of PKCS#12 keystores imported/exported by J2SE, browsers, and other security applications."
1) Create the key & cert for the Tomcat server:
%JAVA_HOME%\bin\keytool -genkey -v -alias tomcat -keyalg RSA -validity 3650 -keystore /path/to/my/tomcat.keystore -dname "CN=localhost, OU=MYOU, O=MYORG, L=MYCITY, ST=MYSTATE, C=MY" -storepass=password -keypass=password
Note that the storepass and keypass *HAS* to be the same here. The CN should be the host/machine name that will appear in the HTTPS URL when accessing this Tomcat, so e.g. localhost, or also *.domain.com I believe.
2) Enable the SSL connector in Tomcat's conf/server.xml:
<connector port="8443" maxhttpheadersize="8192" maxthreads="150" minsparethreads="25" maxsparethreads="75" enablelookups="false" disableuploadtimeout="true" acceptcount="100" scheme="https" secure="true" sslprotocol="TLS" clientauth="true" keystorefile="path/to/my/tomcat.keystore" keystorepass="password" truststorefile="path/to/my/tomcat.keystore" truststorepass="password">
In keystoreFile you can specify an absolute pathname, or a relative pathname that is resolved against the Tomcat root installation directory. You also have to specify the truststore. (The stores don't have to be in a user home directory.)
If all you wanted is well-known one-way SSL with no client certificate, you would have to change the above to clientAuth="false" and stop here, else do as above and keep reading for the interesting part.
3) Create the client (yours, or a machine's) key & cert:
keytool -genkey -v -alias vorburgerKey -keyalg RSA -storetype PKCS12 -keystore vorburger.p12 -dname "CN=Michael Vorburger NEU, OU=DerTest, O=DieOrg, L=Lausanne, ST=VD, C=CH" -storepass mypassword -keypass mypassword
This looks and is similar to 1) above, but we are storing this into a different keystore (because the server/Tomcat doesn't have your private key) of storetype PKCS12. On Windows, you can double-click and import this *.p12 file into IE, or add it to Firefox via Tools / Options, Security, Certificates, View Certificates, Import. You'll have to type in the mypassword (above). BTW, again the storepass and keypass *HAS* to be the same here, else Windows/IE or Mozilla Certificate importing will fail.
4) Now we need to add the certificate (containing the public key) to the Tomcat keystore so that it recognizes this client certificate, by first exporting it from the keystore from step 3 and then importing it into the keystore from step 1: (Typically you probably would not import each client cert but a CA root cert and then sign each client cert with that one, but as said, I am showing a simple setup for tests here.)
keytool -export -alias vorburgerCert -keystore vorburger.p12 -storetype PKCS12 -storepass mypassword -rfc -file vorburger.cer
keytool -import -v -file vorburger.cer -keystore tomcat.keystore -storepass password
On Windows, you can double-click and see the vorburger.cer. You may also would like to list the contents of the tomcat.keystore via this command, and notice how the entry for the 'tomcat' alias is a keyEntry, but the entry for the 'vorburgerCert' is a trustedCertEntry here - makes sense?
keytool -list -keystore ..\tomcat-server.keystore -storepass password
5) Just like with BASIC authentication, you still have to add this user to the usual conf/tomcat-users.xml, by specifying that dname from step 3) as username, and any password (will be ignored) and the roles you'd like that user to have:
<user username="CN=Michael Vorburger NEU, OU=DerTest, O=DieOrg, L=Lausanne, ST=VD, C=CH" password="null" roles="admin">
That's it! You should now be able to access e.g. https://localhost:8443, the browser should ask you for, or automatically send, the client certificate which the server used to 'strongly' authenticate you - done!
Some instructions also suggest to set <login-config><auth-method>CLIENT-CERT</auth-method> in web.xml, but apparently that's not actually needed on Tomcat with clientAuth="true" in server.xml.
Some notes for testing/debugging/trying that may be useful if you would like to try this too: IE just says "Cannot find server" if the server does not accept the client cert - not very helpful! Firefox says "Could not establish an encrypted connection because your certificate was rejected by localhost.", better. Also, if for test you remove a cert from IE again, you have to close it, all windows, shut it down - else it's still in memory although it does not appear in respective dialog anymore.
PS: Some copy/pasted background information that may be of interest... "The PKCS#12 (Personal Information Exchange Syntax Standard, specifies a portable format for storage and/or transport of a user's private keys, certificates, miscellaneous secrets, and other items. The SunJSSE provider supplies a complete implementation of the PKCS12 java.security.KeyStore format for reading and write pkcs12 files. This format is also supported by other toolkits and applications for importing and exporting keys and certificates, such as Netscape/Mozilla, Microsoft's Internet Explorer, and OpenSSL. For example, these implementations can export client certificates and keys into a file using the ".p12" filename extension.
J2SE 1.4.x provided read-only support for PKCS#12 keystores, and a small number of protection algorithms. The enhanced PKCS#12 keystore in J2SE 5 supports reads and writes of PKCS#12 keystores, and provides more protection algorithms (such as those supported by popular browsers). This improves interoperability of PKCS#12 keystores imported/exported by J2SE, browsers, and other security applications."
33 Comments:
Hi, Micheal
I have read your blog, but I am still quite puzzle by how the browser, be it IE or Firefox,
I have a requirement, eg a client cert stores CN=01, how do a jsf page on opening, retrieve the value of CN which is 01 ?
regards
James(Singapore)
> "client cert stores CN=01, how do a jsf page on opening, retrieve the value of CN which is 01"
You should be able to obtain the client cert via java.security.cert.X509Certificate certs[] = (X509Certificate[] )HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate")
The full DN (including the CN) should also be returned by HttpServletRequest.getUserPrincipal().getName() I believe.
Hi, Micheal
So sorry, did finish my first comment and posted it.
I am puzzle by how the browser, be it IE or Firefox, loads the client cert information.
I have a requirement, eg a client cert stores CN=01, then we import this client cert into the browser.
this is the confusing part, how do a jsf page on opening, retrieve the value of CN which is 01 ?
regards
James(Singapore)
Thanks, Micheal
I think this is something like this right ?
X509Certificate cert = (X509Certificate)HttpServletRequest.getAttribute("javax.servlet.request.X509Certificate").get(0);
logger.info("Issuer DN : " + cert.getIssuerDN().getName());
regards
James(Singapore)
Hi, Micheal
Why is it that I cant find the attribute name, javax.servlet.request.X509Certificate ?
What is it that I missing out ?
regards
James
Did you configure Tomcat according to steps 1-5 above? If it can't find the certificate, the configuration might be wrong and the browser is not actually sending one, because the server is not requesting it.
BTW: I just fixed some HTML problem, in step 2 (<connector ...>) and step 5 (<user ... >) the correct XML config did not show; sorry about that!
If you are not using Tomcat, you'd have to check your Web Container / AppServer's documentation for how to set up mutual SSL.
I'm not sure that I agree with this statement:
In keystoreFile you can specify an absolute pathname, or a relative pathname that is resolved against the Tomcat root installation directory.
You are saying that the path is relative to the Tomcat root installation directory, which is CATALINA_HOME...but I believe that the real answer should be that it is relative to CATALINA_BASE.
Of course BASE = HOME if you do not specify BASE, but many of us do specify BASE for a different location...so we can use a single physical tomcat install to host multiple Tomcat instances...which is the intended purpose of BASE.
cheers,
Steve G.
Hello Michael,
I can't figure out mutual SSL with Tomcat 5.x.
Estonians are issued smartcards with authentification certificates signed by a root CA maintained by the state.
I've imported the root certif into the tomcat keystore but keep receiving authentification errors. Definitely importing each and every user's certificate is not viable at all.
Could you shed some light on configuring "truststore" related attributes for the HTTPS connector.
Or maybe my self-generated server certificate (DN=localhost for testing purposes) should be signed by the root CA ?
Many thanks for your very helpful blog
This is very helpful, but how can you connect using a pure java client rather than a web browser?
I would like to do 2 way certificate exchange and connect to a site using web services. Is their any java example code to do this?
Thanks,
Yc
Michael,
I have found this very useful in doing some testing for extracting client certificate information when the client is a browser. I would like to know if there are any examples (step by step like you have here) where the client is a web service (also running tomcat). Do you know of any?
Regards,
Andrew
Hi,
i am trying to implement mutual SSL using your blog.. and doing exactly same what you mentioned.. but i am getting problems @ 2 places -
1. in tomcat-users.xml tomcat is throwing error at start-up that we can not use '=' in username.
2. and when i run with mutual SSL i get 'page cannot be displayed' on browser..
Any Help..
Adding to above comment i am getting error like this
-------------------------------------------------------------
http-8443-Processor25, setSoTimeout(60000) called
http-8443-Processor25, READ: SSL v2, contentType = Handshake, translated length = 65
*** ClientHello, SSLv3
RandomCookie: GMT: 0 bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 80, 89, 205, 62, 47, 160, 5, 102
, 80, 49, 28, 126, 203, 157, 15 }
Session ID: {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_
RSA_WITH_DES_CBC_SHA, SSL_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_RSA_
EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_DHE_
DSS_WITH_DES_CBC_SHA, SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA]
Compression Methods: { 0 }
***
%% Created: [Session-1, SSL_RSA_WITH_RC4_128_MD5]
*** ServerHello, SSLv3
RandomCookie: GMT: 1152178331 bytes = { 43, 199, 101, 241, 108, 130, 77, 142, 191, 19, 43, 173, 186, 1
46, 174, 246, 115, 52, 143, 247, 5, 137, 17, 125, 127, 108, 44, 38 }
Session ID: {69, 173, 217, 155, 220, 254, 104, 247, 118, 194, 112, 230, 58, 216, 241, 19, 115, 156, 18
9, 248, 24, 193, 33, 191, 216, 176, 214, 41, 128, 19, 21, 193}
Cipher Suite: SSL_RSA_WITH_RC4_128_MD5
Compression Method: 0
***
Cipher suite: SSL_RSA_WITH_RC4_128_MD5
*** Certificate chain
chain [0] = [
[
Version: V1
Subject: CN=localhost, OU=MYOU, O=MYORG, L=MYCITY, ST=MYSTATE, C=MY
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4
Key: Sun RSA public key, 1023 bits
modulus: 74036248444253570455762772474000893141593496337533608755380078492705036037430210820478461095
7009802857695574846266198863077999910357218396804246073867894670606127190686266470055059536891429483457
9741499382585384440795032804225377417448714338700776966557712230774502295831163450130925706488908591507
5132692189
public exponent: 65537
Validity: [From: Wed Jan 17 13:13:55 GMT+05:30 2007,
To: Sat Jan 14 13:13:55 GMT+05:30 2017]
Issuer: CN=localhost, OU=MYOU, O=MYORG, L=MYCITY, ST=MYSTATE, C=MY
SerialNumber: [ 45add3bb]
]
Algorithm: [MD5withRSA]
Signature:
0000: 53 98 4E 7A EC BC C6 3C 32 D2 2B 59 37 68 BE C4 S.Nz...<2.+Y7h..
0010: C5 DD A3 56 21 58 BD E8 F2 2E 51 37 68 DB 0B 98 ...V!X....Q7h...
0020: 4F 2B 9A 64 EC 34 B0 04 49 22 8D 5A 09 77 D8 69 O+.d.4..I".Z.w.i
0030: EA 5B B1 3D AA 91 21 4B 01 51 BB 29 1B 93 2A 0C .[.=..!K.Q.)..*.
0040: AD 42 FA CC 94 E8 D8 BA CF 77 0C AA 04 A8 99 CE .B.......w......
0050: DC C3 A4 DD 54 5B 4A 7F 72 1D CA FF 19 62 AD 80 ....T[J.r....b..
0060: 78 B8 FC B5 12 A0 1A E6 F0 A8 3C D8 B1 11 37 03 x.........<...7.
0070: 34 D6 75 F4 28 13 4B CB D8 82 3E EC 37 4C 53 B4 4.u.(.K...>.7LS.
]
***
*** CertificateRequest
Cert Types: RSA, DSS,
Cert Authorities:
*** ServerHelloDone
http-8443-Processor25, WRITE: SSLv3 Handshake, length = 5621
http-8443-Processor25, received EOFException: error
http-8443-Processor25, handling exception: javax.net.ssl.SSLHandshakeException: Remote host closed conn
ection during handshake
http-8443-Processor25, SEND SSLv3 ALERT: fatal, description = handshake_failure
http-8443-Processor25, WRITE: SSLv3 Alert, length = 2
http-8443-Processor25, called closeSocket()
http-8443-Processor24, RECV SSLv3 ALERT: warning, no_certificate
SSL -- handshake alert: no_certificate
http-8443-Processor24, handling exception: javax.net.ssl.SSLProtocolException: handshake alert: no_certificate
http-8443-Processor24, SEND SSLv3 ALERT: fatal, description = unexpected_message
You should email the tomcat-users mailing list.
At least in my tomcat 5 server.xml it was necessary to have the params
keystorefile="path/to/my/tomcat.keystore" keystorepass="password" truststorefile="path/to/my/tomcat.keystore" truststorepass="password">
as keystoreFile, keystorePass, truststoreFile, truststorePass (instead of keystorefile etc, upperCase)
Thanks for the tutorial!
Hello Michael,
thanks for your great article. I did everything you suggested and it worked fine. Almost... I cannot get my IE to send the client certificate. I'm running WinXP and just did a doubleclick on the p12 file and went through the following assistant. In my IE settings I see the certificate. So far so good. Now when I try to access my SSL website I get asked to choose with which certificate I want to authorize. But the list is empty. I cannot choose any certificate which results in me not beeing able to access my site obviously.
Is there anything I have to do? I even checked the certificate to be used for client authorization in the settings and restarted my computer. Still it does not work.
Any suggestions anyone?
I do have the same problem, the cert does not appear in the list of client certificates to use (i.e. this list is empty). Bye, Werner
Could someone please update the procedure which works?
hi,Micheal
Thank you very much that after reading your great blog, I got my tomecat with SSL working now.
the following is what I use
tomcat 5.5.20
jdk1.5.0_14
OS: XP SP2
but,
1)
the step 3 and step 4 alias are different, they should be the same
either vorburgerKey or vorburgerCert, I think.
2)
I dont know why but if not delete the file "tcnative-1.dll", the https would not work, can anybody tell me why? ;(
Thank you!
this was of great help to me >
Thankyou very much.
These kind of quality work is highly appreciated .
Hi Michael,
Thanks for your Article, it helped me a lot :).
best regards,
Michael
Thank you, this was very useful, since I didn't find the syntax how the X509 DN maps to a tomcat user elsewhere.
Hi, Michael
This post helped me a lot. But I'm facing a problem. If the username corresponding to the "dname" of client certificate is not in the "tomcat-users.xml" file, I get an HTTP 401 error.
It's embarrassing because there may be a lot of users, each with its own certificate, and I don't want to have to add all users in "tomcat-users.xml".
Is there a way to do this ?
Excellent post, this helped me a lot. You may want to add a "-alias" option during import, otherwise the key ends up as "mykey" in the keystore.
Cheers,
André
hi Michael,
i've tried following the same procedure as adviced, but i am unable to see anything on the IE browser when i place a request,
on Google chrome i get the following error 'SSL connection error'
tomcat used is v5.5
i've been sitting on this since 5 days and haven't made significant progress.
Please help.
hi Michael,
i've tried following the same procedure as adviced, but i am unable to see anything on the IE browser when i place a request,
on Google chrome i get the following error 'SSL connection error'
tomcat used is v5.5
i've been sitting on this since 5 days and haven't made significant progress.
Please help.
hi Michael,
i've tried following the same procedure as adviced, but i am unable to see anything on the IE browser when i place a request,
on Google chrome i get the following error 'SSL connection error'
tomcat used is v5.5
i've been sitting on this since 5 days and haven't made significant progress.
Please help.
hi Mike,
I tried the steps as u have said, but still unable to set up.
can you suggest why?
Just like a previous poster said, I had to ensure that all of the parameters in the <Connector.. node were of the proper upper and lower-case.
The case-sensitive params that work for me are: keystoreFile, keystorePass, truststoreFile, truststorePass, maxThreads, SSLEnabled, sslProtocol.
Initially, when they were all lowercase, when I went to https://localhost:8443 , Firefox would give this error:
Secure Connection Failed
An error occurred during a connection to localhost:8443.
SSL received a record that exceeded the maximum permissible length.
(Error code: ssl_error_rx_record_too_long)
The following versions are being used:
* Firefox v3.6.20
* Ubuntu v10.10 x86
* apache-tomcat-6.0.26
For me, the solution to the problem of " the cert does not appear in the list of client certificates to use" (like what previous comment-posters asked), was this:
* The certificate that was not showing up for me was a certificate issued to me by a recognized root CA (ORC).
* I had to import everything in the certificate chain (which was 2 certificates for me). This means,
** must import the root CA's certificate into the Tomcat keystore.
** and also must import any intermediate CA certs as well into the Tomcat keystore (only 1 for me).
After doing both of these, Firefox allowed me to choose the certificate I wanted to use which had been previously hidden.
Hello Michael,
I was going through your excellent blog at
http://www.vorburger.ch/blog1/2006/08/setting-up-two-way-mutual-ssl-with.html
I have one thing to clarify from you.
I deeply appreicate your response.
I want to know that if I do 2-way SSL for SSL handshake, still
the whole message encryption is Symmetric.
(similar to one-way SSL)
Just because client has certificate does NOT mean that now 2 key pairs will be used for encyrption and signature.
Thanks.
Chirag
Good article. On my IE 8 I didn't have to restart windows when I removed the certificate from the certificate repository.
Anyway, very useful article! Thanks
Not sure why you would mention adding the user to tomcat-users.xml, as that is not needed for 2 way ssl?
Dear Michael,
Very useful blog.
Good work & thanks alot for sharing this.
Regards,
Gajendra
Post a Comment
<< Home