工作需要使用到 Android 加密传输(SSL),双向认证 ,因为未使用过,所以搜索了总结了一下相关知识,并整理作为笔记
参考博客 :Retrofit 2.0 详解(二)加载https请求(转)
Android实现ssl双向验证
搜索的时候关于SSL+http 的解释有很多,不做细说,本文只记录如何实现,包括证书制作、myeclipse服务部署以及Android端代码。
证书制作
打开cmd命令窗口
1、生成客户端keystore
keytool -genkeypair -alias E:\sslhttpTest\keystool\client -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore E:\sslhttpTest\keystool\client.jks
注:此处 E:\sslhttpTest\keystool\client 是本人手误 只需输入 client 即可,alias 无需路径
2、生成服务端keystore
keytool -genkeypair -alias server -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore E:\sslhttpTest\keystool\server.keystore
3、导出客户端证书
keytool -export -alias E:\sslhttpTest\keystool\client -file E:\sslhttpTest\keystool\client.cer -keystore E:\sslhttpTest\keystool\client.jks -storepass 123456
4、导出服务端证书
keytool -export -alias server -file E:\sslhttpTest\keystool\server.cer -keystore E:\sslhttpTest\keystool\server.keystore -storepass 123456
5、重点:证书交换
将客户端证书导入服务端keystore中,再将服务端证书导入客户端keystore中, 一个keystore可以导入多个证书,生成证书列表。
生成客户端信任证书库(由服务端证书生成的证书库):
keytool -import -v -alias server -file E:\sslhttpTest\keystool\server.cer -keystore E:\sslhttpTest\keystool\truststore_s_for_c.jks -storepass 123456
将客户端证书导入到服务器证书库(使得服务器信任客户端证书):
keytool -import -v -alias E:\sslhttpTest\keystool\client -file E:\sslhttpTest\keystool\client.cer -keystore E:\sslhttpTest\keystool\client_for_server.keystore -storepass 123456
6、生成Android识别的BKS库文件
运行protecle.jar将client.jks和truststore_s_for_c.jks分别转换成client.bks和truststore_s_for_c.bks ,然后放到android客户端的assert目录下
File -> open Keystore File -> 选择证书库文件 -> 输入密码 -> Tools -> change keystore type -> BKS -> save keystore as -> 保存即可
7、配置Tomcat服务器(可选)
找到Tomcat安装目录 conf\server.xml文件,配置8443端口
注:
可以不配置tomcat ,只需配置 eclipse 的工程目录中的对应的server.xml文件 E:\eclipsework\.metadata\.me_tcat7\conf\server.xml(我的工程目录在E:\eclipsework 根据实际情况找路径) 。如果 eclipsework\.metadata\.me_tcat7 的目录下没有 conf 目录,需要新建conf文件夹然后将 tomcat 安装目录 conf\server.xml文件复制过来,然后启动myeclipse的服务,会报错 根据报错删除
即可,配置 server.xml 的端口
Android 端配置
SSLHelper
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class SSLHelper {
private static final String TAG = "SSLHelper";
private final static String CLIENT_PRI_KEY = "client";
private final static String TRUSTSTORE_PUB_KEY = "truststore_s_for_c";
private final static String CLIENT_BKS_PASSWORD = "123456";
private final static String TRUSTSTORE_BKS_PASSWORD = "123456";
private final static String KEYSTORE_TYPE = "BKS";
private final static String PROTOCOL_TYPE = "TLS";
private final static String CERTIFICATE_FORMAT = "X509";
public static SSLSocketFactory getSSLCertifcation(Context context) {
SSLSocketFactory sslSocketFactory = null;
try { // 服务器端需要验证的客户端证书,其实就是客户端的keystore
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);// 客户端信任的服务器端证书
KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);//读取证书
InputStream ksIn = context.getAssets().open(CLIENT_PRI_KEY);
InputStream tsIn = context.getAssets().open(TRUSTSTORE_PUB_KEY);//加载证书
keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray());
trustStore.load(tsIn, TRUSTSTORE_BKS_PASSWORD.toCharArray());
ksIn.close();
tsIn.close(); //初始化
SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_FORMAT);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_FORMAT);
trustManagerFactory.init(trustStore);
keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return sslSocketFactory;
}
}
UnSafeTrustManager
public class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
Retrofit 初始化
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//创建okhttp
OkHttpClient httpClient = new OkHttpClient().newBuilder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(30, TimeUnit.SECONDS)
.sslSocketFactory(SSLHelper.getSSLCertifcation(context), new UnSafeTrustManager())
.hostnameVerifier(new HttpsUtil.UnSafeHostnameVerifier())//由于还没有域名,此处设置忽略掉域名校验
.build();
retrofit=new Retrofit.Builder()
.baseUrl(url)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
所有代码可以在百度网盘下载
密码:on6o
目录说明:keystool.zip 测试证书压缩包
LoginSSL.zip 测试后台压缩包
SSLTest.zip 测试Android 压缩包 ,使用时请替换 MainActivity中的 url 地址的 ip 修改为测试的IP地址