7

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
Community
  • 1
  • 1
sixtyfootersdude
  • 25,859
  • 43
  • 145
  • 213

5 Answers5

15

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
Zac Thompson
  • 12,401
  • 45
  • 57
Armadillo
  • 1,408
  • 11
  • 9
  • +1: Thanks for the clear answer. I am pretty sure that this what I am doing **Except** I am setting both the client and the server to use the same keystore file. Ie: server.keystore. I beleave that the keystore can act as both a keystore and truststore. I am using mine to be both the server's and the client's keystore and truststore. Is this legal? – sixtyfootersdude Feb 04 '10 at 16:50
  • 1
    I haven't tried that configuration myself but it *may* work. You would have to use different aliases for the key material and the cert as they serve different purposes (and both have to be there). It seems that you are aware that this setup is only good for testing purposes and not production... – Armadillo Feb 04 '10 at 17:05
  • Meaning that I would have to have one trustedCertEntry and one privateKeyEntry in my one .keystore file? If I continue to have problems I will try to create a separate .keystore file for the client. Thanks for your assistance. – sixtyfootersdude Feb 04 '10 at 17:35
  • Armadillo, Thanks for the feedback, I added an update in the question. I think it is better to move to a simpler configuration (one keystore for server, one truststore for client) as you suggested. However I am still getting the same exception. Maybe now that I have a clearer example (above) it will be easier to identify my problem. – sixtyfootersdude Feb 04 '10 at 18:41
  • 1
    Not sure this is the actual problem, but you have clientAuth="true" in your 8443 port connector. That would require trust to also be established in the other direction (client -> server). Your 443 port does not have that. Which is your requirement? – Armadillo Feb 04 '10 at 19:03
  • Hi Armadillo, I changed that to false but I still have the same exception. Thanks for the pointer – sixtyfootersdude Feb 04 '10 at 19:26
  • 1
    OK. So can you post your client code so that I can take a look? Also, can you point your browser at the same port? What do you get? (you should be able to query for the WSDL of your web service) – Armadillo Feb 04 '10 at 19:43
  • You question lead me to an interesting observation. My client was attempting to access: https://sco-up:443/common/ws/A2. But I was curious if that was the problem. I changed that address to https://sco-up:443/asdfasdhahdhadsfkjlasd (ie garbage). And I got the same exception. This seems very odd to me. Going to look into this. – sixtyfootersdude Feb 05 '10 at 14:19
  • In response to you question if I navigate to https://sco-up:443/common/ws/A2 in a browser I get plain text: "HTTP GET not supported". (Which I think seems logical). If I navigate to https://sco-up:443/asdfasdfkd/asdfads: I get some kind of html table with title: "HTTP Status 404 - /asdfasdfkd/asdfadsf". – sixtyfootersdude Feb 05 '10 at 14:22
  • How should I be able to query the WSDL for web service? Should I be able to do this even if it is in httpS ? Thanks and happy friday. – sixtyfootersdude Feb 05 '10 at 14:24
  • Actually, I was making a dumb mistake. I was not restarting my server properly so it was not rereading the keystores. Thanks for all the help have a great weekend. – sixtyfootersdude Feb 05 '10 at 21:44
10

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
Martín Schonaker
  • 7,273
  • 4
  • 32
  • 55
Laurent Caillette
  • 1,281
  • 2
  • 14
  • 20
  • This entire answer is based on the fallacy that self-signed certificates don't work and/or aren't supported and /or insecurely leak the private key. As none of this is true, the answer is pointless and doesn't address the question that was asked. – user207421 Feb 23 '17 at 19:16
  • Come on @EJP I'm using self-signed certificates in my scripts so I assume they do work. What I propose to sixtyfootersdude is to try to reproduce his problem with a well-understood trust chain. – Laurent Caillette Feb 23 '17 at 23:44
  • Come off it yourself. What you *said* in your fifth paragraph is that 'SSL/TLS implementations probably don't support it', and the subsequent remarks appear to imply that you think certificates contain private keys. – user207421 Feb 24 '17 at 04:19
  • No you misunderstood the point. When I write "it's like deploying the ultimately secret private key from Verisign on some obscure Web server to sign casual pages" there is no truststore involved. I clearly mention the 'KeyUsage' extension, which sets the difference between a Certification Authority and domain name authentication. This is all about certificate signing (on the "keystore side" if you prefer) and there is no truststore involved. [continued...] – Laurent Caillette Feb 24 '17 at 08:01
  • [...continuing] When running an HTTPS client in Java, it failed with a truststore importing only a self-signed certificate for domain name authentication. But it succeeded after importing the self-signed certificate signing the certificate for domain name authentication. – Laurent Caillette Feb 24 '17 at 08:01
  • Your fourth and fifth paragraphs say up there in black and white that 'SSL/TLS implementations probably don't support it', where 'it' is 'export[ing] the self-signed certificate in your keystore as a trusted certificate in your truststore'. MS SSL, JSSE and OpenSSL, the most commonly used implementations on the planet, all support exactly that. – user207421 Feb 24 '17 at 11:26
  • The paragraph you mention is a bit obscure and I might be wrong in my assumptions about SSL/TLS implementations (hence the conditional I used). But you are wrong to conclude that I believe that certificates contain private keys. You probably missed the 3rd paragraph where I wrote: "When you export a certificate, you only export its public key". – Laurent Caillette Feb 24 '17 at 15:33
  • This is one of the 10 best answers I have ever seen on this website. I learned more from this one answer than 100 other blog posts with the same jumble of commands. Your answer is different because it explains *why*. This answer was my _key_ (no pun intended!) to understanding the problem of creating a trusted certificate chain for localhost. I was also to implement a minimalist version of your commands -- a single CA cert, and a single signed cert for localhost. Then, I trusted the CA cert on my MS Windows (certmgr.msc). Restart Google Chrome. Like magic... it works! – kevinarpe Sep 27 '21 at 14:10
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.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

I don't get it. Are you using the server key store with the client? What is your use case exactly? Are you trying to setup mutual authentication?

If yes, you're on the wrong path here. You'll need a client key store (for the client's self-signed certificate and private key) and a client trust store (for the server's "stand-alone" self-signed certificate i.e. without its private key). Both are different from the server key store.

Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • I thought that I could use the server's keystore with the client for testing. This would ensure that the client has the servers certificate. Although maybe the client does need its own self signed certificate... – sixtyfootersdude Feb 04 '10 at 15:00
  • AS I said, it depends on what you want to do (which is not clear to me). If you want to implement mutual authentication, the client needs its certificate too. And in that case, I don't think that you can use the server key store as client trust store. – Pascal Thivent Feb 04 '10 at 15:42
-1
Springboot 2.1.5 , java 1.8, keytool(it is part of JDK 8) 

these are the steps to follow to generate the self signed ssl certifcate in spring boot


1. Generate self signed ssl certificate

keytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650

ex: D:\example> <here run the above command>

if it is not working then make sure that your java bin path is set at environment variables to the PATH variable as

C:\Program Files\Java\jdk1.8.0_191\bin

2. after key generation has done then copy that file in to the src/main/resources folder in your project

3. add key store properties in applicaiton.properties 

server.port: 8443
server.ssl.key-store:classpath:keystore.p12
server.ssl.key-store-password: test123     # change the pwd
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat

3. change your postman ssl verification settings to turn OFF
    go to settings and select the  ssl verification  to turn off

now verify the url ( your applicaiton url)
https://localhost:8443/test/hello