在SSL握手时,客户端需要知道服务端的证书,如果证书必须先存在客户端的KeyStore中,那挺麻烦的!
在SSL握手过程中,服务端的确会把证书发给客户端,客户端肯定能拿到!
开始一直想着,一次握手,即拿到证书,又用这个证书来握手,建立连接,后来发现几乎不可行!
决定第一次握手时,先把证书拿到,再进行第二次SSL正式握手!用WireShark来看,Chrome访问HTTPS的站点也是这样做的!
不废话,直接上可用的代码:
import org.apache.http.conn.ssl.SSLContexts; import javax.net.ssl.*; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * God Bless You! * Author: Li Pengpeng * Date: 2014-11-05 */ public class CertUtil { /** * 目的是加载 HTTPS服务器提供的证书(含公钥) * @param keyStore keyStore * @param host host * @param port port * @throws Exception */ public static void checkTrustKey(KeyStore keyStore, String host, int port) throws Exception { TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0]; SavingTrustManager tm = new SavingTrustManager(defaultTrustManager); SSLContext context = SSLContexts.custom().useTLS().loadTrustMaterial(keyStore).build(); context.init(null, new TrustManager[]{tm}, null); SSLSocketFactory factory = context.getSocketFactory(); SSLSocket socket = (SSLSocket) factory.createSocket(host, port); socket.setSoTimeout(10000); try { socket.startHandshake(); socket.close(); // handshake success, need not set Cert } catch (SSLException e) { X509Certificate[] chain = tm.chain; if (chain == null || chain.length == 0) { throw e; } // handshake fail, set Cert for (X509Certificate cert : chain) { keyStore.setCertificateEntry(host + "_" + cert.getSerialNumber(), cert); } } } private static class SavingTrustManager implements X509TrustManager { private final X509TrustManager tm; private X509Certificate[] chain; SavingTrustManager(X509TrustManager tm) { this.tm = tm; } public X509Certificate[] getAcceptedIssuers() { throw new UnsupportedOperationException(); } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new UnsupportedOperationException(); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { this.chain = chain; tm.checkServerTrusted(chain, authType); } } }