2

I am trying to use https between two tomcat servers. Unfortunately, the self-signed certificates are causing this error:

Caused by: javax.net.ssl.SSLHandshakeException: 
           sun.security.validator.ValidatorException: PKIX path building failed: 
           sun.security.provider.certpath.SunCertPathBuilderException: 
                      unable to find valid certification path to requested target

Specifically, I have a master tomcat and a number of slave tomcat servers. The master communicates from a servlet using a simple HttpURLConnection.

What is the simplest way for me to create self signed certificates using my own self generated Certificate Authority, such that every time I add a new server, I do not need to change the master tomcat server.

I have access to openssl and java 7 keytool

For reference my previous configuration:

The server.xml connector:

<Connector port="443" maxHttpHeaderSize="8192" maxThreads="150" 
    minSpareThreads="25" maxSpareThreads="75" enableLookups="false" 
    disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" 
    secure="true" clientAuth="false" sslProtocol="TLS" keystoreType="PKCS12" 
    keystoreFile="/usr/java/apache-tomee-plus/conf/keystore.ks" 
    keystorePass="XXX_SSL" truststoreType="JKS" 
    truststoreFile="/usr/java/apache-tomee-plus/conf/truststore.ks" 
    SSLEnabled="true" maxPostSize="0"/>

The startup script /etc/init.d/tomee

$DAEMON_HOME/jsvc \
-user $TOMCAT_USER \
-home $JAVA_HOME \
-pidfile $JSVC_PID_FILE \
-Dcatalina.home=$CATALINA_HOME \
-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.conf \
-Djavax.net.ssl.keystore=$CATALINA_HOME/conf/keystore.ks \
-Djavax.net.ssl.keyStorePassword=XXX_SSL \
-Djavax.net.ssl.trustStore=$CATALINA_HOME/conf/truststore.ks \
-Djavax.net.ssl.trustStorePassword=changeit \
-Djava.awt.headless=true \
-Djava.io.tmpdir=$TMP_DIR \
-Dopenam.agents.bootstrap.dir=/home/tomcat/tomcat_v6_agent/Agent_001/config \
-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses \
-outfile $CATALINA_HOME/logs/catalina.out \
-errfile $CATALINA_HOME/logs/catalina.err \
$CATALINA_OPTS \
-cp $CLASSPATH \
org.apache.catalina.startup.Bootstrap

conf/jaas.conf

josso {
  org.josso.tc55.agent.jaas.SSOGatewayLoginModule required debug=true;
};

Which is there for legacy support only and will be phased out. I'm not sure it even loads since it is built for tomcat 5.5...

Within the code I am avoiding the problems of using IP addresses within the CN= by using the following HostnameVerifier().

HostnameVerifier hv = new HostnameVerifier()
{
    public boolean verify(String urlHostName, SSLSession session)
    {
        return true;
    }
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);

connection = (HttpURLConnection) servlet.openConnection();

------------ Update ---------------

This has been solved by a lengthy discussion with @Bruno, please use his original post and the long chat discussion that we had.

In the end I used the tools Keytool Explorer and XCA to make it easier for me to learn and execute.

