https应用:避免HttpClient的”javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated”异常

在开发https应用时,你的测试服务器常常没有一个(有效的)SSL证书。在你的客户端连接测试服务器时,如下的异常会被抛出:”javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated”。


我将讨论使用Apache HttpClient时,解决该问题的一种方法(http://hc.apache.org/httpcomponents-client/)。
1. 代码片段
通常,你会像下面那样来创建HttpClient:
this.client = new DefaultHttpClient();
我们将需要告诉client使用一个不同的TrustManager。TrustManager(http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/javax/net/ssl/TrustManager.html)是一个检查给定的证书是否有效的类。SSL使用的模式是X.509(http://en.wikipedia.org/wiki/X.509),对于该模式Java有一个特定的TrustManager,称为X509TrustManager。首先我们需要创建这样的TrustManager。
[java]  view plain copy
  1. X509TrustManager tm = new X509TrustManager() {  
  2.         public X509Certificate[] getAcceptedIssuers() {  
  3.             return null;  
  4.         }  
  5.         public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}  
  6.         public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}  
  7.     };  



可以看到,该代码做的不多:如果一个证书是无效的,那么TrustManager在checkXXX方法中将抛出CertificateException异常。既然我们总是接受所有的证书,我们从来不抛出异常。
下面我们需要找到一个将TrustManager设置到我们的HttpClient的方法。TrustManager只是被SSL的Socket所使用。Socket通过SocketFactory创建。对于SSL Socket,有一个SSLSocketFactory(http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/javax/net/ssl/SSLSocketFactory.html)。当创建新的SSLSocketFactory时,你需要传入SSLContext到它的构造方法中。在SSLContext中,我们将包含我们新创建的TrustManager。
首先我们需要得到一个SSLContext:
SSLContext ctx = SSLContext.getInstance("TLS");
TLS是SSL的继承者,但是它们使用相同的SSLContext。
然后我们需要使用我们上面新创建的TrustManager来初始化该上下文:
ctx.init(null, new TrustManager[]{tm}, null);
最后我们创建SSLSocketFactory:
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
现在我们仍然需要将SSLSocketFactory注册到我们的HttpClient上。这是在SchemeRegistry中完成的:
[java]  view plain copy
  1. SchemeRegistry registry = new SchemeRegistry();  
  2. registry.register(new Scheme("https"443, ssf));  
  3. ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry);  
  4. return new DefaultHttpClient(mgr, base.getParams());  


我们注册了一个新的Scheme,使用协议https,我们新创建的SSLSocketFactory包含了我们的TrustManager,然后我们告诉HttpClienthttps的默认端口是443.


2. 完整示例代码
下面的类接收HttpClient作为参数,然后返回一个新的接受任意SSL证书的HttpClient:
[java]  view plain copy
  1. /** 
  2.  * 避免HttpClient的”SSLPeerUnverifiedException: peer not authenticated”异常 
  3.  * 不用导入SSL证书 
  4.  * @author shipengzhi(shipengzhi@sogou-inc.com) 
  5.  * 
  6.  */  
  7. public static class WebClientDevWrapper {  
  8.     public static org.apache.http.client.HttpClient wrapClient(org.apache.http.client.HttpClient base) {  
  9.         try {  
  10.             SSLContext ctx = SSLContext.getInstance("TLS");  
  11.             X509TrustManager tm = new X509TrustManager() {  
  12.                 public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
  13.                     return null;  
  14.                 }  
  15.                 public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}  
  16.                 public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}  
  17.                 @Override  
  18.                 public void checkClientTrusted(  
  19.                         java.security.cert.X509Certificate[] chain,  
  20.                         String authType)  
  21.                         throws java.security.cert.CertificateException {  
  22.                     // TODO Auto-generated method stub  
  23.                       
  24.                 }  
  25.                 @Override  
  26.                 public void checkServerTrusted(  
  27.                         java.security.cert.X509Certificate[] chain,  
  28.                         String authType)  
  29.                         throws java.security.cert.CertificateException {  
  30.                     // TODO Auto-generated method stub  
  31.                       
  32.                 }  
  33.             };  
  34.             ctx.init(nullnew TrustManager[] { tm }, null);  
  35.             SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
  36.             SchemeRegistry registry = new SchemeRegistry();  
  37.             registry.register(new Scheme("https"443, ssf));  
  38.             ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry);  
  39.             return new DefaultHttpClient(mgr, base.getParams());  
  40.         } catch (Exception ex) {  
  41.             ex.printStackTrace();  
  42.             return null;  
  43.         }  
  44.     }  
  45. }  


现在你就可以在代码中如下的创建HttpClient了:
[java]  view plain copy
  1. public HttpClient4() {  
  2.     this.client = new DefaultHttpClient();  
  3.     this.client = WebClientDevWrapper.wrapClient(client);  
  4. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值