SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed

1. Background

After a project updated the Nginx SSL certificate in May 2023, it was normal to access the system directly in the browser, but another project of its own and other third-party systems returned an error message when calling through the interface address:

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
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)

The caller environment that triggers this error message is the JAVA development environment.

2. Reason

2.1 may be related to the SSL intermediate certificate update issued by DigiCert in 2023:

[Service Bulletin Update] DigiCert will migrate to Generation 2 (G2) Root and ICA Hierarchies in March 2023
https://www.gworg.com/industry-news/1509.html

[Important] Instructions for DigiCert to update DV SSL intermediate certificate
https://help.aliyun.com/zh/ssl-certificate/product-overview/digicert-updated-dv-intermediate-certificates?spm=a2c4g.11186623.0.i19

2.2 It may also be that the certificate chain of the server certificate file is incomplete

related information:

Certificate chain (Compliant SSL certificate path consists of: root certificate, intermediate certificate, and server certificate to form an SSL certificate)

  • Root certificate, also known as CA certificate (such as “ca.crt”)
  • Intermediate certificate, also known as intermediate CA certificate

(When configuring HTTPS, you need to splice the server certificate and the intermediate certificate into a complete certificate before uploading)

  • Server Certificate

(https://help.aliyun.com/document_detail/66710.html?spm=a2c4e.11153940.0.0.59e9749e2KMPBG)

Root certificate, CA certificate

The browser or operating system will have a built-in list of trusted certificates, and the root certificate does not require server-side configuration;

Java program clients or other clients need to configure the root certificate in advance. Installing a CA certificate (such as “ca.crt”) on the client usually means that the client will trust the digital certificate issued by this CA certificate.

https authentication

The certificate sent by the server to the client contains the intermediate CA certificate and the server certificate, which are spliced together.

Quotation source: Author: ScoTing Link: https://www.jianshu.com/p/c64546495ee4

3. Solution

There are two solutions to the “PKIX path building failed” exception:

One is the caller solution: If your project calls a third-party interface and this exception occurs, and the third-party system is inconvenient to handle it, you can fix it on your own system;

One is to solve the problem on the server side: if your system is a server side, you can handle it on the server side, and other callers can return to normal without making changes);

3.1 Caller solution

The caller’s solution is to import the security authentication certificate of the called domain name into its own server; the specific implementation method is:

1. Compile InstallCert.java to get two class files and execute the InstallCert class

