Diagnosticar problemas con conexiones SSL/TLS en Java

Java usa un almacén de Autoridades Certificadoras (CA), Java trustStore, que es mantenido por la Java Development Kit (JDK). Cuando una aplicación Java se conecta a un servicio SSL/TLS, lo primero que hace es comprobar si el certificado está firmado por alguna de las CA albergadas en la trustStore. En caso de que el certificado no pueda ser validado por la trustStore en los logs se registrará un error similar a PKIX path building failed... unable to find valid certification path to requested target.

Para comprobar si, efectivamente, los problemas de conexión se deben a la falta de una CA en la trustStore, se puede usar la clase de Java SSLPoke en dos sencillos pasos:

  1. Descargar SSLPoke.class
  2. Ejecutar java SSLPoke host port

A continuación un ejemplo, usando SSLPoke para validar una trustStore que aun no contiene la correspondiente CA.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
root@jira:/# cd $JAVA_HOME/plugin
root@jira:plugin# wget https://confluence.atlassian.com/kb/files/779355358/779355357/1/1441897666313/SSLPoke.class
root@jira:plugin# java SSLPoke dc.my-company.com 3269
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:387)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:230)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:747)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:138)
at SSLPoke.main(SSLPoke.java:31)
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)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
... 15 more

En caso de querer validar una trustStore distinta a la incluida por defecto en Java, $JAVA_HOME/lib/security/cacert, se puede usar el parámetro -Djavax.net.ssl.trustStore=/path/truststore.jks.

A continuación un ejemplo, usando SSLPoke para validar una trustStore que contiene la correspondiente CA.:

1
2
root@jira:plugin# java -Djavax.net.ssl.trustStore=/apps/certs/truststore.jks SSLPoke dc.my-company.com 3269
Successfully connected

Solucionar el problema PKIX path building failed

La solución consiste descargar los certificados de las CA e incluirlos en la trustStore de Java.

Descargar los certificados de las CA

El primer paso es descargar el certificado de las CA que lo firman. Esto se puede automatizar con el siguiente bloque, el cual permite descargar el certificado del sitio, de las CA Intermedias y de la CA final en formato PEM en archivos separados, donde cert01.pem equivale al certificado del sitio y los sucesivos cert0N.pem a las CA.

1
2
3
SERVER=dc.my-company.com
PORT=3269
openssl s_client -showcerts -connect $SERVER:$PORT</dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert0" c ".pem"}'

Ejemplo:

1
2
3
4
5
6
7
8
root@jira:tmp# SERVER=dc.my-company.com
root@jira:tmp# PORT=3269
root@jira:tmp# openssl s_client -showcerts -connect $SERVER:$PORT</dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert0" c ".pem"}'
depth=2 CN = MY-CONPANY CA Root
verify error:num=19:self signed certificate in certificate chain
DONE
root@jira:tmp# ls cert*
cert01.pem cert02.pem cert03.pem

El primer certificado, cert01.pem, corresponde al certificado del servidor. Los restantes certificados, cert02.pem y cert03.pem en el ejemplo, corresponden a las CA.

Es posible comprobar los certificados mediante openssl x509 -text -noout -in cert.pem, o bien limitar su salida a las partes que nos interesen como por ejemplo:

1
2
3
4
5
6
7
8
9
root@jira:tmp# openssl x509 -subject -issuer -noout -in cert01.pem
subject= /C=ES/ST=Madrid/L=Madrid/O=My-Company/OU=Group IT Infrastructure/CN=dc.my-company.com
issuer= /DC=com/DC=my-company/CN=MY-COMPANY CA
root@jira:tmp# openssl x509 -subject -issuer -noout -in cert02.pem
subject= /DC=com/DC=my-company/CN=MY-COMPANY CA
issuer= /CN=MY-COMPANY CA Root
root@jira:tmp# openssl x509 -issuer -issuer -subject -noout -in cert03.pem
subject= /CN=MY-COMPANY CA Root
issuer= /CN=MY-COMPANY CA Root

Importar los certificados de las CA en la trustStore:

El último paso es importar los certificados de las CA en la keyStore mediante keytool -import -trustcacerts -alias "My-Company CA" -file cert.pem -keystore /path/truststore.jks.
Ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
root@jira:tmp# keytool -import -trustcacerts -alias "My Company CA ROOT" -file cert03.pem -keystore /apps/certs/trsustore.jks
Enter keystore password:
Owner: CN=MY-COMPANY CA Root
issuer= CN=MY-COMPANY CA Root, DC=MY-COMPANY, DC=com
Serial number: 1221a80c00010010e49a
Valid from: Wed Dec 21 10:04:49 CET 2016 until: Sun Dec 20 10:04:49 CET 2026
Certificate fingerprints:
MD5: E7:EA:4D:29:3E:78:B4:FF:82:32:BF:3C:E6:1A:B1:8C
SHA1: 4F:B8:1D:FE:1A:39:94:67:00:8C:E6:71:FE:66:AE:11:A4:A1:28:B5
SHA256: 1D:68:B2:11:18:7A:9D:7B:AA:64:22:12:6C:F8:14:CD:DF:A6:0E:85:B0:28:0C:A8:87:91:CC:10:36:34:5B:C0
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 1.3.6.1.4.1.311.21.10 Criticality=false
0000: 30 18 30 0A 06 08 2B 06 01 05 05 07 03 01 30 0A 0.0...+.......0.
0010: 06 08 2B 06 01 05 05 07 03 02 ..+.......
#2: ObjectId: 1.3.6.1.4.1.311.21.7 Criticality=false
0000: 30 2F 06 27 2B 06 01 04 01 82 37 15 08 85 A9 C1 0/.'+.....7.....
0010: 1A 84 CA DA 2F 85 AD 89 05 85 B1 D1 41 84 E3 F2 ..../.......A...
0020: 0D 81 5A 82 8E D0 73 83 80 81 75 02 01 64 02 01 ..Z...s...u..d..
0030: 0C .
#3: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[
accessMethod: caIssuers
accessLocation: URIName: ldap:///CN=My-COMPANY%20CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=MY-COMPANY,DC=com?cACertificate?base?objectClass=certificationAuthority
,
accessMethod: caIssuers
accessLocation: URIName: http://dc.mycompany.com/CertEnroll/mycompanyca.MY-COMPANY.com_MY-COMPANY%20CA(1).crt
]
]
#4: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: C6 20 70 01 79 FC AE 6B 7F 40 97 01 E8 2F 58 25 . p.y..k.@.../X%
0010: A4 0F 7C E8 ....
]
]
#5: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: ldap:///CN=MY-COMPANY%20CA,CN=MYCOMPANYCA,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=MY-COMPANY,DC=com?certificateRevocationList?base?objectClass=cRLDistributionPoint, URIName: http://mycompanyca.MY-COMPANY.com/CertEnroll/MY-COMPANY%20CA.crl]
]]
#6: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
clientAuth
]
#7: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
]
#8: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 4A B1 96 73 BA C3 D6 71 DC 26 C1 53 2E 18 13 3F J..s...q.&.S...?
0010: 26 CF 5E 7F &.^.
]
]
Trust this certificate? [no]: y
Certificate was added to keystore

Entradas de interés

Contenidos
  1. 1. Solucionar el problema PKIX path building failed
    1. 1.1. Descargar los certificados de las CA
    2. 1.2. Importar los certificados de las CA en la trustStore: