绕开HttpClient里的SSLPeerUnverifiedException: peer not authenticated异常

最近客户抱怨我们的cobrowsing服务无法访问他们的服务器,查看日志发现以下异常:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
	at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
	at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
	at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:399)
	at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:143)

最初根据搜索结果得知是我们用的HttpClient客户端不认可服务器的SSL证书导致,于是用了网上的办法来绕过服务器证书检查:

DefaultHttpClient httpclient = new DefaultHttpClient();
    SSLContext sslContext;
    try {
        sslContext = SSLContext.getInstance("SSL");
        // set up a TrustManager that trusts everything
        try {
            sslContext.init(null,
                    new TrustManager[] { new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        public void checkClientTrusted(
                                X509Certificate[] certs, String authType) {
                        }

                        public void checkServerTrusted(
                                X509Certificate[] certs, String authType) {
                        }
                    } }, new SecureRandom());定位到
        } catch (KeyManagementException e) {
        }
         SSLSocketFactory ssf = new SSLSocketFactory(sslContext,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
         ClientConnectionManager ccm = this.httpclient.getConnectionManager();
         SchemeRegistry sr = ccm.getSchemeRegistry();
         sr.register(new Scheme("https", 443, ssf));            
    } catch (Exception e) {
        log.error(e.getMessage(),e);
    }

但是依然报错,后来研究得知,这个问题不是在服务器端证书的检查,而在客户端证书检查,然后我试着给了一个证书,但是依然报错。没办法只好根据调用站定位到org.apache.http.conn.ssl.SSLSocketFactory.createSocket里,派生该类,重新实现createSocket,然后不执行verify方法,经过测试就没有出现这个问题了:

SSLSocketFactory ssf = new SSLSocketFactory(ctx)
	{
	    // non-javadoc, see interface LayeredSocketFactory
	    public Socket createSocket(
	        final Socket socket,
	        final String host,
	        final int port,
	        final boolean autoClose
	    ) throws IOException, UnknownHostException {
	        SSLSocket sslSocket = (SSLSocket) socketfactory.createSocket(
	              socket,
	              host,
	              port,
	              autoClose
	        );
	        sslSocket.setEnabledProtocols(new String[]{"SSLv3"});
	        return sslSocket;
	    }
	};

这个因为绕过了双向的证书检查,会带来潜在的不安全性比如中间人攻击,但是考虑到cobrowsing本身对安全性要求并不太高,也就算了。


Last modified on 2014-03-12