java開發https請求ssl不受信任問題

本文來自http://blog.csdn.net/zhaky/article/details/50923411

在java代碼中請求https鏈接的時候,可能會報下面這個錯誤

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

原因是沒有證書。在瀏覽器中直接使用url訪問是可以的,應該是瀏覽器之前就保存過對應的.cer證書。

解決方法有兩種,從目標機器獲得有效證書或者忽略證書信任問題。

一、獲得目標機器有效證書

1、編譯安裝證書程序  javac InstallCert.java(代碼如下)

[java] view plain copy

/* 

 * 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. 

 */  

/** 

 * http://blogs.sun.com/andreas/resource/InstallCert.java 

 * Use: 

 * java InstallCert hostname 

 * Example: 

 *% java InstallCert ecc.fedora.redhat.com 

 */  

  

import javax.net.ssl.*;  

import java.io.*;  

import java.security.KeyStore;  

import java.security.MessageDigest;  

import java.security.cert.CertificateException;  

import java.security.cert.X509Certificate;  

  

/** 

 * Class used to add the server's certificate to the KeyStore 

 * with your trusted certificates. 

 */  

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() {  

        return new X509Certificate[0]; //for jdk1.7  

         //throw new UnsupportedOperationException(); //for jdk1.6 

        }  

  

        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);  

        }  

    }  

  

}  




注意:

以上代碼有一個地方要區分JDK1.6還是JDK1.7

        public X509Certificate[] getAcceptedIssuers() {  

        return new X509Certificate[0]; //for jdk1.7  

         //throw new UnsupportedOperationException(); //for jdk1.6 

        } 

2、運行安裝證書程序生成證書

java InstallCert my.hoolai.com

例如:java InstalCert smtp.zhangsan.com:465 admin

如果不加參數password和host的端口號,上面的獲取證書程序中默認給的端口號是:443,密碼是:changeit


3、根據運行提示信息,輸入1,回車,在當前目錄下生成名爲: jssecacerts 的證書

將證書放置到$JAVA_HOME/jre/lib/security目錄下, 切記該JDK的jre是工程所用的環境!!!

或者:

System.setProperty("javax.net.ssl.trustStore", "你的jssecacerts證書路徑");

可以更改密碼,在security目錄下運行命令

keytool -storepasswd -new xxxcom -keystore cacerts

就可以修改密碼,修改後使用命令

keytool -list -v -keystore cacerts

查看文件的信息,會提示需要密碼才能查看,如果輸入密碼與修改後的密碼匹配,說明修改成功了。

 PS:至此這種方式可以成功使用ssl了,另外再補充一下,根據剛纔生成的文件jssecacerts,可以生成cer文件,

命令如下

keytool -export -alias xxx.com-1 -keystore jssecacerts -rfc -file xxx.cer

如上,之前的工具類中默認命名別名是加上"-1"。使用InstallCert設置的密碼需要跟cacerts文件中的密碼一致,

如果修改過密碼,就需要修改InstallCert類中對應的密碼字符串,否則會有下面這個異常:

java.security.UnrecoverableKeyException: Password verification failed

二、忽略證書信任問題

源碼:http://mengyang.iteye.com/blog/575671

一定要注意需要在connection創建之前調用文章裏所述的方法,像這個樣子:

trustAllHttpsCertificates();

HostnameVerifier hv = new HostnameVerifier() {  

       public boolean verify(String urlHostName, SSLSession session) {  

           return true;  

       }  

   };

HttpsURLConnection.setDefaultHostnameVerifier(hv);

connection = (HttpURLConnection) url.openConnection();

 兩種方法都有效


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章