最近对产品的通讯方式进行了调整,由原先的socket通信,改用https通信(也支持http). https通信相对于http通信多了一层 客户端与服务端之间的双向认证, 遇到的问题也是出现在这里,抓狂了近一周,任然无结果. 但不管怎样先把这几天学习SSL的一些想法写出来先,以后碰到少走点弯路.
一般我们通过httpClient 发送https请求代码如下:
public static void main(String args[]) throws Exception { HttpClient client=wrapClient(new DefaultHttpClient()); HttpGet get=new HttpGet("https://localhost:8000"); HttpResponse resp=client.execute(get); System.out.println(resp.getStatusLine().getStatusCode()); } public static HttpClient wrapClient(HttpClient base) throws Exception{ SSLContext ctx = SSLContext.getInstance("TLSv1"); X509TrustManager tm = new X509TrustManager(){ public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public void checkServerTrusted(X509Certificate[] arg0, String arg1)throws CertificateException{} }; ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); ClientConnectionManager ccm = base.getConnectionManager(); SchemeRegistry registry = ccm.getSchemeRegistry(); registry.register(new Scheme("https", 443, ssf)); return new DefaultHttpClient(ccm, base.getParams()); }
我遇到的情况是内网有三台window机子:
192.168.64.11 win7, 192.168.64.101 win7, 192.168.64.102 win2003
11机子与101机子相互发送 https请求没有任何问题, 102机子发送https请求到11没有问题,但是11机子发送https请求到102怎么也发不通,抛出peer not authenticated 异常
刚开始以为是102的防火墙问题,关了之后还是发不通,百度说可能是两台机子的时间对不上,对了下时间也没问题,这就纳闷了.
查了下httpClient 官方发送https请求做法,与上面的代码雷同, 没办法自己看源代码,最后查到是 SSL握手的问题,即数据没交互前出错了.看问题代码:
public static void main(String args[]) throws Exception { testSslByHttpClient(); } public static void testSslByHttpClient() throws Exception{ SSLContext ctx = SSLContext.getInstance("SSL"); X509TrustManager tm = new X509TrustManager(){ public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public void checkServerTrusted(X509Certificate[] arg0, String arg1)throws CertificateException{} }; ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf=new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); Socket socket=ssf.createSocket(); InetSocketAddress addre=new InetSocketAddress("127.0.0.1", 8000); socket.setSoTimeout(1000); socket.connect(addre,0); SSLSocket ssl=(SSLSocket) socket; //ssl.setNeedClientAuth(false); SSLSession session=ssl.getSession(); System.out.println(session.getCipherSuite()); }
正常输出结果: TLS_RSA_WITH_AES_128_CBC_SHA(值可以变化)
普及下 cipherSuite 是双方进行通信的密码套件,约定了双方加密相关的行为. 了解详情参考下:http://blog.chinaunix.net/uid-24709751-id-4050413.html.
在来看下 SSLSocket类 getSession()方法的解释:
Returns the SSL Session in use by this connection. These can be long lived, and frequently correspond to an entire login session for some user. The session specifies a particular cipher suite which is being actively used by all connections in that session, as well as the identities of the session's client and server. This method will initiate the initial handshake if necessary and then block until the handshake has been established. If an error occurs during the initial handshake, this method returns an invalid session object which reports an invalid cipher suite of "SSL_NULL_WITH_NULL_NULL".