在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);
}
}
}