/*
 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of Sun Microsystems nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
 
public class InstallCert {
public static void main(String[] args) throws Exception {
String host;
int port;
char[] passphrase;
if ((args.length == 1) || (args.length == 2)) {
String[] c = args[0].split(":");
host = c[0];
port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
String p = (args.length == 1) ? "changeit" : args[1];
passphrase = p.toCharArray();
} else {
System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
return;
}
 
File file = new File("jssecacerts");
if (file.isFile() == false) {
char SEP = File.separatorChar;
File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
file = new File(dir, "jssecacerts");
if (file.isFile() == false) {
file = new File(dir, "cacerts");
}
}
\t\t
System.out.println("Loading KeyStore " + file + "...");
InputStream in = new FileInputStream(file);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, passphrase);
in.close();
 
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
context.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory factory = context.getSocketFactory();
 
System.out.println("Opening connection to " + host + ":" + port + "...");
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
try {
System.out.println("Starting SSL handshake...");
socket.startHandshake();
socket.close();
System.out.println();
System.out.println("No errors, certificate is already trusted");
} catch (SSLException e) {
System.out.println();
e.printStackTrace(System.out);
}
 
X509Certificate[] chain = tm.chain;
if (chain == null) {
System.out.println("Could not obtain server certificate chain");
return;
}
 
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 
System.out.println();
System.out.println("Server sent " + chain.length + " certificate(s):");
System.out.println();
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
MessageDigest md5 = MessageDigest.getInstance("MD5");
for (int i = 0; i < chain.length; i + + ) {
X509Certificate cert = chain[i];
System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN());
System.out.println(" Issuer " + cert.getIssuerDN());
sha1.update(cert.getEncoded());
System.out.println(" sha1 " + toHexString(sha1.digest()));
md5.update(cert.getEncoded());
System.out.println(" md5 " + toHexString(md5.digest()));
System.out.println();
}
 
System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
String line = reader.readLine().trim();
int k;
try {
k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
} catch (NumberFormatException e) {
System.out.println("KeyStore not changed");
return;
}
 
X509Certificate cert = chain[k];
String alias = host + "-" + (k + 1);
ks.setCertificateEntry(alias, cert);
 
OutputStream out = new FileOutputStream("jssecacerts");
ks.store(out, passphrase);
out.close();
 
System.out.println();
System.out.println(cert);
System.out.println();
System.out.println("Added certificate to keystore 'jssecacerts' using alias '" + alias + "'");
}
 
\t
private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
 
\t
private static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 3);
for (int b : bytes) {
b&= 0xff;
sb.append(HEXDIGITS[b >> 4]);
sb.append(HEXDIGITS[b & amp; 15]);
sb.append(' ');
}
return sb.toString();
}
 
\t
private static class SavingTrustManager implements X509TrustManager {
private final X509TrustManager tm;
private X509Certificate[] chain;
 
SavingTrustManager(X509TrustManager tm) {
this.tm = tm;
}
 
public X509Certificate[] getAcceptedIssuers() {
throw new UnsupportedOperationException();
}
 
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
throw new UnsupportedOperationException();
}
 
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
this.chain = chain;
tm.checkServerTrusted(chain, authType);
}
}
}

2. Execution method: java InstallCert hostname For example: java InstallCert www.cebbank.com

Next you will see the following print information

java InstallCert www.cebbank.com
Loading KeyStore /usr/java/jdk1.6.0_31/jre/lib/security/cacerts...
Opening connection to www.cebbank.com:443...
Starting SSL handshake...
 
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
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1731)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:925)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1170)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1197)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1181)
at InstallCert.main(InstallCert.java:102)
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:323)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:198)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1198)
... 8 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)
... 14 more
 
Server sent 1 certificate(s):
 
 1 Subject CN=www.cebbank.com, OU=Terms of use at www.verisign.com/rpa (c)05, OU=CEB, O="China Everbright Bank Co., Ltd", L=Beijing
   Issuer CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network
   sha1 5b d2 85 6e b3 a4 2b 07 a2 13 47 b3 be 3e 1f c9 d3 ce 46 57
   md5 05 d8 ae ee f1 d9 51 63 6d 2f 11 e0 ac d0 e7 d7
 
Enter certificate to add to trusted keystore or 'q' to quit: [1]

3. Then enter 1 and press Enter, you will see printing information similar to the following

[
[
  Version: V3
  Subject: CN=www.cebbank.com, OU=Terms of use at www.verisign.com/rpa (c)05, OU=CEB, O="China Everbright Bank Co., Ltd", L=Beijing
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
 
  Key: Sun RSA public key, 2048 bits
  modulus: 308312463845488095407052282928413930625837322509939099163557804137221615570745684697382545734720933417104815171399108 77
  public exponent: 65537
  Validity: [From: Mon Jul 02 08:00:00 CST 2012,
               To: Thu Jul 03 07:59:59 CST 2014]
  Issuer: CN=VeriSign Class 3 Extended Validation SSL CA, OU=Terms of use at https://www.verisign.com/rpa (c)06, OU=VeriSign Trust Network
  SerialNumber: [5715ab25 6be8fa42 2fa28dd4 601bc732]
 
Certificate Extensions: 9
[1]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [
   accessMethod: 1.3.6.1.5.5.7.48.1
   accessLocation: URIName: http://ocsp.verisign.com,
   accessMethod: 1.3.6.1.5.5.7.48.2
   accessLocation: URIName: http://EVSecure-aia.verisign.com/EVSecure2006.cer]
]
 
[2]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: www.cebbank.com
]
 
[3]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: FC 8A 50 BA 9E B9 25 5A 7B 55 85 4F 95 00 63 8F ..P...%Z.U.O..c.
0010: E9 58 6B 43 .XkC
]
 
]
 
[4]: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
  [CertificatePolicyId: [2.16.840.1.113733.1.7.23.6]
[PolicyQualifierInfo: [
  qualifierID: 1.3.6.1.5.5.7.2.1
  qualifier: 0000: 16 1C 68 74 74 70 73 3A 2F 2F 77 77 77 2E 76 65 ..https://www.ve
0010: 72 69 73 69 67 6E 2E 63 6F 6D 2F 63 70 73 risign.com/cps
 
]] ]
]
 
[5]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]
 
[6]: ObjectId: 1.3.6.1.5.5.7.1.12 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 62 30 60 A1 5E A0 5C 30 5A 30 58 30 56 16 09 .b0`.^.\0Z0X0V..
0010: 69 6D 61 67 65 2F 67 69 66 30 21 30 1F 30 07 06 image/gif0!0.0..
0020: 05 2B 0E 03 02 1A 04 14 4B 6B B9 28 96 06 0C BB . + ......Kk.(....
0030: D0 52 38 9B 29 AC 4B 07 8B 21 05 18 30 26 16 24 .R8.).K..!..0 &.$
0040: 68 74 74 70 3A 2F 2F 6C 6F 67 6F 2E 76 65 72 69 http://logo.veri
0050: 73 69 67 6E 2E 63 6F 6D 2F 76 73 6C 6F 67 6F 31 sign.com/vslogo1
0060: 2E 67 69 66 .gif
 
 
[7]: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
  serverAuth
  clientAuth
]
 
[8]: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
  [DistributionPoint:
     [URIName: http://EVSecure-crl.verisign.com/EVSecure2006.crl]
]]
 
[9]: ObjectId: 2.5.29.15 Criticality=false
KeyUsage[
  DigitalSignature
  Key_Encipherment
]
 
]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 42 0A 89 BF 48 08 1E F4 98 F2 E5 DB 0D 83 EF 37 B...H.....7
0010: EC 27 6F 4D 81 69 C6 4A 4C 17 EC 57 F5 48 2A 14 .'oM.i.JL..W.H*.
0020: 3C 54 B2 C5 49 39 42 BA EC 83 78 02 F9 96 6C 63 <T..I9B...x...lc
0030: 80 BC 60 61 BB 20 D1 AD C3 D3 76 47 6F 0C 7B AC ..`a. ....vGo...
0040: 76 B2 C7 2D B1 0A 7A 00 CA 40 38 86 FF 9F 12 F5 v..-..z..@8.....
0050: BE 5A E7 42 97 2F DF DE 0C 19 C5 F6 92 58 17 7A .Z.B./.......X.z
0060: 9A 1D 2C 2C DA 8B 83 83 2D BE 07 58 56 36 92 E7 ..,,....-..XV6..
0070: B1 F8 A0 B5 00 F4 C3 30 D1 34 37 3D 94 75 28 04 .......0.47=.u(.
0080: A2 D8 C3 FE B1 E1 C2 2E 51 A8 6F D5 09 6D 49 DB.....Q.o..mI.
0090: 2E 1D 4B F7 A8 06 30 B4 97 E7 C2 33 26 FD 6A DF ..K...0....3 &.j.
00A0: D6 B0 10 A1 F2 73 DD 5A 60 DE 51 5E EA 80 46 86 .....s.Z`.Q^..F.
00B0: 25 0B 53 FC C2 57 80 35 09 2D 31 55 28 35 EE 0F %.S..W.5.-1U(5..
00C0: 62 50 4B 12 75 0B 02 9F 2F 0B D2 8A 0D 23 E3 C1 bPK.u.../....#..
00D0: 48 28 56 33 E1 DE 31 DD 72 78 15 96 EE 2B A5 1D H(V3..1.rx... + ..
00E0: 37 85 1B E5 88 53 80 88 02 6D 90 F3 E6 4A 74 AC 7....S...m...Jt.
00F0: D2 CA 0E 04 BC 46 A0 57 34 FA CF 9D E5 D7 0E 4B .....F.W4......K
]
Added certificate to keystore 'jssecacerts' using alias 'www.cebbank.com-1'

4. At the same time, we will find that a certificate named jssecacerts has been generated in the current directory and copy the certificate named jssecacerts to the server %JAVA_HONME%\jre\lib\security\ Restart the last application service in the directory and the certificate will take effect.

3.2 Server-side solution

If you want to solve this kind of problem on the server side, you need to check whether it is caused by a missing intermediate certificate.

Detection:

Detection address: https://www.myssl.cn/tools/check-server-cert.html

Test results:

If a prompt similar to the one shown in the screenshot appears, it means that the intermediate certificate is missing.

Solution:

1. Find your certificate file: it usually has a .crt or .pem suffix.

2. Copy the contents of the certificate file to this address resolution: Download DigiCert and GeoTrust intermediate certificates_OPENSSL toolkit_Xuntong Integrity

3. After successful parsing, you can get 2 generated certificate files: intermediate certificate file and root certificate file. Merge the contents of your certificate file (.crt or .pem), the contents of the intermediate certificate file and the root certificate file into one In the new file (such as fullchain.pem), the merged certificate file format requirements are extremely strict, and no blank lines, spaces, etc. can exist. The combined format example is:

4. Configure the merged certificate file upload server to Nginx and restart nginx to solve the problem;

5. Check the address again to check the certificate installation result.

Relevant citations:

https://blog.csdn.net/jadyer/article/details/7799540

https://juejin.cn/post/7021329856323387429