JDK https證書問題: unable to find valid certification path to requested target

在使用Java程序訪問https資源時報以下錯誤:

sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

錯誤原因

在了解該問題產(chǎn)生原因之前石洗,先了解一下https的通信原理:

HTTPS傳輸流程
  • 1.瀏覽器將支持的加密算法信息發(fā)送給服務(wù)器
  • 2.服務(wù)器選擇一套瀏覽器支持的加密算法漆羔,以證書的形式回發(fā)瀏覽器
    • 證書信息里一般包含證書發(fā)布的CA機(jī)構(gòu)他匪,證書的有效期,公鑰,證書所有者,簽名等
  • 3.瀏覽器驗(yàn)證證書合法性看成,并結(jié)合證書公鑰加密信息發(fā)送給服務(wù)器(瀏覽器會隨機(jī)生成一串密碼,并使用證書中的公鑰加密跨嘉,之后使用哈希算法的握手消息川慌,使用隨機(jī)數(shù)對消息進(jìn)行加密)
  • 4.服務(wù)器使用私鑰解密信息(確定隨機(jī)密碼),驗(yàn)證哈希(通過密碼解密Web傳來的握手信息)偿荷,加密響應(yīng)消息(新的握手信息)回發(fā)瀏覽器
  • 5.瀏覽器解密響應(yīng)消息窘游,并對消息進(jìn)行驗(yàn)真,之后進(jìn)行加密交互數(shù)據(jù)(對稱加密)

平時跳纳,當(dāng)我們使用瀏覽器訪問https資源的時候忍饰,若瀏覽器發(fā)現(xiàn)證書不可信(一般原因是證書未通過權(quán)威機(jī)構(gòu)頒發(fā)),會彈出提示框告訴用戶寺庄,讓用戶選擇是否信任該證書或是否繼續(xù)訪問等艾蓝。類似的,當(dāng)通過Java代碼訪問https資源時斗塘,也會有相似的安全機(jī)制赢织。因?yàn)镴VM使用了自己的security manager而非操作系統(tǒng)的keyring,因此當(dāng)https請求資源不在security manager管理范圍內(nèi)會報文章開頭處的錯誤以提示開發(fā)人員進(jìn)行處理

問題處理

該問題處理有兩種方式馍盟,
一是避開安全檢查于置,即不對請求的https資源進(jìn)行證書驗(yàn)證
二是將請求的https請求資源證書添加信任
二者各有優(yōu)勢,筆者采用了后者對問題進(jìn)行了處理

處理過程

創(chuàng)建 InstallCert.java 文件

/* 
 * 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);  
        }  
    }  
  
}  

終端編譯 InstallCert.java 文件并運(yùn)行

javac InstallCert.java
java InstallCert www.xxx.com

運(yùn)行后出現(xiàn)下圖提示

1.png

輸入1 回車
當(dāng)出現(xiàn)下圖信息說明證書已經(jīng)生成
2.png

在 InstallCert.java 目錄下會找到生成的 jssecacerts 文件贞岭,將其復(fù)制到JDK路徑下 \jre\lib\security
重啟項(xiàng)目八毯,問題解決

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搓侄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子话速,更是在濱河造成了極大的恐慌讶踪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泊交,死亡現(xiàn)場離奇詭異乳讥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廓俭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門云石,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人研乒,你說我怎么就攤上這事留晚。” “怎么了告嘲?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長路星。 經(jīng)常有香客問我萌丈,道長退疫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任仰楚,我火速辦了婚禮,結(jié)果婚禮上犬庇,老公的妹妹穿的比我還像新娘僧界。我一直安慰自己,他們只是感情好臭挽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布捂襟。 她就那樣靜靜地躺著,像睡著了一般欢峰。 火紅的嫁衣襯著肌膚如雪葬荷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天纽帖,我揣著相機(jī)與錄音宠漩,去河邊找鬼。 笑死懊直,一個胖子當(dāng)著我的面吹牛扒吁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播室囊,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雕崩,長吁一口氣:“原來是場噩夢啊……” “哼魁索!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晨逝,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蛾默,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捉貌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體支鸡,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年趁窃,在試婚紗的時候發(fā)現(xiàn)自己被綠了牧挣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡醒陆,死狀恐怖瀑构,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刨摩,我是刑警寧澤寺晌,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站澡刹,受9級特大地震影響呻征,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜罢浇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一陆赋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚷闭,春花似錦攒岛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗅榕,卻和暖如春挠进,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背誊册。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工领突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人案怯。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓君旦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子金砍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容