一、单向认证
/************************************* https单向认证 ******************************************/
public static class SSLCustomSocketFactory extends SSLSocketFactory {
private static final String TAG = "SSLCustomSocketFactory";
public SSLCustomSocketFactory(KeyStore trustStore) throws Throwable {
super(trustStore);
}
public static SSLSocketFactory getSocketFactory(){
try {
//客户端证书
InputStream ins = App.getContext().getAssets().open("client.cer");
CertificateFactory cerFactory = CertificateFactory.getInstance("X.509");
Certificate cer = cerFactory.generateCertificate(ins);
//创建一个证书库,并将证书导入证书库
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
trustStore.setCertificateEntry("trust", cer);
SSLSocketFactory factory = new SSLSocketFactory(trustStore);
return factory;
}catch (Exception e){
logger.w("%s", e.getMessage());
e.printStackTrace();
}
return null;
}
}
/************************************* https单向认证 end ***************************************/
二、双向认证。这里介绍两种实现。
/************************************* https 双向认证1 *****************************************/
public static SSLSocketFactory createSSLSocketFactory() {
final String KEY_PASS = "123456";
MySSLSocketFactory sf = null;
try {
// 服务器的证书, 供客户端校验
InputStream ins = App.getContext().getAssets().open("client.bks");
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try {
trustStore.load(ins, KEY_PASS.toCharArray());
} finally {
ins.close();
}
sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
} catch (Exception e) {
e.printStackTrace();
logger.e("%s", e, e.getClass().toString());
}
return sf;
}
public static class MySSLSocketFactory extends SSLSocketFactory {
private static final String CLIENT_KEY_MANAGER = "X509"; // 密钥管理器
private static final String CLIENT_TRUST_MANAGER = "X509"; // 信任证书管理器
private static final String CLIENT_KEY_KEYSTORE = "BKS";
private static final String CLIENT_KET_PASSWORD = "123456";
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
// 取得keyManageFactory和TrustManagerFactory的x509密钥管理实例
KeyManagerFactory keyManager = KeyManagerFactory.getInstance(CLIENT_KEY_MANAGER);
TrustManagerFactory trustManager = TrustManagerFactory.getInstance(CLIENT_TRUST_MANAGER);
KeyStore kks = KeyStore.getInstance(CLIENT_KEY_KEYSTORE);
InputStream ins;
try {
// 客户端证书, 供服务器端校验
ins = App.getContext().getAssets().open("verify_by_server.bks");
kks.load(ins, CLIENT_KET_PASSWORD.toCharArray());
ins.close();
} catch (IOException e) {
e.printStackTrace();
logger.e("%s", e, e.getClass().toString());
} catch (CertificateException e) {
e.printStackTrace();
logger.e("%s", e, e.getClass().toString());
}
keyManager.init(kks, CLIENT_KET_PASSWORD.toCharArray());
trustManager.init(truststore);
sslContext.init(keyManager.getKeyManagers(), trustManager.getTrustManagers(), null);
}
@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
injectHostname(socket, host);
return sslContext.getSocketFactory().createSocket(socket, host,
port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
private void injectHostname(Socket socket, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(socket.getInetAddress(), host);
} catch (Exception ignored) {
}
}
}
/******************************** https 双向认证1 end ****************************************/
另一种实现:
/******************************** https 双向认证2 start **************************************/
private static final String KEY_STORE_TYPE_BKS = "bks";//证书类型 固定值
private static final String KEY_STORE_TYPE_P12 = "PKCS12";//证书类型 固定值
private static final String KEY_STORE_CLIENT_PATH = "client.p12";//客户端要给服务器端认证的证书
private static final String KEY_STORE_TRUST_PATH = "client.bks";//客户端验证服务器端的证书库
private static final String KEY_STORE_PASSWORD = "123456";// 客户端证书密码
private static final String KEY_STORE_TRUST_PASSWORD = "123456";//客户端证书库密码
/**
* 获取SslSocketFactory
*
* @param context 上下文
* @return SSLSocketFactory
*/
public static SSLSocketFactory getSslSocketFactory(Context context) {
try {
// 服务器端需要验证的客户端证书
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
// 客户端信任的服务器端证书
KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);
InputStream ksIn = context.getResources().getAssets().open(KEY_STORE_CLIENT_PATH);
InputStream tsIn = context.getResources().getAssets().open(KEY_STORE_TRUST_PATH);
try {
keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ksIn.close();
} catch (Exception ignore) {
}
try {
tsIn.close();
} catch (Exception ignore) {
}
}
return new SSLSocketFactory(keyStore, KEY_STORE_PASSWORD, trustStore);
} catch (KeyManagementException | UnrecoverableKeyException | KeyStoreException | FileNotFoundException | NoSuchAlgorithmException | ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/******************************** https 双向认证2 end ***************************************/
使用:
if(httpsClient == null){
httpsClient = new AsyncHttpClient();
SSLSocketFactory sf = SSLCustomSocketFactory.getSocketFactory(); //单向认证
SSLSocketFactory sf = createSSLSocketFactory(); //双向认证1
SSLSocketFactory sf = getSslSocketFactory(App.getContext());//双向认证2
if(sf != null){
httpsClient.setSSLSocketFactory(sf);
}
HttpProtocolParams.setUseExpectContinue(httpsClient.getHttpClient().getParams(), false);
}
如果提供的证书是.truststore和.p12格式的,Android客户端不能直接使用.struststore,会报错:Wrong version of key store,需要转成.bks格式的。
client_xxx.p12, 客户端证书,用于请求的时候给服务器来验证客户端身份之用.
client_xxx.truststore, 客户端证书库,用于验证服务器端身份.
注: client_xxx.truststore需要转换为client_xxx.bks格式后才能使用.
使用Portecle工具进行转换, portecle-1.11.zip.
1. 启动工具cmd, 键入:
java -jar portecle.jar
2. 打开要转换的文件
3. 选择Tool->Change Keystore Type->Bks.
4. 步骤3提示成功后,点击保存,即转换为.bks格式文件, 保存为client.bks。
5. 同理,将p12文件转换为bks文件, 保存为verify_by_server.bks。
另,使用bcprov-jdk15on-159.jar 将.cer转成.bks:
C:\Users\Administrator>keytool -importcert -v -trustcacerts -alias server -file 需要转换的.cer -keystore 转换后要存放的.bks地址 -storetype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk15on-159.jar的本地地址 -storepass 123456