Keytool create a trusted self signed certificate

56,953

Solution 1

You would need to "establish trust" between your server and client (I'm assuming you only need to do server-side authentication). This is because you use self-signed certs. That involves importing your server's cert into the client trust store:

On the server side:

keytool -keystore <keystore file> -alias <alias> -export -file <certfilename>.cert

Copy the .cert file over to the client side and then:

keytool -keystore <truststore file> -alias <alias> -import -file <certfilename>.cert

Solution 2

You can't share the keystore between client and server, because the keystore contains the private key. When authenticating, the client skips the certificates with private keys. As said above you need to deploy a truststore on client side.

The certificates in a keystore don't behave the same way, depending on how you generated or imported them.

An imported certificate's entry type (seen when verbosely listing the whole keystore with -list -v) is "trustedCertEntry". A generated certificate's entry type is "PrivateKeyEntry". When you export a certificate, you only export its public key, and an optional reference to its issuer.

Seems like you need to export the self-signed certificate in your keystore as a trusted certificate in your truststore (names make sense here).

I wouldn't do that, because SSL/TLS implementations probably don't support it. From a real world perspective it's like deploying the ultimately secret private key from Verisign on some obscure Web server to sign casual pages, while the only purpose of this private key is to remain in a safe and sign other certificates. SSL/TLS implementors probably won't pollute their code with such a use case, and anyways, the "KeyUsage" certificate extension may restrict a certificate usage to signing, preventing encipherment.

That's why I suggest to rebuild a chain of certificates for your test.

The keytool documentation contains an interesting part about creating a chain (-gencert command) but it's a very skeletal example which doesn't cover the keystore-truststore relationship. I've enhanced it to simulate a third-party certification authority.

A temporary store their-keystore.jks represents a certificate-emitting authority. I feed it with a certificate chain of ca2 -> ca1 -> ca with ca being considered as a root certificate. The chain appears with each non-root certificate (namely ca1 and ca2) referencing their issuer as Certificate[2]. Please note that every certificate is "PrivateKeyEntry".

Then I feed the my-keystore.jks with those certificates in order: ca, ca1, ca2. I import ca with the -trustcacerts option which means it becomes a root certificate. In my-keystore.jks each imported certificate now is "trustedCertEntry" which means there is only the public key. The issuing relationship only appears in the "Issuer" field but it's OK because the trust relationship mattered most at the time of the import.

At this point my-keystore.jks simulates an environment containing some trusted certificates, like a fresh JRE. The their-keystore.jks simulates the owners of those certificates, who have the power to sign certificate requests.

So do I : I create a self-signed certificate e1 in my-keystore.jks, get it signed by ca2 (through their-keystore.jks) and import the signed result back into my-keystore.jks. e1 is still a "PrivateKeyEntry" (because its private key remains in my-keystore.jks) but now I've built the following chain : e1 -> ca2 -> ca1. It seems that ca1 -> ca is implicit with ca being a certification authority.

To build the truststore I just import certificates ca, ca1 and ca2 the same way I did for my-keystore.jks. Please note I don't import e1, as I expect the SSL/TLS client to validate it against ca2.

I think this gets rather close of how things work in real world. What's nice here is you have full control on the certificates, and no dependency on JRE's cacerts.

Here is the code putting what I say in practice. Seems to work with Jetty (client and server) as long as you disable certificate revocation list (a topic left for another day).

#!/bin/bash

rm  their-keystore.jks 2> /dev/null
rm  my-keystore.jks    2> /dev/null
rm  my-truststore.jks  2> /dev/null

echo "===================================================="
echo "Creating fake third-party chain ca2 -> ca1 -> ca ..."
echo "===================================================="

keytool -genkeypair -alias ca  -dname cn=ca                           \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -ext BasicConstraints:critical=ca:true,pathlen:10000                \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca1 -dname cn=ca1                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -genkeypair -alias ca2 -dname cn=ca2                          \
  -validity 10000 -keyalg RSA -keysize 2048                           \
  -keystore their-keystore.jks -keypass Keypass -storepass Storepass


  keytool -certreq -alias ca1                                            \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -gencert -alias ca                                             \
    -ext KeyUsage:critical=keyCertSign                                   \
    -ext SubjectAlternativeName=dns:ca1                                  \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass   \
| keytool -importcert -alias ca1                                         \
    -keystore   their-keystore.jks -keypass Keypass -storepass Storepass

#echo "Debug exit" ; exit 0

  keytool -certreq -alias ca2                                           \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -gencert -alias ca1                                           \
    -ext KeyUsage:critical=keyCertSign                                  \
    -ext SubjectAlternativeName=dns:ca2                                 \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v -storepass Storepass -keystore their-keystore.jks


echo  "===================================================================="
echo  "Fake third-party chain generated. Now generating my-keystore.jks ..."
echo  "===================================================================="
read -p "Press a key to continue."

# Import authority's certificate chain

  keytool -exportcert -alias ca                                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -trustcacerts -noprompt -alias ca                 \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca1                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                        \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -noprompt -alias ca2                              \
    -keystore  my-keystore.jks -keypass Keypass -storepass Storepass

# Create our own certificate, the authority signs it.

keytool -genkeypair -alias e1  -dname cn=e1                        \
  -validity 10000 -keyalg RSA -keysize 2048                        \
  -keystore my-keystore.jks -keypass Keypass -storepass Storepass

  keytool -certreq -alias e1                                            \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass     \
| keytool -gencert -alias ca2                                           \
    -ext SubjectAlternativeName=dns:localhost                           \
    -ext KeyUsage:critical=keyEncipherment,digitalSignature             \
    -ext ExtendedKeyUsage=serverAuth,clientAuth                         \
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass  \
| keytool -importcert -alias e1                                         \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-keystore.jks

echo "================================================="
echo "Keystore generated. Now generating truststore ..."
echo "================================================="
read -p "Press a key to continue."

  keytool -exportcert -alias ca                                        \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -trustcacerts -noprompt -alias ca                \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca1                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca1                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

  keytool -exportcert -alias ca2                                       \
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass    \
| keytool -importcert -noprompt -alias ca2                             \
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass

keytool -list -v  -storepass Storepass -keystore  my-truststore.jks

rm  their-keystore.jks 2> /dev/null

Solution 3

You mustn't do that. A keystore is strictly private. If you leak it to anybody you have fatally compromised security. There is no point in doing this kind of thing just to get it working, because it isn't working - it is just a security breach. You have to do it right: export from the server's keystore into the client's truststore, and from the client's keystore if any to the server's keystore.

Share:
56,953
sixtyfootersdude
Author by

sixtyfootersdude

Updated on September 24, 2020

Comments

  • sixtyfootersdude
    sixtyfootersdude over 3 years

    I am trying to use the (java) keytool to create a self signed certificate but when I attempt to use it I get the following exception (see bottom for entire exception).

    ...<5 more exceptions above this>
    Caused by: sun.security.validator.ValidatorException: No trusted certificate found
            at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
            at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
            at sun.security.validator.Validator.validate(Validator.java:203)
            at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
            at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
            at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
            ... 22 more
    

    I know that I can by-pass this with this code:

    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLSession;
    
    HostnameVerifier hv = new HostnameVerifier() {
        public boolean verify(String urlHostName, SSLSession session) {
            System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost());
            return true;
        }
    };
    
    HttpsURLConnection.setDefaultHostnameVerifier(hv);
    

    (source)

    But I am not interested in this solutions because I think that it creates a security hole. (please correct me if I am wrong).

    Can anyone point me in the right direction? I am testing locally at the moment right now so it is pretty easy to change things. I have access to the server code, client code and to the .keystore file.

    Update

    I was attempting to use one .keystore file for both the client and server but in hopes of simplifying my issues I have created server.keystore (see below) and client.truststore (see below). I am reasonably confident that the certicates are correct but if someone could verify I would be grateful.

    server.keystore

    hostname[username:/this/is/a/path][711]% keytool -list -keystore server.keystore -v
    Enter keystore password:
    
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 1 entry
    
    Alias name: hostname
    Creation date: Feb 4, 2010
    Entry type: PrivateKeyEntry
    Certificate chain length: 1
    Certificate[1]:
    Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
    Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
    Serial number: 4b6b0ea7
    Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
    Certificate fingerprints:
             MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
             SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
             Signature algorithm name: SHA1withDSA
             Version: 3
    
    
    *******************************************
    *******************************************
    

    client.truststore

    hostname[username:/this/is/a/path][713]% keytool -list -keystore client.truststore -v
    Enter keystore password:
    
    Keystore type: JKS
    Keystore provider: SUN
    
    Your keystore contains 1 entry
    
    Alias name: mykey
    Creation date: Feb 4, 2010
    Entry type: trustedCertEntry
    
    Owner: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
    Issuer: CN=hostname, OU=hostname, O=hostname, L=hostname, ST=hostname, C=hostname
    Serial number: 4b6b0ea7
    Valid from: Thu Feb 04 13:15:03 EST 2010 until: Wed May 05 14:15:03 EDT 2010
    Certificate fingerprints:
             MD5:  81:C0:3F:EC:AD:5B:7B:C4:DA:08:CC:D7:11:1F:1D:38
             SHA1: F1:78:AD:C8:D0:3A:4C:0C:9A:4F:89:C0:2A:2F:E2:E6:D5:13:96:40
             Signature algorithm name: SHA1withDSA
             Version: 3
    
    
    *******************************************
    *******************************************
    

    Update

    I thought it could be useful to include the entire exception:

    javax.xml.soap.SOAPException: java.io.IOException: Could not transmit message
            at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:115)
            at org.jboss.ws.core.soap.SOAPConnectionImpl.call(SOAPConnectionImpl.java:66)
            at com.alcatel.tpapps.common.utils.SOAPClient.execute(SOAPClient.java:193)
            at com.alcatel.tpapps.common.utils.SOAPClient.main(SOAPClient.java:280)
    Caused by: java.io.IOException: Could not transmit message
            at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:192)
            at org.jboss.ws.core.client.SOAPRemotingConnection.invoke(SOAPRemotingConnection.java:77)
            at org.jboss.ws.core.soap.SOAPConnectionImpl.callInternal(SOAPConnectionImpl.java:106)
            ... 3 more
    Caused by: org.jboss.remoting.CannotConnectException: Can not connect http client invoker. sun.security.validator.ValidatorException: No trusted certificate found.
            at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:368)
            at org.jboss.remoting.transport.http.HTTPClientInvoker.transport(HTTPClientInvoker.java:148)
            at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:141)
            at org.jboss.remoting.Client.invoke(Client.java:1858)
            at org.jboss.remoting.Client.invoke(Client.java:718)
            at org.jboss.ws.core.client.RemotingConnectionImpl.invoke(RemotingConnectionImpl.java:171)
            ... 5 more
    Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
            at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
            at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584)
            at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
            at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
            at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
            at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
            at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
            at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
            at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877)
            at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089)
            at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116)
            at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100)
            at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
            at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
            at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857)
            at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
            at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:288)
            ... 10 more
    Caused by: sun.security.validator.ValidatorException: No trusted certificate found
            at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
            at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
            at sun.security.validator.Validator.validate(Validator.java:203)
            at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
            at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
            at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
            ... 22 more