网上基本上都是信任所有证书来解决 SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
这个方法的代码如下:
public static String httpsPostInvoke(String url,Map<String, Object> map) throws IOException, ServiceException {
HttpClient client = new DefaultHttpClient();
String str = "";
try {
X509TrustManager xtm = new X509TrustManager(){ //创建TrustManager
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() { return null; }
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{xtm}, null);
SSLSocketFactory socketFactory = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
List<NameValuePair> params = new ArrayList<NameValuePair>();//构建POST请求的参数
for (String key : map.keySet()) {
String value = null;
Object obj = null;
if((obj = map.get(key)) != null){
value = obj.toString();
}
params.add(new BasicNameValuePair(key, value));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
HttpPost post = new HttpPost(url);//创建HttpPost
post.setEntity(entity);
HttpResponse response = client.execute(post);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException("Http接口状态出错("
+ response.getStatusLine().getStatusCode() + ")");
}
str = EntityUtils.toString(response.getEntity());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
client.getConnectionManager().shutdown();
return str;
}
}
执行,ok,没报javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
但是,信任所有的证书,对于强迫症的我和程序猿绝对不能写死的一贯原则,我...又TM花了很长时间,脑补了很多ssl、https的知识,结合网上的代码,发现是可以信任指定证书的,如下代码:
public static String httpsPostInvoke(String url,Map<String, Object> map) throws IOException, ServiceException {
InputStream inputStream = null;
HttpClient httpClient = new DefaultHttpClient();
String result = "";
try{
//从 inputStream 加载 CA 证书
inputStream = NetworkUtil.class.getResourceAsStream("/testCa.cer");
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = certificateFactory.generateCertificate(inputStream);
//构造含有信任 CA 证书的 KeyStore
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
trustStore.setCertificateEntry("myalias", certificate);
SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, socketFactory));
List<NameValuePair> params = new ArrayList<NameValuePair>();//构建POST请求的参数
for (String key : map.keySet()) {
String value = null;
Object obj = null;
if((obj = map.get(key)) != null){
value = obj.toString();
}
params.add(new BasicNameValuePair(key, value));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
HttpPost post = new HttpPost(url);
post.setEntity(entity);
HttpResponse response = httpClient.execute(post);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException("Http接口状态出错("
+ response.getStatusLine().getStatusCode() + ")");
}
result = EntityUtils.toString(response.getEntity());
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} finally {
if(null != inputStream){
inputStream.close();
}
httpClient.getConnectionManager().shutdown();
}
return result;
}
执行......还是javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
想死的心都有了,继续度娘...没有啥进展...又过了一段时间,在stackoverflow(英语渣渣的我遇到问题,顿时感觉自己英语可以达到了8级!!)找到如下说明和命令
要把证书导入到jdk的KeyStore中,如下命令(相信聪明的你看路径和文件名知道怎么修改相应的参数了吧):
keytool -importcert -alias myalias -file "C:\Users\zhangxiaoning\Desktop\testCa.cer" -keystore "D:\Java\jdk1.6.0_45\jre\lib\security\cacerts" -storepass changeit
另外,骚年们想看命令的说明可以参考一下blog,传送门:
http://www.cnblogs.com/benio/archive/2010/09/15/1826990.html
继续执行代码...javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated,我已挂了......
继续百度、实验、验证,排除一系列可能性,发现...好像导出的证书有问题...
我居然把接口的URL在浏览器输入错了,原先导出的证书不是接口url的证书,是两个不同的url!不同的域名!该死的供应商,给了我错误的URL,我也没注意,怪我!
好吧重新导出证书.顺便记录chrome怎么导出。如图:
其他默认就行了。
运行程序...
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
我也不知道怎么表述我的心情了。
找资料,看到了一个blog说好像是jdk版本问题,把jdk1.6.0_45\jre\lib\security下的两个jar包换成jdk1.6以上的jar包,换了后执行直接报了classnotfound
算了,先用jdk1.7的执行,调用成功 = =
但是公司的很多系统都是用jdk1.6的,用jdk1.7的话,可能会有问题,所以继续找资料。
在无意中,百度到了一句,java ssl网络的debug log打印,在你要调用接口前加入以下一句:
System.setProperty("javax.net.debug", "ssl,handshake");
调用https接口前会打印一系列log,前面一堆的可以忽略,直接看最下面的,比如我的:
知道了问题,搜索的范围变小了很多了,直接百度:javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair
查看了很多资料,终于找到了原因,jdk1.6只支持1024字节的DH,我发现我证书的公钥是2048字节的,所以报错了,jdk1.7以上支持,所以成功。
可以查看详细的说明:
传送门:https://stackoverflow.com/questions/6851461/java-why-does-ssl-handshake-give-could-not-generate-dh-keypair-exception
https://github.com/syncany/syncany/issues/483
所以解决方法是:
1.下载jar包:传送门:http://download.csdn.net/detail/nk_tf/9609842 (先前在csdn上下载了一份,发现是坏的jar包,我的1分没了,气死我了~~)
2.复制这两个jar包到: $JAVA_HOME/jre/lib/ext
3.编辑$JAVA_HOME/jre/lib/security/java.security,在9下面加入这句:
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=com.sun.net.ssl.internal.ssl.Provider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=com.sun.security.sasl.Provider
security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.8=sun.security.smartcardio.SunPCSC
security.provider.9=sun.security.mscapi.SunMSCAPI
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
重新调用,没报错,OK!!