本文主要讨论的是java开发https请求ssl不受信任的解决方法,具体分析及实现代码如下。
在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
如果不加参数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创建之前调用文章里所述的方法,像这个样子:
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();
|
好吧,两种方法都试过有效。
总结
以上就是本文关于java开发https请求ssl不受信任问题解决方法的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://blog.csdn.net/Zhaky/article/details/50923411