您的位置:首页 > 移动开发 > Android开发

Android实现ssl双向验证

2018-02-24 21:13 344 查看

Android实现双向验证

前言

Android端实现双向认证的难点主要在于Android接受的证书格式是BKS,而一般提供的证书不包含此格式,需要手动转换

转换之后如果请求不了,表现为握手失败(Handshake failed),则一般是证书转换错误

网上有一大堆转换方法,有的虽能正常生成BKS证书,但还是导致握手失败,推测应该还是转换方式有误

在这里提供一种自己亲自试验过能正常转换的方法供大家参考

证书转换

使用转换工具

在这里提供一个比较好用的转换工具Portecle

下载完后解压双击start.bat即可使用,至于详细使用请自行百度

使用此工具可以很方便地将jks转为bks

通过openssl和keytool转换

openssl可以到官网下载,openssl暂时没有windows版,大伙可以在此处下载Win32OpenSSLwindows版本

下载完记得配置环境变量,同时也记得配置jdk的环境变量,因为后面需要用到keytool命令

现在假设你拿到了客户端证书client.crt和client.key(或者client.pem和client.key)

通过以下命令生成pfx文件,期间会让输入密码,输入你想要的密码即可,假如这里输入的是123456

openssl pkcs12 -export -out client.pfx -inkey client.key -in client.pem

或者

openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt

执行成功则可以看到client.pfx文件,接着再执行如下命令

keytool -importkeystore -srckeystore client.pfx -destkeystore client.jks -srcstoretype PKCS12 -deststoretype JKS

执行成功即可看到生成了client.jks

拿到生成的jks就可以通过Portecle工具转换成bks了

代码实现

SSLHelper.java

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.bks";
private final static String TRUSTSTORE_PUB_KEY = "ca.bks";
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_STANDARD = "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();

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_STANDARD);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_STANDARD);
trustManagerFactory.init(trustStore);
keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());

//初始化SSLContext
SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new java.security.SecureRandom());

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


HttpsUtil.java

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 java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class HttpsUtil {

public static class SSLParams
{
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}

public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password)
{
SSLParams sslParams = new SSLParams();
try
{
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (trustManagers != null)
{
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else
{
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager},null);
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = trustManager;
return sslParams;
} catch (NoSuchAlgorithmException e)
{
throw new AssertionError(e);
} catch (KeyManagementException e)
{
throw new AssertionError(e);
} catch (KeyStoreException e)
{
throw new AssertionError(e);
}
}

public static class  UnSafeHostnameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
}

public static 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[]{};
}
}

private static TrustManager[] prepareTrustManager(InputStream... certificates)
{
if (certificates == null || certificates.length <= 0) return null;
try
{

CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates)
{
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try
{
if (certificate != null)
certificate.close();
} catch (IOException e)

{
}
}
TrustManagerFactory trustManagerFactory = null;

trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

return trustManagers;
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
} catch (CertificateException e)
{
e.printStackTrace();
} catch (KeyStoreException e)
{
e.printStackTrace();
} catch (Exception e)
{
e.printStackTrace();
}
return null;

}

private static KeyManager[] prepareKeyManager(InputStream bksFile, String password)
{
try
{
if (bksFile == null || password == null) return null;

KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();

} catch (KeyStoreException e)
{
e.printStackTrace();
} catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
} catch (UnrecoverableKeyException e)
{
e.printStackTrace();
} catch (CertificateException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}

private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers)
{
for (TrustManager trustManager : trustManagers)
{
if (trustManager instanceof X509TrustManager)
{
return (X509TrustManager) trustManager;
}
}
return null;
}

private static class MyTrustManager implements X509TrustManager
{
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;

public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException
{
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
{

}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
{
try
{
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce)
{
localTrustManager.checkServerTrusted(chain, authType);
}
}

@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}

}


retrofit调用

//创建日志拦截器,用于日志打印
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//创建okhttp
OkHttpClient httpClient = new OkHttpClient().newBuilder()
.addInterceptor(interceptor)
.sslSocketFactory(SSLHelper.getSSLCertifcation(this), new HttpsUtil.UnSafeTrustManager())
.hostnameVerifier(new HttpsUtil.UnSafeHostnameVerifier())//由于还没有域名,此处设置忽略掉域名校验
.build();
//创建retrofit
String baseUrl = "https://180.101.147.89:8743/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(httpClient)
//.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();


顺便贴下依赖build.gradle

apply plugin: 'com.android.application'

android {
...
}
dependencies {
...

//retrofit
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:converter-scalars:2.3.0"
implementation "com.squareup.retrofit2:converter-gson:2.3.0"
implementation "com.squareup.okhttp3:logging-interceptor:3.8.1"
}


参考

http://blog.csdn.net/zhangyong125/article/details/50402183
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: