Client certificate missing: How to recover it - java

I am on a team that is are trying to connect a Java J2SE application to a secure corporate web services site using certificates. None of the team members have prior experience creating certificates and coding for this type of connection.
We prepared and submitted a CSR and received a .p7b certificate from the web service site. The .p7b contains two certificates: one for us issued by the web services site and a second for the web services site issued by the corporate owner’s own CA. Both appear in the keystore list below. The keystore is referenced in a custom SSLContext and the Java code creates an Apache HttpClient with that SSLContext without error.
When the Java code attempts to execute an HttpGet, the web service site rejects the SSLHandshake and terminates the connection with the error:
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
In the handshake debug trace, in step 13 of the handshake, is the message:
ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
Certificate chain
Empty
Keytool lists the contensts of the keystore as:
(corporate identities redacted)
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: server
Creation date: Mar 28, 2019
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: ...
Issuer: ...
Serial number: ...
Valid from: Mon Mar 11 19:00:00 CDT 2019 until: Wed Mar 11 18:59:59 CDT 2020
Certificate fingerprints:
SHA1: ...
SHA256: ...
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.16.840.1.113733.1.6.9 Criticality=false
0000: 01 01 FF ...
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:false
PathLen: undefined
]
#3: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: http://onsite-crl.pki.digicert.com/ATTServicesIncApplicationCertificates/LatestCRL.crl]
]]
#4: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
]
#5: ObjectId: 2.16.840.1.113730.1.1 Criticality=false
NetscapeCertType [
SSL client
]
Certificate[2]:
Owner: ...
Issuer: ...
Serial number: ...
Valid from: Wed Feb 23 18:00:00 CST 2011 until: Tue Feb 23 17:59:59 CST 2021
Certificate fingerprints:
SHA1: ...
SHA256: ...
Signature algorithm name: SHA1withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 97 20 99 C2 73 2A 45 EB E0 02 7F 47 DA 7B AB 7C . ..s*E....G....
0010: EB 1F AF 6E ...n
]
]
#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:0
]
#3: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: http://onsitecrl.verisign.com/offlineca/ATTServicesIncATTServicesIncRootCA.crl]
]]
#4: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
Key_CertSign
Crl_Sign
]
#5: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
CN=VeriSignMPKI-2-51
]
#6: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 25 64 37 69 DB DC 57 99 43 80 79 29 90 6C B8 13 %d7i..W.C.y).l..
0010: 58 EE B6 D0 X...
]
]
*******************************************
*******************************************
]
}
It appears that the keystore includes an entry for our private key but not a client certificate; only the two certificates from the .p7b from the web services site are listed.
The StackOverflow post at:
why doesn't java send the client certificate during SSL handshake?
suggests exporting the client certificate from the keystore and editing the certificate chain. This does not relieve the problem, apparently because the keystore contains our private key but not our signed certificate.
I do not have an artifact that identifies as the client certificate. The Oracle procedure given at:
https://docs.oracle.com/cd/E19509-01/820-3503/ggezu/index.html
suggests the signed client certificate should have been created in the process of preparing the original CSR (step 3 of the procedure) , however we do not appear to have that certificate file as a separate artifact.
I think we need to reconstruct the client certificate and import it into the proper location of the certificate chain in the keystore. Since we do not appear to have the original file created when the CSR was created, can the client certificate be re-created now from scratch (step 3 - 5 of the Oracle procedure) and edited back into the chain? Is there a way to extract or reconstitute the client certificate from the original CSR?
Any questions, insights or suggestions very much appreciated. Thanks.

It appears that the keystore includes an entry for our private key but not a client certificate; only the two certificates from the .p7b from the web services site are listed.
The PrivateKeyEntry you show listed by keytool does contain a client certificate (by BC, KU and NCT), and a CA certificate that presumably is the issuing (parent) cert for that client cert. (If you imported these certs to this keystore with keytool, the CA cert is definitely the issuer because keytool verifies that; if you used another tool it should enforce the same requirement, but might not.) You describe the p7b as containing "second for the web services site issued by the corporate owner’s own CA" but (1) if that were the case keytool would not have imported it as part of the same chain and (2) it would make no sense, because the client does not need a CA-issued (i.e. not self-signed) server cert in its keystore or truststore, only the cert of the server's CA in its truststore, and that as a separate entry not in the client's PrivateKeyEntry even if it's a shared file.
Per your description you are getting a javax.net.debug=ssl trace, so look at the part of that trace where it loads the keystore and make sure this entry is getting loaded, and look at the server's *** CertificateRequest under Cert Authorities to see what CA(s) it is asking for (immediately before *** ServerHelloDone) and compare that(those) to the actual CA for the chain in your keystore (unredacted). Since you are using Apache HttpClient, if you are using one of the overloads that specifies a PrivateKeyStrategy make sure it selects the alias correctly.

Related

REST client call fails with "SunCertPathBuilderException: unable to find valid certification path to requested target"

I am trying to test 2 applications locally. One is a REST API server, the other is a REST client. The server runs on Open Liberty in eclipse. The client runs as a JUnit test in eclipse.
I have installed our company certificate in the truststores of the eclipse JRE and the server configuration. The unit test is configured to use the JRE which has the certificate installed and I also set the truststore location explicitly in the run configuration:
-Djavax.net.ssl.keyStore="C:\Program Files\Java\jre\lib\security\cacerts"
-Djavax.net.ssl.keyStorePassword=changeit
-Djavax.net.ssl.trustStore="C:\Program Files\Java\jre\lib\security\cacerts"
-Djavax.net.ssl.trustStorePassword=changeit
-Djavax.net.debug=all
The error message contains:
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:450)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:317)
... 51 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
... 57 more
I enabled SSL debugging and saw
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=localhost, OU=OpenLibertyServer, O=ibm, C=us
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 2048 bits
params: null
modulus: <removed.....>
public exponent: 65537
Validity: [From: Thu Mar 19 15:08:27 CET 2020,
To: Fri Mar 19 15:08:27 CET 2021]
Issuer: CN=localhost, OU=OpenLibertyServer, O=ibm, C=us
SerialNumber: [ 21...29]
Certificate Extensions: 2
[1]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
DNSName: localhost
]
[2]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: ...
]
The debugging also lists all of the certificates which are in the truststore, including the one I installed.
What might be wrong or missing here?
UPDATE
I set the truststore for the client to be the exact same truststore that the server uses and the problem is 'solved'. This is naturally not realistic solution and is probably an indication that a certificate is actually missing. At least I can continue with testing...
I'm not sure what's the cause of your problem but in my experience it's easier to import the trusted root certificate in the Windows store (it might already be pushed there by your company) and to add this JVM option:
-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT
"C:\Program Files\Java\jre\lib\security\cacerts" is the default java truststore, you need to add certificat chain in the java client cacerts.
Export the certificat chain in CER format with a tool or with Windows and add it in your default java truststore like this:
keytool -import -trustcacerts -keystore cacerts -storepass changeit -noprompt -alias yourCertAlias -file path\to\yourCertificatChain.cer
Alternatively if you want to switch of JRE easily you can put certificat chain in an external truststore and call it with :
-Djavax.net.ssl.trustStore=<truststore path> -javax.net.ssl.trustStorePassword=<truststorepassword>

java.security.cert.CertPathValidatorException: Certificate chaining error

I have the following application error when accessing https://..appRoot../TestJavaMongo/test/mongo/all by following the doc: https://medium.com/#blumareks/mongodb-for-you-a-hardcore-java-cloud-developer-f67b909834f4
I'm testing to connect the Mongo DB service from the Liberty Java CF app on IBM Cloud. How to resolve the error?
``` Error 500: com.mongodb.MongoTimeoutException: Timed out after
30000 ms while waiting for a server that matches
WritableServerSelector. Client view of cluster state is {type=UNKNOWN,
servers=[{address=sl-us-south-1-portal.16.dblayer.com:50233,
type=UNKNOWN, state=CONNECTING,
exception={com.mongodb.MongoSocketWriteException: Exception sending
message},
caused by {javax.net.ssl.SSLHandshakeException: com.ibm.jsse2.util.h:
PKIX path building failed:
java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl
could not build a valid CertPath.; internal cause is:
java.security.cert.CertPathValidatorException: The certificate issued
by CN=********* is not trusted; internal cause is:
java.security.cert.CertPathValidatorException: Certificate chaining
error}, ...
$ keytool -list -v -keystore mongoKeyStore
Enter keystore password:
Keystore type: JKS Keystore provider: SUN
Your keystore contains 1 entry
Alias name: mykey Creation date: 24/06/2017 Entry type:
trustedCertEntry
Owner: CN=mwsadows#us.ibm.com-0b24b0ff06b390e0cc5e803aecd5ec82 Issuer:
CN=mwsadows#us.ibm.com-0b24b0ff06b390e0cc5e803aecd5ec82 Serial number:
59491e1b Valid from: Tue Jun 20 23:07:39 AEST 2017 until: Sat Jun 20
23:00:00 AEST 2037 Certificate fingerprints: MD5:
A4:54:21:6A:52:E1:8B:CB:07:CC:25:A3:3B:1A:8B:05 SHA1:
BE:5D:AE:94:C3:A5:37:2D:43:B2:E7:FC:CF:39:19:EE:B8:10:29:9B SHA256:
D5:6B:EB:D6:88:36:D4:77:06:9B:8D:2B:83:39:9B:95:A5:E3:22:09:99:EF:32:89:31:E2:88:C2:86:58:83:62
Signature algorithm name: SHA512withRSA Version: 3
Extensions:
1: ObjectId: 2.5.29.35 Criticality=false AuthorityKeyIdentifier [ KeyIdentifier [ 0000: 65 2C 47 37 D6 4C B7 24 E9 A1 AA 14 01 4A 12
AD e,G7.L.$.....J.. 0010: 63 E0 7C 56
c..V ] ]
2: ObjectId: 2.5.29.19 Criticality=false BasicConstraints:[ CA:true PathLen:2147483647 ]
3: ObjectId: 2.5.29.37 Criticality=false ExtendedKeyUsages [ serverAuth clientAuth ]
4: ObjectId: 2.5.29.15 Criticality=true KeyUsage [ Key_CertSign ]
5: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 65 2C 47 37 D6 4C B7 24 E9 A1 AA 14 01 4A 12
AD e,G7.L.$.....J.. 0010: 63 E0 7C 56
c..V ] ]
I recently had this same issue with our db provider, they sent me their .crt file and I had to create a keystore with it.
keytool -import -alias "name" -file "/path/to/file" -keystore "/path/to/keystore/file" -storetype pkcs12 -storepass "keystorepass"
then at my main class I defined truststore and truststore pass:
System.setProperty("javax.net.ssl.trustStore", "/path/to/keystore/file");
System.setProperty("javax.net.ssl.trustStorePassword", "keystorepass");
Hope it helps.
As the error says, the certificate isn't trusted. I see that tutorial has a pretty substantial section on importing the certificate from the Mongo service into a keystore and setting it as the trustStore for Liberty. Double check those steps to make sure that you:
Correctly obtained and saved the certificate from the Mongo service, ie that it is not incomplete or corrupted or anything.
Check that you created a keystore using the keytool command in the correct location, and that you replaced the one from the repository if you checked that out, and that it includes the certificate you saved for the Mongo server. You can use keytool -list -v -keystore <path_to_keystore.jks> on your keystore to see what's inside.
Check that you configured Liberty correctly to use this keystore as your trust store. I think the tutorial is a little misleading, as it says
A typical application will also need to set several JVM system
properties to ensure that the client presents an TLS/SSL certificate
to the MongoDB server: javax.net.ssl.keyStore The path to a key store
containing the client’s TLS/SSL certificates
javax.net.ssl.keyStorePassword The password to access this key store
However, I think you actually need to set this keystore you created as the value for the javax.net.ssl.trustStore property (and set the trustStorePassword property accordingly) because the JVM uses the trustStore to check the public keys of hosts it is connecting to for trust.
Either way, make sure those properties are set appropriately and that Liberty is not throwing up any errors in the logs related to it on server start.
Once you have verified the whole chain (that you have exported the correct certificate, that you have created a keystore that contains that certificate, and that you have set that keystore as the truststore for the server,) you should have security set up to talk to the Mongo server correctly. If you have changed the existing truststore while the server is running you have to restart the server for it to pick up those changes.

keytool Java and TLS/SSL

I have a socket client and server socket. The server has a keystore with a key in it.
keytool -genkey -alias mystuff -keyalg RSA -keystore keystore.jks -keysize 2048
This page says that it generates a key pair when the above command is run. https://www.sslshopper.com/article-most-common-java-keytool-keystore-commands.html
When I run this command:
keytool -list -v -keystore keystore.jks
I can see:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: mystuff
Creation date: 25-Jun-2015
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Richard, OU=OSS, O=OSS, L=Yat, ST=Hamp, C=GB
Issuer: CN=Richard, OU=OSS, O=OSS, L=Yat, ST=Hamp, C=GB
Serial number: 48ee5103
Valid from: Thu Jun 25 17:09:18 BST 2015 until: Sun Jun 19 17:09:18 BST 2016
Certificate fingerprints:
MD5: 60:63:F2:41:A3:AB:DB:E0:63:F9:B0:E4:C8:2C:90:D4
SHA1: 52:8A:F7:76:82:B7:E9:BE:D3:4E:4A:3C:DD:CF:8A:58:A6:9F:70:DE
SHA256: 3C:80:C1:0E:E7:30:DD:69:9F:97:A9:02:F9:4E:6E:57:84:82:C5:22:0E:7F:7A:EE:C1:D2:7A:8A:45:A3:86:79
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 42 4F 07 15 3A 9A 8C 59 6A 65 EB B6 62 FD 77 C5 BO..:..Yje..b.w.
0010: AC E3 B2 4F ...O
]
]
What I am seeing there is the certificate but what does the entrytype: PrivateKeyEntry mean ?
So ... when I load the Java Server:
java -Djavax.net.ssl.keyStore=keystore.jks -Djavax.net.ssl.keyStorePassword=somepassword EchoServer
I can point it to the keystore so it can be used in setting up the SSLServerSocket.
Now on the client side does the client just need the exported crt from that key store using this command ?
keytool -export -alias mystuff -file mystuff.crt -keystore keystore.jks
Or is something else required to enable secure SSL between the Java Sockets.
Additional Information
Standard Java Notes
Running server and client using SSL
http://stilius.net/java/java_ssl.php
First copy certificate file that you created before into working directory and run server with these parameters (notice that you have to change keyStore name and/or trustStrorePassword if you specified different options creating certificate:
java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 EchoServer
And now again copy certificate file that you created before into working directory and run client with these parameters (notice that you have to change keyStore name and/or trustStrorePassword if you specified different options creating certificate:
java -Djavax.net.ssl.trustStore=mySrvKeystore -Djavax.net.ssl.trustStorePassword=123456 EchoClient
If you want SSL debug information just add these parameters when running server and/or client:
-Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl
javax.net.ssl.keyStore- Location of the Java keystore file containing an application process's own certificate and private key. On Windows, the specified pathname must use forward slashes, /, in place of backslashes.
javax.net.ssl.keyStorePassword - Password to access the private key from the keystore file specified by javax.net.ssl.keyStore. This password is used twice: To unlock the keystore file (store password), and To decrypt the private key stored in the keystore (key password).
javax.net.ssl.trustStore - Location of the Java keystore file containing the collection of CA certificates trusted by this application process (trust store). On Windows, the specified pathname must use forward slashes, /, in place of backslashes, .
If a trust store location is not specified using this property, the SunJSSE implementation searches for and uses a keystore file in the following locations (in order):
$JAVA_HOME/lib/security/jssecacerts
$JAVA_HOME/lib/security/cacerts
javax.net.ssl.trustStorePassword - Password to unlock the keystore file (store password) specified by javax.net.ssl.trustStore.
javax.net.ssl.trustStoreType - (Optional) For Java keystore file format, this property has the value jks (or JKS). You do not normally specify this property, because its default value is already jks.
javax.net.debug - To switch on logging for the SSL/TLS layer, set this property to ssl.
Richard

Android Hash Key - it really should be simpler than this

I'm trying to create a hash key for Facebook for an app.
I'm on 64 bit Windows Vista.
I have installed Java JDK and OpenSSL (versions openssl-0.9.8e X64 & openssl-0.9.8k X64) and am trying the following:
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug.keystore | "C:\Program Files\Java\jdk1.7.0_51\bin\openssl.exe" sha1 -binary | "C:\Program Files\Java\jdk1.7.0_51\bin\openssl.exe" base64
And what I get is this:
Is there a better way to do this? I am novice at this I suppose but not in general, this is a minefield
I dont know the better way but I followed these steps to get Keyhash
C:\Program Files (x86)\Java\jre7\bin>keytool -list -v -keystore C:\Users\VivekWa
rde.android\debug.keystore
Enter keystore password:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: androiddebugkey
Creation date: Aug 12, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 6fccf69b
Valid from: Mon Aug 12 18:15:22 IST 2013 until: Wed Aug 05 18:15:22 IST 2043
Certificate fingerprints:
MD5: 12:5E:CB:88:01:1D:FB:94:36:84:8B:50:F5:5B:61:DC
SHA1: 5A:60:85:BE:AA:29:3C:18:D5:29:4A:C6:C4:23:DE:89:A6:EA:C2:C8
SHA256: 60:1C:6F:A0:E7:BD:57:F8:02:38:6F:A5:77:7A:57:AC:74:9A:42:3B:6A:
A1:3A:AF:27:38:F1:57:EF:BD:BC:CC
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
//#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 65 5C 20 02 31 12 B8 0B 62 10 77 99 DB C7 D3 76 e\ .1...b.w....v
0010: A5 40 1C 40 .#.#
]
]
C:\Program Files (x86)\Java\jre7\bin>keytool -exportcert -alias androiddebugkey
-keystore "C:\Users\VivekWarde.android" | "C:\OpenSSL\bin\openssl" sha1 -binary
|"C:\OpenSSL\bin\openssl" base64
A5EP+MbXYyEPDcmKBGmZxVJd0oo=

How to add CA Certificate which contain invalid URI name " file://\\ " into keystore file

I generated keystore file and csr file using keytool, then I send that csr file to CA authority.
CA authority generated Certificate files both DER and base64. When I put back those certificates into keystore I got such errors,
#2: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
Unparseable AuthorityInfoAccess extension due to
java.io.IOException: invalid URI name:file://\\
#3: ObjectId: 2.5.29.31 Criticality=false
Unparseable CRLDistributionPoints extension due to
java.io.IOException: invalid URI name:file://\\
I found the solution on
http://www.cisco.com/en/US/docs/voice_ip_comm/meetingplace/7_1/english/troubleshooting/trbl_ssl_application_server.html
The Solution is "Sign the certificate without the URL that includes the UNC path."
But the CA, certificate cannot create without URL.
Because of such restriction,
how to add CA Certificate which contain invalid URI name "file://\\" into keystore file?

Categories