You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Robert Sulliman <ro...@sjrb.ca> on 2016/11/14 18:25:06 UTC

Tomcat - Two Way SSL as Server

Hi All,

I'm trying to implement two way SSL on a new web service that we are building and I'm having some issues.

First some info on  the environment.

Server version: Apache Tomcat/8.0.36
Server built:   Jun 9 2016 13:55:50 UTC
Server number:  8.0.36.0
OS Name:        Linux
OS Version:     3.10.0-514.el7.x86_64
Architecture:   amd64
JVM Version:    1.8.0_111-b14
JVM Vendor:     Oracle Corporation

We use an internal certificate authority to sign all of our certificates. So all the client certificates are signed by our internal root. When I trust the root certificate in the client trust store everything works. All client certificates signed by the internal root work.

However, if I remove the root certificate from the client trust store, and add individual client certificates instead I get a cert chain error.
________________________________
*** ECDH ServerKeyExchange
Signature Algorithm SHA512withRSA
Server key: Sun EC public key, 256 bits
  public x coord: 107108750176335210433834926983330116805775068919227166974389735341685270962458
  public y coord: 93195725734236902743006469378087068209149058097948526490562555560744449337507
  parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Cert Authorities:
<CN=Client, OU=Information Technology, O=Company, L=Calgary, ST=Alberta, C=CA>
*** ServerHelloDone
http-nio2-8443-exec-4, WRITE: TLSv1.2 Handshake, length = 4482
http-nio2-8443-exec-2, READ: TLSv1.2 Handshake, length = 7
*** Certificate chain
<Empty>
***
http-nio2-8443-exec-2, fatal error: 42: null cert chain
javax.net.ssl.SSLHandshakeException: null cert chain
%% Invalidated:  [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
http-nio2-8443-exec-2, SEND TLSv1.2 ALERT:  fatal, description = bad_certificate
http-nio2-8443-exec-2, WRITE: TLSv1.2 Alert, length = 2
http-nio2-8443-exec-2, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLHandshakeException: null cert chain
http-nio2-8443-exec-2, called closeOutbound()
http-nio2-8443-exec-2, closeOutboundInternal()
________________________________
This is an issue for us as we can't have all the client certificates in the company granted access to this endpoint, it kind of defeats the purpose.

The company root certificate is in another trust store used on server startup. Here are my configs.

Server.xml connector:
________________________________
   <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               port="8443" maxThreads="24" minSpareThreads="4" maxSpareThreads="4" acceptCount="1000" server=" "
               scheme="https" secure="true" SSLEnabled="true"
               keystoreFile="certs/servercert.jks" keystorePass=" CrazyPasswordHere"
               clientAuth="true" truststoreFile="/usr/local/tomcat/certs/clienttrust.jks" truststorePass="CrazyPasswordHere"
               sslEnabledProtocols="TLSv1.2" sslProtocol="TLS"
               ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
               TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
               useServerCipherSuitesOrder="true" compression="on" compressionMinSize="2048"
               compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript" />
________________________________
Systemd init:
________________________________
# Systemd unit file for tomcat
[Unit]
Description=Apache Tomcat
After=syslog.target network.target

[Service]
Type=forking

Environment=JAVA_HOME=/usr/lib/jvm/jre
Environment=CATALINA_PID=/usr/local/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/usr/local/tomcat
Environment=CATALINA_BASE=/usr/local/tomcat
Environment='CATALINA_OPTS= -Xms2048M -Xmx2048M -server -XX:+UseParallelGC \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=8090 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.password.file=/usr/local/tomcat/conf/jmxremote.password \
-Dcom.sun.management.jmxremote.access.file=/usr/local/tomcat/conf/jmxremote.access \
-Djavax.net.debug=SSL \
-Djavax.net.ssl.trustStore=/usr/local/tomcat/certs/servertrust.jks \
-Djavax.net.ssl.trustStorePassword=CrazyPasswordHere \
-Djavax.net.ssl.keyStore=/usr/local/tomcat/certs/serverclient.jks \
-Djavax.net.ssl.keyStorePassword=CrazyPasswordHere '
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'

ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/bin/kill -15 $MAINPID

User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target
________________________________

Thanks!

Robert Sulliman

RE: Tomcat - Two Way SSL as Server

Posted by Jo...@wellsfargo.com.
> -----Original Message-----
> From: Robert Sulliman [mailto:robert.sulliman@sjrb.ca]
> Sent: Monday, November 14, 2016 2:46 PM
> To: Tomcat Users List
> Subject: RE: Tomcat - Two Way SSL as Server
> 
> Thanks John,
> 
> I am trying to do #2, manually adding client certificates to the trust store.
> However it doesn't work unless I add the root certificate to the trust store as
> well, or I get the certificate chain error below. It is a headache to handle certs
> like this, but as a rule of thumb we leave the responsibility for these certs on the
> client themselves.
> 
> I'm pretty sure I'm not going to persuade security to create a new CA for me just
> for this one service... If I use a custom servlet, I lose the ability to do revocation
> checks on the certificates (I'm assuming that Tomcat does this natively, I haven't
> actually tested it yet.)
> 
> Robert Sulliman
> 
> -----Original Message-----
> From: John.E.Gregg@wellsfargo.com [mailto:John.E.Gregg@wellsfargo.com]
> Sent: Monday, November 14, 2016 1:24 PM
> To: users@tomcat.apache.org
> Subject: RE: Tomcat - Two Way SSL as Server
> 
> 
> 
> 
> 
> > -----Original Message-----
> > From: Robert Sulliman [mailto:robert.sulliman@sjrb.ca]
> > Sent: Monday, November 14, 2016 12:25 PM
> > To: users@tomcat.apache.org
> > Subject: Tomcat - Two Way SSL as Server
> >
> > Hi All,
> >
> > I'm trying to implement two way SSL on a new web service that we are
> > building and I'm having some issues.
> >
> > First some info on  the environment.
> >
> > Server version: Apache Tomcat/8.0.36
> > Server built:   Jun 9 2016 13:55:50 UTC
> > Server number:  8.0.36.0
> > OS Name:        Linux
> > OS Version:     3.10.0-514.el7.x86_64
> > Architecture:   amd64
> > JVM Version:    1.8.0_111-b14
> > JVM Vendor:     Oracle Corporation
> >
> > We use an internal certificate authority to sign all of our
> > certificates. So all the client certificates are signed by our
> > internal root. When I trust the root certificate in the client trust
> > store everything works. All client certificates signed by the internal root work.
> >
> > However, if I remove the root certificate from the client trust store,
> > and add individual client certificates instead I get a cert chain error.
> > ________________________________
> > *** ECDH ServerKeyExchange
> > Signature Algorithm SHA512withRSA
> > Server key: Sun EC public key, 256 bits
> >   public x coord:
> >
> 10710875017633521043383492698333011680577506891922716697438973534
> > 1685270962458
> >   public y coord:
> >
> 93195725734236902743006469378087068209149058097948526490562555560
> > 744449337507
> >   parameters: secp256r1 [NIST P-256, X9.62 prime256v1]
> > (1.2.840.10045.3.1.7)
> > *** CertificateRequest
> > Cert Types: RSA, DSS, ECDSA
> > Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA,
> > SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA,
> > SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA,
> > SHA1withECDSA, SHA1withRSA, SHA1withDSA Cert Authorities:
> > <CN=Client, OU=Information Technology, O=Company, L=Calgary,
> > ST=Alberta, C=CA>
> > *** ServerHelloDone
> > http-nio2-8443-exec-4, WRITE: TLSv1.2 Handshake, length = 4482
> > http-nio2- 8443-exec-2, READ: TLSv1.2 Handshake, length = 7
> > *** Certificate chain
> > <Empty>
> > ***
> > http-nio2-8443-exec-2, fatal error: 42: null cert chain
> > javax.net.ssl.SSLHandshakeException: null cert chain %% Invalidated:
> > [Session- 2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
> > http-nio2-8443-exec-2, SEND TLSv1.2 ALERT:  fatal, description =
> > bad_certificate http-nio2-8443-exec-2, WRITE: TLSv1.2 Alert, length =
> > 2 http- nio2-8443-exec-2, fatal: engine already closed.  Rethrowing
> > javax.net.ssl.SSLHandshakeException: null cert chain
> > http-nio2-8443-exec-2, called closeOutbound() http-nio2-8443-exec-2,
> > closeOutboundInternal() ________________________________ This is an
> > issue for us as we can't have all the client certificates in the
> > company granted access to this endpoint, it kind of defeats the purpose.
> >
> > The company root certificate is in another trust store used on server startup.
> > Here are my configs.
> >
> > Server.xml connector:
> > ________________________________
> >    <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
> >                port="8443" maxThreads="24" minSpareThreads="4"
> > maxSpareThreads="4" acceptCount="1000" server=" "
> >                scheme="https" secure="true" SSLEnabled="true"
> >                keystoreFile="certs/servercert.jks" keystorePass="
> CrazyPasswordHere"
> >                clientAuth="true"
> > truststoreFile="/usr/local/tomcat/certs/clienttrust.jks"
> > truststorePass="CrazyPasswordHere"
> >                sslEnabledProtocols="TLSv1.2" sslProtocol="TLS"
> >
> >
> ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WIT
> > H_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
> >
> >
> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_C
> > BC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
> >                useServerCipherSuitesOrder="true" compression="on"
> > compressionMinSize="2048"
> >
> > compressableMimeType="text/html,text/xml,text/plain,text/css,text/java
> > script, application/javascript" /> ________________________________
> > Systemd init:
> > ________________________________
> > # Systemd unit file for tomcat
> > [Unit]
> > Description=Apache Tomcat
> > After=syslog.target network.target
> >
> > [Service]
> > Type=forking
> >
> > Environment=JAVA_HOME=/usr/lib/jvm/jre
> > Environment=CATALINA_PID=/usr/local/tomcat/temp/tomcat.pid
> > Environment=CATALINA_HOME=/usr/local/tomcat
> > Environment=CATALINA_BASE=/usr/local/tomcat
> > Environment='CATALINA_OPTS= -Xms2048M -Xmx2048M -server -
> > XX:+UseParallelGC \ -Dcom.sun.management.jmxremote \
> > -Dcom.sun.management.jmxremote.port=8090 \ -
> > Dcom.sun.management.jmxremote.ssl=false \ -
> > Dcom.sun.management.jmxremote.authenticate=true \ -
> >
> Dcom.sun.management.jmxremote.password.file=/usr/local/tomcat/conf/jmx
> > r
> > emote.password \ -
> > Dcom.sun.management.jmxremote.access.file=/usr/local/tomcat/conf/jmxre
> > m ote.access \ -Djavax.net.debug=SSL \ -
> > Djavax.net.ssl.trustStore=/usr/local/tomcat/certs/servertrust.jks \ -
> > Djavax.net.ssl.trustStorePassword=CrazyPasswordHere \ -
> > Djavax.net.ssl.keyStore=/usr/local/tomcat/certs/serverclient.jks \ -
> > Djavax.net.ssl.keyStorePassword=CrazyPasswordHere '
> > Environment='JAVA_OPTS=-Djava.awt.headless=true -
> > Djava.security.egd=file:/dev/./urandom'
> >
> > ExecStart=/usr/local/tomcat/bin/startup.sh
> > ExecStop=/bin/kill -15 $MAINPID
> >
> > User=tomcat
> > Group=tomcat
> >
> > [Install]
> > WantedBy=multi-user.target
> > ________________________________
> >
> > Thanks!
> >
> > Robert Sulliman
> 
> 
> When you say "we can't have all the client certificates in the company granted
> access to this endpoint," it sounds like you're talking about a whitelist on the
> server.  I think that's possible with Apache, but not in Tomcat itself that I know
> of.
> 
> What I've seen done instead is use a custom servlet filter that grabs the subject
> from the client certificate and checks it against a DB, config file, etc.  You can
> access the client cert using
> request.getAttribute("javax.servlet.request.X509Certificate").  It doesn't have to
> be a literal full match.  Instead you can have the code enforce a rule like this: "In
> test environments, the subject must contain OU=TEST and the CN can be one of
> foo, bar, baz, etc."  You can make it as simple or complex as you want.
> 
> You'll still need to set the server to require client auth and will still need the
> server to trust the internal CA that signs the clients' certs.  Technically any client
> with a cert signed by that CA will be able to connect, but your servlet filter
> should stop the wrong clients from getting any farther.  This is definitely one of
> those cases where you want the code to fail closed!
> 
> If you don't want to go this route, other things I can think of are:
> 
> 1. Create a dedicated internal CA for just this purpose and trust it instead of the
> company-wide one.
> 2. Add all client certs directly to the server's trust store.  Is this what you were
> trying to do?  I don't know why it didn't work, but this will be a huge
> maintenance headache.  Inevitably clients will forget to tell you when they get
> new certs and suddenly they'll be locked out.
> 
> 
> John
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org

Sorry, I don't know what that error means.

Since you mentioned CRLs, I found that Tomcat already supports a connector property called trustManagerClassName.  Sounds like this could be your one-stop-shop for checking a CRL and authorizing the client.  Doing so could still be based on the subject, or whatever you want, but the code would be in a custom trust manager instead of a servlet filter.  I think I actually like this better.



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


RE: Tomcat - Two Way SSL as Server

Posted by Robert Sulliman <ro...@sjrb.ca>.
Thanks John,

I am trying to do #2, manually adding client certificates to the trust store. However it doesn't work unless I add the root certificate to the trust store as well, or I get the certificate chain error below. It is a headache to handle certs like this, but as a rule of thumb we leave the responsibility for these certs on the client themselves.

I'm pretty sure I'm not going to persuade security to create a new CA for me just for this one service... If I use a custom servlet, I lose the ability to do revocation checks on the certificates (I'm assuming that Tomcat does this natively, I haven't actually tested it yet.)

Robert Sulliman 

-----Original Message-----
From: John.E.Gregg@wellsfargo.com [mailto:John.E.Gregg@wellsfargo.com] 
Sent: Monday, November 14, 2016 1:24 PM
To: users@tomcat.apache.org
Subject: RE: Tomcat - Two Way SSL as Server





> -----Original Message-----
> From: Robert Sulliman [mailto:robert.sulliman@sjrb.ca]
> Sent: Monday, November 14, 2016 12:25 PM
> To: users@tomcat.apache.org
> Subject: Tomcat - Two Way SSL as Server
> 
> Hi All,
> 
> I'm trying to implement two way SSL on a new web service that we are 
> building and I'm having some issues.
> 
> First some info on  the environment.
> 
> Server version: Apache Tomcat/8.0.36
> Server built:   Jun 9 2016 13:55:50 UTC
> Server number:  8.0.36.0
> OS Name:        Linux
> OS Version:     3.10.0-514.el7.x86_64
> Architecture:   amd64
> JVM Version:    1.8.0_111-b14
> JVM Vendor:     Oracle Corporation
> 
> We use an internal certificate authority to sign all of our 
> certificates. So all the client certificates are signed by our 
> internal root. When I trust the root certificate in the client trust 
> store everything works. All client certificates signed by the internal root work.
> 
> However, if I remove the root certificate from the client trust store, 
> and add individual client certificates instead I get a cert chain error.
> ________________________________
> *** ECDH ServerKeyExchange
> Signature Algorithm SHA512withRSA
> Server key: Sun EC public key, 256 bits
>   public x coord:
> 10710875017633521043383492698333011680577506891922716697438973534
> 1685270962458
>   public y coord:
> 93195725734236902743006469378087068209149058097948526490562555560
> 744449337507
>   parameters: secp256r1 [NIST P-256, X9.62 prime256v1] 
> (1.2.840.10045.3.1.7)
> *** CertificateRequest
> Cert Types: RSA, DSS, ECDSA
> Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, 
> SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, 
> SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, 
> SHA1withECDSA, SHA1withRSA, SHA1withDSA Cert Authorities:
> <CN=Client, OU=Information Technology, O=Company, L=Calgary, 
> ST=Alberta, C=CA>
> *** ServerHelloDone
> http-nio2-8443-exec-4, WRITE: TLSv1.2 Handshake, length = 4482 
> http-nio2- 8443-exec-2, READ: TLSv1.2 Handshake, length = 7
> *** Certificate chain
> <Empty>
> ***
> http-nio2-8443-exec-2, fatal error: 42: null cert chain
> javax.net.ssl.SSLHandshakeException: null cert chain %% Invalidated:  
> [Session- 2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
> http-nio2-8443-exec-2, SEND TLSv1.2 ALERT:  fatal, description = 
> bad_certificate http-nio2-8443-exec-2, WRITE: TLSv1.2 Alert, length = 
> 2 http- nio2-8443-exec-2, fatal: engine already closed.  Rethrowing
> javax.net.ssl.SSLHandshakeException: null cert chain 
> http-nio2-8443-exec-2, called closeOutbound() http-nio2-8443-exec-2, 
> closeOutboundInternal() ________________________________ This is an 
> issue for us as we can't have all the client certificates in the 
> company granted access to this endpoint, it kind of defeats the purpose.
> 
> The company root certificate is in another trust store used on server startup.
> Here are my configs.
> 
> Server.xml connector:
> ________________________________
>    <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
>                port="8443" maxThreads="24" minSpareThreads="4"
> maxSpareThreads="4" acceptCount="1000" server=" "
>                scheme="https" secure="true" SSLEnabled="true"
>                keystoreFile="certs/servercert.jks" keystorePass=" CrazyPasswordHere"
>                clientAuth="true"
> truststoreFile="/usr/local/tomcat/certs/clienttrust.jks"
> truststorePass="CrazyPasswordHere"
>                sslEnabledProtocols="TLSv1.2" sslProtocol="TLS"
> 
> ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WIT
> H_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
> 
> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_C
> BC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
>                useServerCipherSuitesOrder="true" compression="on"
> compressionMinSize="2048"
> 
> compressableMimeType="text/html,text/xml,text/plain,text/css,text/java
> script, application/javascript" /> ________________________________ 
> Systemd init:
> ________________________________
> # Systemd unit file for tomcat
> [Unit]
> Description=Apache Tomcat
> After=syslog.target network.target
> 
> [Service]
> Type=forking
> 
> Environment=JAVA_HOME=/usr/lib/jvm/jre
> Environment=CATALINA_PID=/usr/local/tomcat/temp/tomcat.pid
> Environment=CATALINA_HOME=/usr/local/tomcat
> Environment=CATALINA_BASE=/usr/local/tomcat
> Environment='CATALINA_OPTS= -Xms2048M -Xmx2048M -server - 
> XX:+UseParallelGC \ -Dcom.sun.management.jmxremote \
> -Dcom.sun.management.jmxremote.port=8090 \ - 
> Dcom.sun.management.jmxremote.ssl=false \ - 
> Dcom.sun.management.jmxremote.authenticate=true \ - 
> Dcom.sun.management.jmxremote.password.file=/usr/local/tomcat/conf/jmx
> r
> emote.password \ -
> Dcom.sun.management.jmxremote.access.file=/usr/local/tomcat/conf/jmxre
> m ote.access \ -Djavax.net.debug=SSL \ - 
> Djavax.net.ssl.trustStore=/usr/local/tomcat/certs/servertrust.jks \ - 
> Djavax.net.ssl.trustStorePassword=CrazyPasswordHere \ - 
> Djavax.net.ssl.keyStore=/usr/local/tomcat/certs/serverclient.jks \ - 
> Djavax.net.ssl.keyStorePassword=CrazyPasswordHere '
> Environment='JAVA_OPTS=-Djava.awt.headless=true - 
> Djava.security.egd=file:/dev/./urandom'
> 
> ExecStart=/usr/local/tomcat/bin/startup.sh
> ExecStop=/bin/kill -15 $MAINPID
> 
> User=tomcat
> Group=tomcat
> 
> [Install]
> WantedBy=multi-user.target
> ________________________________
> 
> Thanks!
> 
> Robert Sulliman
 

When you say "we can't have all the client certificates in the company granted access to this endpoint," it sounds like you're talking about a whitelist on the server.  I think that's possible with Apache, but not in Tomcat itself that I know of.

What I've seen done instead is use a custom servlet filter that grabs the subject from the client certificate and checks it against a DB, config file, etc.  You can access the client cert using request.getAttribute("javax.servlet.request.X509Certificate").  It doesn't have to be a literal full match.  Instead you can have the code enforce a rule like this: "In test environments, the subject must contain OU=TEST and the CN can be one of foo, bar, baz, etc."  You can make it as simple or complex as you want.

You'll still need to set the server to require client auth and will still need the server to trust the internal CA that signs the clients' certs.  Technically any client with a cert signed by that CA will be able to connect, but your servlet filter should stop the wrong clients from getting any farther.  This is definitely one of those cases where you want the code to fail closed!

If you don't want to go this route, other things I can think of are:

1. Create a dedicated internal CA for just this purpose and trust it instead of the company-wide one.
2. Add all client certs directly to the server's trust store.  Is this what you were trying to do?  I don't know why it didn't work, but this will be a huge maintenance headache.  Inevitably clients will forget to tell you when they get new certs and suddenly they'll be locked out.  


John


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


RE: Tomcat - Two Way SSL as Server

Posted by Jo...@wellsfargo.com.



> -----Original Message-----
> From: Robert Sulliman [mailto:robert.sulliman@sjrb.ca]
> Sent: Monday, November 14, 2016 12:25 PM
> To: users@tomcat.apache.org
> Subject: Tomcat - Two Way SSL as Server
> 
> Hi All,
> 
> I'm trying to implement two way SSL on a new web service that we are building
> and I'm having some issues.
> 
> First some info on  the environment.
> 
> Server version: Apache Tomcat/8.0.36
> Server built:   Jun 9 2016 13:55:50 UTC
> Server number:  8.0.36.0
> OS Name:        Linux
> OS Version:     3.10.0-514.el7.x86_64
> Architecture:   amd64
> JVM Version:    1.8.0_111-b14
> JVM Vendor:     Oracle Corporation
> 
> We use an internal certificate authority to sign all of our certificates. So all the
> client certificates are signed by our internal root. When I trust the root
> certificate in the client trust store everything works. All client certificates signed
> by the internal root work.
> 
> However, if I remove the root certificate from the client trust store, and add
> individual client certificates instead I get a cert chain error.
> ________________________________
> *** ECDH ServerKeyExchange
> Signature Algorithm SHA512withRSA
> Server key: Sun EC public key, 256 bits
>   public x coord:
> 10710875017633521043383492698333011680577506891922716697438973534
> 1685270962458
>   public y coord:
> 93195725734236902743006469378087068209149058097948526490562555560
> 744449337507
>   parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
> *** CertificateRequest
> Cert Types: RSA, DSS, ECDSA
> Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA,
> SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA,
> SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA,
> SHA1withECDSA, SHA1withRSA, SHA1withDSA Cert Authorities:
> <CN=Client, OU=Information Technology, O=Company, L=Calgary, ST=Alberta,
> C=CA>
> *** ServerHelloDone
> http-nio2-8443-exec-4, WRITE: TLSv1.2 Handshake, length = 4482 http-nio2-
> 8443-exec-2, READ: TLSv1.2 Handshake, length = 7
> *** Certificate chain
> <Empty>
> ***
> http-nio2-8443-exec-2, fatal error: 42: null cert chain
> javax.net.ssl.SSLHandshakeException: null cert chain %% Invalidated:  [Session-
> 2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
> http-nio2-8443-exec-2, SEND TLSv1.2 ALERT:  fatal, description =
> bad_certificate http-nio2-8443-exec-2, WRITE: TLSv1.2 Alert, length = 2 http-
> nio2-8443-exec-2, fatal: engine already closed.  Rethrowing
> javax.net.ssl.SSLHandshakeException: null cert chain http-nio2-8443-exec-2,
> called closeOutbound() http-nio2-8443-exec-2, closeOutboundInternal()
> ________________________________ This is an issue for us as we can't have
> all the client certificates in the company granted access to this endpoint, it kind
> of defeats the purpose.
> 
> The company root certificate is in another trust store used on server startup.
> Here are my configs.
> 
> Server.xml connector:
> ________________________________
>    <Connector protocol="org.apache.coyote.http11.Http11Nio2Protocol"
>                port="8443" maxThreads="24" minSpareThreads="4"
> maxSpareThreads="4" acceptCount="1000" server=" "
>                scheme="https" secure="true" SSLEnabled="true"
>                keystoreFile="certs/servercert.jks" keystorePass=" CrazyPasswordHere"
>                clientAuth="true"
> truststoreFile="/usr/local/tomcat/certs/clienttrust.jks"
> truststorePass="CrazyPasswordHere"
>                sslEnabledProtocols="TLSv1.2" sslProtocol="TLS"
> 
> ciphers="TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WIT
> H_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
> 
> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_C
> BC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
>                useServerCipherSuitesOrder="true" compression="on"
> compressionMinSize="2048"
> 
> compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,
> application/javascript" /> ________________________________ Systemd init:
> ________________________________
> # Systemd unit file for tomcat
> [Unit]
> Description=Apache Tomcat
> After=syslog.target network.target
> 
> [Service]
> Type=forking
> 
> Environment=JAVA_HOME=/usr/lib/jvm/jre
> Environment=CATALINA_PID=/usr/local/tomcat/temp/tomcat.pid
> Environment=CATALINA_HOME=/usr/local/tomcat
> Environment=CATALINA_BASE=/usr/local/tomcat
> Environment='CATALINA_OPTS= -Xms2048M -Xmx2048M -server -
> XX:+UseParallelGC \ -Dcom.sun.management.jmxremote \
> -Dcom.sun.management.jmxremote.port=8090 \ -
> Dcom.sun.management.jmxremote.ssl=false \ -
> Dcom.sun.management.jmxremote.authenticate=true \ -
> Dcom.sun.management.jmxremote.password.file=/usr/local/tomcat/conf/jmxr
> emote.password \ -
> Dcom.sun.management.jmxremote.access.file=/usr/local/tomcat/conf/jmxrem
> ote.access \ -Djavax.net.debug=SSL \ -
> Djavax.net.ssl.trustStore=/usr/local/tomcat/certs/servertrust.jks \ -
> Djavax.net.ssl.trustStorePassword=CrazyPasswordHere \ -
> Djavax.net.ssl.keyStore=/usr/local/tomcat/certs/serverclient.jks \ -
> Djavax.net.ssl.keyStorePassword=CrazyPasswordHere '
> Environment='JAVA_OPTS=-Djava.awt.headless=true -
> Djava.security.egd=file:/dev/./urandom'
> 
> ExecStart=/usr/local/tomcat/bin/startup.sh
> ExecStop=/bin/kill -15 $MAINPID
> 
> User=tomcat
> Group=tomcat
> 
> [Install]
> WantedBy=multi-user.target
> ________________________________
> 
> Thanks!
> 
> Robert Sulliman
 

When you say "we can't have all the client certificates in the company granted access to this endpoint," it sounds like you're talking about a whitelist on the server.  I think that's possible with Apache, but not in Tomcat itself that I know of.

What I've seen done instead is use a custom servlet filter that grabs the subject from the client certificate and checks it against a DB, config file, etc.  You can access the client cert using request.getAttribute("javax.servlet.request.X509Certificate").  It doesn't have to be a literal full match.  Instead you can have the code enforce a rule like this: "In test environments, the subject must contain OU=TEST and the CN can be one of foo, bar, baz, etc."  You can make it as simple or complex as you want.

You'll still need to set the server to require client auth and will still need the server to trust the internal CA that signs the clients' certs.  Technically any client with a cert signed by that CA will be able to connect, but your servlet filter should stop the wrong clients from getting any farther.  This is definitely one of those cases where you want the code to fail closed!

If you don't want to go this route, other things I can think of are:

1. Create a dedicated internal CA for just this purpose and trust it instead of the company-wide one.
2. Add all client certs directly to the server's trust store.  Is this what you were trying to do?  I don't know why it didn't work, but this will be a huge maintenance headache.  Inevitably clients will forget to tell you when they get new certs and suddenly they'll be locked out.  


John


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org