关于Android双向认证
作者:时光 date:2015-05-26
遇到一个在Android上实现https双向认证的需求,之前没有做过,故在网上查阅。发现网上的Demo大体分为两种办法:
- 信任所有主机证书
- 使用证书认证(自签名)
第一种方法,不安全,故在此不做讨论,主要记录一下第二种的实现。
在操作之前,我们先了解一下https的原理和运行过程。
Android Https详解
那么,我们可以知道,在Android中,我们需要两个东东来进行https的验证:
- 客户端要给服务器端认证的证书(xxx.p12)
- 客户端验证服务器端的证书库(xxx.keystore PS:此处的证书库在Android中只支持BKS模式)
那么,看一下我们现在已知的材料:
- 客户端证书 client.p12
- 服务端证书 service.cer
PS:如果你什么都没有,教程在这:Android HTTPS SSL双向验证
那么,客户端证书,已经有了,现在就差客户端验证服务器端的证书库了。
如果是客户端来验证服务端,那么我们需要将服务器端的证书来导入到keystore。要生成keystore,我们需要用到java中自带的 keytool 这个工具。
但是java中自带的keytool只能生成JKS模式的keystore,而Android中需要的模式是BKS,因此我们还需要另外一个工具来进行转化:BouncyCastle
在官网上下载和你的jdk版本对应的jar包,比如我的jdk版本是1.7,就下载了bcprov-jdk15on-152.jar。
下载完成以后,把jar包放到jdk和jre下的lib\ext下,并且在对应的lib\security\java.security中添加如下一段:security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
好的,准备工作完成,下面开始实际操作:
- 在D盘下创建文件夹ssl(PS:以此举例,不必非要如此),
- 将我们的 服务端证书 service.cer 和下载的bcprov-jdk15on-152.jar放进来
- 管理员运行CMD
输入一下代码:
keytool -import -v -alias server -file D:\ssl\service.cer
(-import 引入证书文件,-alias server 起别名, -file D:\ssl\service.cer 引入的文件)
-keystore D:\ssl\client.truststore
(-keystore 生成keystore, D:\ssl\client.truststore生成的文件的路径名)
-provider org.bouncycastle.jce.provider.BouncyCastleProvider
(使用此包)
-providerpath “bcprov-jdk15on-152.jar”
(使用包的路径名)
-storetype BKS
(storetype BKS 将由JKS转化为BKS模式)
-storepass 123456
(-storepass keystore的密码 此处密码长度应 <=7 )
运行,就会得到client.truststore,即是我们需要的客户端验证服务器端的证书库。
那么,有了这两个东东,我们就可以继续在Android中进行https操作了。 这里我们使用HttpClient来进行示例:
1.首先我们做一个生成SSLSocketFactory的工具:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Enumeration;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.conn.ssl.SSLSocketFactory;
import android.content.Context;
public class HttpsUtils {
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.truststore";// 客户端验证服务器端的证书库
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 createSSLSocketFactory(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;
}
}
2.在请求之前配置下Scheme:
Scheme scheme_forHttps = new Scheme("https", HttpsUtils.createSSLSocketFactory(context), 443); (PS:此处的端口号https默认是443,如果需要,请改成你所需的服务端的https接口)
SchemeRegistry registry = new SchemeRegistry();
registry.register(scheme_forHttps);
.....
好了,至此,Android上的双向认证就可以使用啦。 Happy Coding.