本文主要討論的是java開發(fā)https請求ssl不受信任的解決方法,具體分析及實現(xiàn)代碼如下。
在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(代碼如下)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
/* * 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() { 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 my.hoolai.com
例如:java InstalCert smtp.zhangsan.com:465 admin
如果不加參數(shù)password和host的端口號,上面的獲取證書程序中默認給的端口號是:443,密碼是:changeit
3、根據(jù)運行提示信息,輸入1,回車,在當前目錄下生成名為: jssecacerts 的證書
將證書放置到$JAVA_HOME/jre/lib/security目錄下, 切記該JDK的jre是工程所用的環(huán)境!!!
或者:
System.setProperty("javax.net.ssl.trustStore", "你的jssecacerts證書路徑");
可以更改密碼,在security目錄下運行命令
keytool -storepasswd -new xxxcom -keystore cacerts
就可以修改密碼,修改后使用命令
keytool -list -v -keystore cacerts
查看文件的信息,會提示需要密碼才能查看,如果輸入密碼與修改后的密碼匹配,說明修改成功了。
PS:至此這種方式可以成功使用ssl了,另外再補充一下,根據(jù)剛才生成的文件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創(chuàng)建之前調(diào)用文章里所述的方法,像這個樣子:
1
2
3
4
5
6
7
8
|
trustAllHttpsCertificates(); HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { return true ; } }; HttpsURLConnection.setDefaultHostnameVerifier(hv); connection = (HttpURLConnection) url.openConnection(); |
好吧,兩種方法都試過有效。
總結(jié)
以上就是本文關(guān)于java開發(fā)https請求ssl不受信任問題解決方法的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/Zhaky/article/details/50923411