D-Klotz
  • 1,973
  • 1
  • 15
  • 37
  • Can you tell us exactly what you already did as configuration to have this error? – Guy Bouallet Feb 18 '14 at 16:25
  • There are already many questions tagged [tomcat] in stackoverflow. Your question does not look out of topic. However, if people don't have immediate answer, it requires time to configure/reproduce all your environment to figure out what is going on. You can set a bounty (http://meta.stackexchange.com/questions/16065/how-does-the-bounty-system-work) to motivate poeple or see the issue directly with tomcat's community (http://tomcat.apache.org/findhelp.html). – Guy Bouallet Feb 19 '14 at 17:17
  • "If this question does not belong here, is there a more appropriate sister site" - configuration problems usually belong on [Super User](http://www.superuser.com) or [Server Fault](http://www.serverfault.com). – jww Feb 21 '14 at 18:22
  • @noloader Thanks. There is the question of the HostNameVerifier code. Perhaps that will let me stay here. – D-Klotz Feb 21 '14 at 19:19
  • For hostname verification, see RFC 6125, [Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)](http://tools.ietf.org/html/rfc6125). – jww Feb 21 '14 at 19:38
  • Update 2 states "How do I use my own CA to sign each cert". If that's no longer an issue, you might consider removing it. That's the sort on non-programming question that belongs on Super User or Server Fault. – jww Feb 21 '14 at 19:39
  • @noloader, it's good to point to RFC 6125, but [it excludes IP addresses from its scope](http://tools.ietf.org/html/rfc6125#section-1.7.2) (and we're talking about IP addresses here). – Bruno Feb 21 '14 at 19:40
  • OK, thanks Bruno. I'm not sure what to make of this post. Its kinda all over the place, and it seems to lack a clear issue. The updates make the issues a moving target. In all cases, the issues appear to be configuration related. – jww Feb 21 '14 at 19:46
  • @noloader, I agree the multiple edits don't necessarily make the question any clearer, on the contrary. A number of SSL-related questions rarely fall clearly into either SO/SF/SU categories, so I don't necessarily mind some of those here (although I understand some people may disagree). This one at least has a coding element (with the bad `HostnameVerifier`), and it's sometimes difficult to know how to configure the SSL settings without details on the library (some may need code). – Bruno Feb 21 '14 at 19:53
  • Sorry for the way this has been "all over the place", part of that is simply because I did not understand the problem itself. The title of the question has and is exactly what I need. How to get there, is why this post seems like a wandering journey. Once I get a solution (almost there), I plan on coming back to the question and doing a re-write. – D-Klotz Feb 21 '14 at 20:03

3 Answers3

2

Firstly, you're not actually making server-to-server connections: these are still client-to-server connections. It just happens that your client is a webapp running within a servlet container.

It matters because it's the client trust settings that are going to be used to establish the connections. Those are different from the trust settings on the Tomcat connector.

How your client webapp uses its trust settings depends on how it's implemented and on which library it uses. It seems fair to say that most clients would use the default JRE settings, unless there is specific code to do otherwise. Without any settings, the JRE's cacerts file would normally be used (see details in JSSE Reference guide, customisation section). You can also specify the usual javax.net.ssl.* properties (using -Djavax.net.ssl....=....); this would need to be done in the catalina launch script file (.bat or .sh depending on the platform) in JAVA_OPTS. This will affect the default values running within this JRE instance, so it might not be your preferred choice (you might want something more specific that affects only a given webapp, but you'd need to know how that webapp is implemented or which possible options it can use). The settings in your <Connector .../> will override this, though (and the truststore settings there don't matter unless you want to use client-certificate authentication too).

If you have a number of systems, it's probably not worth using self-signed certificates: each client would need to import all these self-signed certificates into its trust store. Instead you can create your own CA and import only that CA certificate. (In a local deployment, a single top-level/root CA should be sufficient, without the need for intermediate CAs.) OpenSSL's CA.pl is a good script to build a small CA, but you may find other tools such as XCA or TinyCA more convenient. (Note that you can generally use .p12 files directly in Java, using the PKCS12 keystore type, instead of the default JKS. For PKCS#12 stores, the keystore password and the key/keymanager password are the same.)

Since you're using IP addresses instead of names, you should also be aware that Java follows RFC 2818 on IP address quite stricly (unlike many browsers). In particular, an IP address in the CN of your Subject DN will not work: it needs to be in a Subject Alternative Name extension (of type IPAddress, not DNSname).


EDIT:

First, get rid of that hostname verifier: it introduces a vulnerability to MITM attacks for all the HttpsURLConnection that will use that default verifier, including possible connections to external servers. You can fix this by using an IP address the SAN.

Here are the steps you need to follow:

  • Create your CA (e.g. with XCA). Export its certificate (not its private key) to mycacert.pem.

  • If your application is likely to make HTTPS connections to other services, copy the default cacerts file into a new file mytruststore.jks (if your application isn't making any other connection, don't copy the file, keytool will create it).

  • Import mycacert.pem into mytruststore.jks with keytool:

    keytool -import -keystore mytruststore.jks -alias my_alias_name -cert mycacert.pem
    
  • You now have a truststore that you can install in all your clients that will need to talk to these servers. (You might need to perform these steps again if the cacerts file gets updated with the JRE.)

    • Configure that truststore in your clients. Here, this is done in your Catalina scripts in JAVA_OPTS: -Djavax.net.ssl.trustStore=... (and password). Besides the fact these JVM options are in the Catalina launch strict, they're the same as for other types of client.
    • Get rid of the -Djavax.net.ssl.keyStore=... there, this would only be used for client-certificate authentication, and it's not always a good idea to let the webapps use the certificate of the server within which they're running for client authentication.
  • Create a certificate with that CA for each server you want to use. Make sure you're using an IP-Address SAN if you want to use IP addresses. (Since you're in control of everything, XCA should allow you to generate the private key and certificate in one step, no need for CSR here, but you could if you wanted to. It also has prepared profiles for web server extensions, as well as the option to add Subject Alternative Names of type IP address or other.)

  • Export the combination of certificate and private key into a PKCS#12 (e.g. server1.p12) for each server. Each server should have its own, don't share them. You'll be able to use this as the "keystore keystore" in each server configuration.

    • Configure these keystores in each server in the connector configuration (keystoreType="PKCS12", and keyPass and keystorePass should be identical when using PKCS#12 stores).
    • You don't really need any truststore settings in your connector configuration unless you're using client-certificate authentication.

(It might be worth using client-certificate authentication to make sure the connections to your server at least only come from one other entity coming from that same CA, short of anything more precise, but the truststore you configure on the connector would need to contain only your CA certificate, not the others, so create a brand new truststore for that.)

Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • I've updated with more information. Your statement about IP address and RFC 2818 triggered my memory. I found in our low level class that makes the connection, that we overload the hostname verifier class to always return true. So perhaps that's how we have always bypassed that requirement. Not a great practice I'm sure... – D-Klotz Feb 21 '14 at 04:09
  • The reason for picking "server to server" communication is so people would stop thinking in terms of a web browser client. I agree with you that the client here is a HttpURLConnection request being made from within the master tomcat's servlet. – D-Klotz Feb 21 '14 at 04:16
  • By the way, are you trying to use client-certificate authentication for these connections, or do the client webapps perform other type of authentication (if required) independently of SSL/TLS? – Bruno Feb 21 '14 at 16:15
  • The tomee+ that is exposed to the outside world uses OpenAM SSO, while all of the Tomee+ servers hidden behind each company's firewall, use just https (for now). IF we could figure out how to do client certificate authorization, we might do that, except, as far as I can determine, every time we add a new server, we would need to add a certificate to the Head Tomee+. That isn't something we wish to do if we can help it. If it is the only choice, then perhaps. For now, we are just trying to get plain https communications with our own CA to work. – D-Klotz Feb 21 '14 at 16:28
  • I meant, how do the backend servers know the connections from the head server to the backend servers actually come from the head server? – Bruno Feb 21 '14 at 16:33
  • They have no idea. The head server simply makes a httpUrlConnection (as outlined in update #4) to a specific slave tomcat. Which server is ultimately determined by the user. I should backup. We used to have this working with tomcat5.5, when we moved to tomee+, we broke something. The only thing common in the working 5.5 version of this was a truststore entry. I'm looking at one of the working ones now. RSA 2048, MD5 with RSA, Fingerprint SHA-1. CN=CompanyName. That entry is identical in ALL servers. – D-Klotz Feb 21 '14 at 16:39
  • The slave nodes need to have their keystore configured in their connector configurations, but not in `JAVA_OPTS` (you'd need it there if you wanted them to be using client-certificates, as clients). The CN is ignored when there's a SAN for the IP address (but you can set it to the IP address if you want, various tools use the CN to identify the cert in a list). – Bruno Feb 21 '14 at 19:39
  • When I export the certificate for one of the slaves into PKCS#12 format, do I pick the option "with Certificate Chain"? So inside of "X Certificate" i'm looking at a tree, with the top node being the CA and the first leaf being my "10.93.101.33" cert. I'm right clicking on this leaf and exporting. Right? – D-Klotz Feb 21 '14 at 20:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/48105/discussion-between-d-klotz-and-bruno) – D-Klotz Feb 21 '14 at 20:30
0

To answer your question, I don't think that you need a chain of certificates. I already used tomcat with a self signed server certificate and connected to it with another self signed client certificate. I don't know about tomEE but I guess it would be the same.

To help you figure out what is goining on, I think about these possible debugging actions:

  • before going into automation, try to make things work without scripting. Generating the right configuration will than be easier.

  • make sure the script is running correctly: check if tje contents of the stores are as expected and make sure the server.xml files point to the right ones.

  • Try to test the servers separately using a browser client authentication if you can.

Above all, make sure you really need https between your servers. If you are in a protected realm, you are probably over using security and just slowing your application.

Guy Bouallet
  • 2,099
  • 11
  • 16
  • When you connected with another selfsigned certificate, was it from another server? In a browser, you can just say, "yes, please trust / import this cert". When two servers are trying to communicate, if they don't already trust the cert, you get the error I'm seeing. Isn't that the case? – D-Klotz Feb 18 '14 at 19:01
  • You make a good point about asking ourselves why do we really need https. In our case, these are behind protected firewalls, but the company's we install these servers within, some of them are very paranoid... – D-Klotz Feb 18 '14 at 19:03
  • @GuyBouallet The overhead to using SSL is typically pretty low unless you are running a high volume of requests. Why undersell the need for security on this just because and app is 'internal'? Security in depth which makes use of low hanging fruit like enabling SSL is always positive unless there is a business case NOT to do it. – Matthew Feb 20 '14 at 20:10
  • @Matthew We agree on the fact that SSL is probably not having too mush impact. but only Profiling the application determines it. However, using SSL should answer a risk evaluation and prior analysis of threats. If we are assuming that we are in a trustworthy environment, we may be contradicting ourselves by adding layers of unnecessary security. If environment is hostile, then we should define a threat model, attacks and counter-mesures and SSL is probably not enough. SSL should not be internally a default choice, otherwise, why don't we lock the doors whenever we enter or exit a room at home? – Guy Bouallet Feb 20 '14 at 21:44
  • I would concede any of these points if it wasn't almost no effort to implement (built into the software he is using) and has no impact on the user experience (once on it just works). This is more of a case of turning off a safety feature that impacts nobody just because your pretty sure it's not needed. I am with you on the requirement for proper risk assessment and cost benefit decision on handling threats in general, but this is as close to zero-effort and zero impact on the business as you get. If any of the data might ever be sensitive, protection from internal actors would warrant it. – Matthew Feb 20 '14 at 21:53
0

How do I create self signed certificates that two tomcats will be happy with when using https between the tomcats?

Tomcat must allow you to do one of two things. First is run a private PKI, or second is utilize a pinset.


The easier answer first: a pinset is a set of trusted certificates or public keys to identify a host or set of hosts. It's certificate or public key pinning with a multiplier. In this case, you want to take the 3 or 4 certificates and apply them to each server. Then, when peer's communicate, they will implicitly trust the peer's certificate.

It does not appear Tomcat (or IIS, or Apache) allow you to specify a pinset. That is, you cannot specify a collection of self-signed certificates to trust. So you'll have to use a private PKI.


The harder answer is to run a private PKI. There's nothing difficult about running a private PKI. The difficulty is in implementing it because there's a lot to it. There's no silver bullet to make it work because you need the private PKI and you need to configure your servers to use it.


For the question, "How do I use my own CA to sign each cert", see for example, How To Setup a CA or even Signing a certificate with my CA and Create my CA how to make a trusted self certified....


I can't help you with the configuration of Tomcat. But here are the steps to do it programmatically in OpenSSL if you were building a client and server. Here, "client" and "server" means the role the Tomcat server is taking (each takes both roles):

  • Client

    • call SSL_CTX_load_verify_locations with your CA
    • call SSL_CTX_set_default_verify_paths on the context
    • call SSL_CTX_set_verify with SSL_VERIFY_PEER
  • Server

    • call SSL_CTX_use_certificate_chain_file
    • call SSL_CTX_use_PrivateKey_file
    • call SSL_CTX_set_verify with SSL_VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT

When acting as a client, the Tomcat server needs to know the organization's trusted root. That's what SSL_CTX_load_verify_locations and SSL_CTX_set_default_verify_paths accomplish.

When acting as a client, SSL_CTX_set_verify ensures the Tomcat server verifies the peer's identify (modulo the lack of hostname checking in OpenSSL).

When acting as a server, the Tomcat server needs to know the certificate to send and the private key to sign with. That's what SSL_CTX_use_certificate_chain_file and SSL_CTX_use_PrivateKey_file do.

When acting as a server, the the Tomcat server wants mutual authentication. That's what SSL_CTX_set_verify with SSL_VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT does.

I don't know how to pound these into an Tomcat configuration, though.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • ah you describe what my week has felt like with that simple phrase "... pound these into an Tomcat...". Thank you for the answer. The private PKI discussion is not something I follow, but I don't think I need to. The discussion regarding using the openssl api is interesting. The flow certainly helps solidify my understanding of events. Thanks. – D-Klotz Feb 21 '14 at 22:01
  • Java applications using the JSSE (including Tomcat and webapps running within it) use the truststore to store the certificates they trust. These can be specific end-entity certificates or CA certificates, so you can use a specific collection of certificates if you wish. I also think it's not much harder to maintain your own PKI/CA than to deal with 3+ self-signed certs explicitly. (Also pointing out that Tomcat here is merely the container of the client, which use Java's JSSE API, so OpenSSL functions won't help much.) – Bruno Feb 21 '14 at 23:12