1、 Https通信介绍
Https 即安全的超文本传输协议,最初是由网景公司创建,Https在Http上面提供了一个传输级的安全层,目前安全层所用的协议是SSL(Secure Socket Layer)和其继任者TLS(Transport Layer Security),SSL是一个比较复杂的协议,已有商用和开源的实现版本,例如OpenSSL。所有Http请求和响应数据在传输到网络前都由安全层进行加密,如下图是Https和Http的网络协议层。
客服端与服务器在建立连接前,需要进行握手,握手过程可分为单向和双向认证两类。
(1) 单向认证
客服端只对服务端身份进行认证,具体认证过程如下图:
(2) 双向认证
客服端不仅对服务端身份进行认证,同时服务端也需要客服端发送自己的身份信息,对客服端进行认证,具体认证过程如下图:
2、JSSE介绍
JSEE 全称Java Secure Socket Extension,提供了一个java版本的SSL 协议的框架和实现,包括数据加密、服务器认证、消息完整性校验及可选的客服端认证等功能。Java应用程序可以借助JSEE,方便地实现Https通信。 在JSEE API中,连接的端点类是SSLSocket和 SSLEngine,下图展示了用于创建SSLSocket和SSLEngine的类:
了解socket编程的朋友,从图中应该很容易看出,客服端所用的SSLSocket由SSLSocketFactory创建,服务器用的SSLServerSocket由SSLServerSocketFactory创建,这两个factory由SSLContext创建,SSLContext对象创建后需要用KeyManger、TrustManger、SecureRandom进行初始化,分别对本文用到的几个类说明下,对其他类有兴趣的可以参考oracle JSSE官方文档。
SSLSocket:继承java网络编程中Socket的作用,应用程序通过SSLSocket完成SSL握手、通信数据的收发等;
SSLEngine:类同SSLSocket,比SSLSocket功能更强大,SSLSocket提供的是阻塞IO模型,SSLEngine能提供非阻塞的IO模型;
KeyManger:用于管理密钥、证书等,通信的一端通过KeyManager获取密钥、证书传递给通信对方,对方验证证书获取密钥;
TrustManger:用于管理信任材料,用于验证通信对等方的身份,传递的证书是否合法;
3、HttpClient发送Https请求
借助JSSE,HttpClient对Https请求提供全面的支持,HttpClient 借助JSSE API创建SSLSocket,用于Https请求,相关的类如下图:
SSLProtocolSocketFactory类的createSocket方法内部实现是调用SSLSocketFactory的createSocket方法创建SSLSocket的,如下图:
(1)、标准的Https请求实例
标准的Https请求跟Http请求没有任何差异,HttpClient执行请求时依据url来判断是Http还是Https请求,自动进行通信处理,如下实例:
HttpClienthttpclient = new HttpClient();
GetMethod httpget = newGetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
httpget.releaseConnection();
}
当客服端通过代理与服务器通信时,与标准请求相比,需要设置与代理服务器进行身份认证的参数,建立SSL隧道的工作由Httpclient完成,应用无须关注,如下实例:
HttpClient httpclient = new HttpClient();
httpclient.getHostConfiguration().setProxy("myproxyhost",8080);
httpclient.getState().setProxyCredentials("my-proxy-realm"," myproxyhost",
newUsernamePasswordCredentials("my-proxy-username","my-proxy-password"));
GetMethod httpget = newGetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
httpget.releaseConnection();
}
(2)、定制的Https请求
一般场景下,标准Https请求能满足应用需求,有些应用场景需要对SSL协议细节进行定制,例如客服端接收服务器自签名的证书、双向认证、或用其他第三方SSL库替换JSSE的实现等,可按照如下步骤实现定制Https请求:
第一步,提供一个定制的套接字工厂类,该类实现 org.apache.commons.
httpclient.protocol.SecureProtocolSocketFactory接口,该factory负责创建SSLSocket, 实现定制就在这步,后续几步是描述将定制的Socket用于Https请求中;
第二步,第一步创建的SocketFactory实例化 org.apache.commons.httpclient.protocol.Protocol类型对象,实例化时需要指明协议和端口字段,如下实例代码:
Protocolmyhttps = new Protocol("https", new MySSLSocketFactory(), 443);
第三步,可以请求的主机或协议类设置协议对象,当针对主机设置时,当请求注册的主机时,会用定制SocketFactory创建SSLSocketSocket,代码实例如下:
HttpClienthttpclient = new HttpClient();
httpclient.getHostConfiguration().setHost("www.whatever.com",443, myhttps);
GetMethodhttpget = new GetMethod("/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} catch(Exception e) {
e.printStackTrace();
} finally{
httpget.releaseConnection();
}
当针对https协议设置协议对象时,即所有https请求都会用定制的SocketFactory创建SSLSocketSocket,代码实例如下:
Protocol.registerProtocol("https",
newProtocol("https", new MySSLSocketFactory(), 443));
HttpClienthttpclient = new HttpClient();
GetMethodhttpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} catch(Exception e) {
e.printStackTrace();
} finally {
httpget.releaseConnection();
}
针对https协议设置协议对象这种方式需要慎用,需要考虑应用所有的https请求是否都是可用相同的定制SSLSocket,可跟踪registerProtocol方法实现,在项目中就遇到乱用这种方式导致的问题:公司有个系统需要调用微信和支付宝相关的接口,两者都是Https请求,调用微信接口需要定制Socket,错误地针对https协议注册定制Socket,导致调用微信接口加载本地证书失败时,其他线程调用支付宝接口也出现失败,报错的异常堆栈一致。
(3)、开源的定制SocketFactory实例
Apath开源了相关的定制实例,可以参考EasySSLProtocolSocketFactory、StrictSSLProtocolSocketFactory、AuthSSLProtocolSocketFactory,其中AuthSSLProtocolSocketFactory就是定制双向认证的实例,看了前面的知识介绍理解这些实例代码就轻松了。定制这块还是很灵活的,甚至可定制SSLContext对象,即用其他第三方SSL协议实现。
4、参考资料
1、HTTP权威指南
2、https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/
JSSERefGuide.html
3、http://hc.apache.org/httpclient-3.x/sslguide.html
4、https://alvinalexander.com/java/jwarehouse/commons-httpclient-2.0.1/src/contrib/org/apache/commons/httpclient/contrib/ssl/index.shtml
网友评论