SSL - SSLHandshakeException: unable to find valid certification path to requested target

襲冷發表於2018-08-27

一、異常日誌

javax.net.ssl.SSLHandshakeException
    Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
	at java.security.cert.CertPathBuilder.build(Unknown Source)
	... 18 more

 

二、解決方案

        因缺少安全證照出現的異常,可通過下面的方式將服務端的安全認證證照匯入到客戶端

    1、編譯如下工具類

package cert;

/* 
 * 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");  
            }  
        }  
          
        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 + "'");  
    }  
  
      
    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();  
  
      
    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 & 15]);  
            sb.append(' ');  
        }  
        return sb.toString();  
    }  
  
      
    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、執行 java InstallCert hostname,比如:java InstallCert www.twitter.com 或 java InstallCert 101.231.204.80:5000。然後可以看到如下日誌 

 

[root@local usr]$ java InstallCert 101.231.204.80:5000
Loading KeyStore /usr/java/jdk1.7.0_55/jre/lib/security/cacerts...
Opening connection to 101.231.204.80:5000...
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 sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:804)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)
        at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323)
        at cert.InstallCert.main(InstallCert.java:98)
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:385)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
        at sun.security.validator.Validator.validate(Validator.java:260)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
        at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:107)
        at cert.InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:189)
        at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:813)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1323)
        ... 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:196)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
        ... 16 more

Server sent 1 certificate(s):

 1 Subject CN=101.231.204.80, OU=Local RA, O=cfca, L=beijing, ST=beijing, C=CN
   Issuer  CN=101.231.204.80, OU=Local RA, O=cfca, L=beijing, ST=beijing, C=CN
   sha1    02 eb 09 66 15 ec a3 c4 d8 8e f6 4f e5 a7 a9 18 3e 02 23 01 
   md5     64 9f 16 3b e7 95 a6 a2 14 a6 79 de 9e 82 78 17 

Enter certificate to add to trusted keystore or 'q' to quit: [1]

    3、根據提示輸入 1 ,會看到如下日誌

 

1

[
[
  Version: V3
  Subject: CN=101.231.204.80, OU=Local RA, O=cfca, L=beijing, ST=beijing, C=CN
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: 27046836126329656084021915372821670843451536300460607349030517292404890084799178086846617620738935902888712284603725485401688160327085116409553281451072821171829251692829997697184241960076922057414797279941386914233404377962849519705096898302532673605044905252381354661149660263864618166316903627311506815380962630271597252372061967018070023082540931349957578186959391961135716686695602385932530405406600044526797585516408002645028319196281430260529530215108676165655053787659565528243256834915620979491199423351369510848526762486371845423831506230623973510957108819507437170927278403535409991979426324853850572293293
  public exponent: 65537
  Validity: [From: Mon Apr 10 21:49:35 CST 2017,
               To: Tue Apr 10 21:49:35 CST 2018]
  Issuer: CN=101.231.204.80, OU=Local RA, O=cfca, L=beijing, ST=beijing, C=CN
  SerialNumber: [    0dade1ef]

]
  Algorithm: [SHA256withRSA]
  Signature:
0000: B0 69 4A 5C A1 84 78 47   27 41 24 36 F9 F1 E1 3D  .iJ\..xG'A$6...=
0010: 1B E3 5E C6 B4 4D 4E E0   CB A4 DF 9A 3F 39 74 33  ..^..MN.....?9t3
0020: BC E7 98 D7 B3 24 CD 7C   91 92 FD 8C 9E 7B 89 B8  .....$..........
0030: 54 A3 6E F4 DA D1 99 1A   6B 35 7B C9 52 C8 6A E8  T.n.....k5..R.j.
0040: C8 16 F8 4B 92 EA 17 8B   CF 32 EA B7 5C 3D 95 DC  ...K.....2..\=..
0050: 73 EE BA 1D E8 B1 6A 46   96 48 F8 97 79 58 BA 48  s.....jF.H..yX.H
0060: 7B F2 56 A2 80 A9 4B B2   B7 7F 7B 09 51 66 A8 C8  ..V...K.....Qf..
0070: F4 B2 1F 96 47 62 81 92   2C D3 13 20 4B B9 B5 8E  ....Gb..,.. K...
0080: 12 7E 46 BA D7 34 AF 6D   7F 1B 3D 44 5D 26 15 AC  ..F..4.m..=D]&..
0090: 9E D2 2A 77 EC 34 6D 22   A1 85 38 5B 95 2C C7 70  ..*w.4m"..8[.,.p
00A0: 4A EC 51 F0 4A 2B 21 9D   56 46 6E 18 9F 50 1D E5  J.Q.J+!.VFn..P..
00B0: F7 8C BD 50 DB FE 57 9C   CD 14 EC 1F B2 37 E7 47  ...P..W......7.G
00C0: AA 05 1E 7F 62 53 22 26   FD 78 E4 C2 54 43 D0 BE  ....bS"&.x..TC..
00D0: 23 7C 75 16 85 EE 40 F2   51 70 AA C6 B5 E5 0F 52  #.u...@.Qp.....R
00E0: 2B DC E8 CE 54 0C 94 09   80 85 B9 76 8C 46 99 9C  +...T......v.F..
00F0: C6 66 7F B9 EC 36 C5 97   39 A9 31 D0 05 08 8C 71  .f...6..9.1....q

]

Added certificate to keystore 'jssecacerts' using alias '101.231.204.80-1'

    4、此時在當面目錄下已經生成了一個名為jssecacerts的證照,將這個的證照拷貝到 /%JAVA_HONME%/jre/lib/security/ 目錄中,或者使用System.setProperty("javax.net.ssl.trustStore", "jssecacerts證照路徑") 載入證照即可

 

 

 

 

相關文章