APP和服务器的安全十分重要,服务器端https证书可以防止钓鱼网站假冒服务器和客户端通信,盗取用户帐号信息和骗财。客户端https证书可以防止别人假冒客户端破解服务器端通信协议,盗取服务器端数据,比如爬虫就十分讨厌。双向验证,如果再对通信的核心字段使用RSA或者DES/AES加密,就可以保证APP和服务足够安全,并能防御各种代理攻击。
package com.xxx.api;
import android.content.Context;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okio.Buffer;
/**
* Created by jiazhiguo([email protected]) on 2018/5/29.
*/
public class SslSocketFactoryHelp {
/**
* 1.双向验证客户端包括两个证书,一个是客户端私钥,一个是服务器端公钥,服务器端也有两个证书,
* 一个是服务器私钥,一个是客户端公钥
* 2.客户端私钥服务器用来验证客户端合法性,客户端公钥用来验证服务器合法性,
* 3.客户端严格的双向两个证书都不可少,但客户端对于服务器的验证取决于客户端,理论上可以跳过或者全部信任,
* 这时只需要发送客户端私有证书加密数据供服务检验即可。
* 4.服务器端严格的双向同样是两个证书都不可少,但公共服务器一般不对客户端证书做严格校验,可以支持
* 客户端使用X509TrustManager空证书或者默认证书请求服务,如开放的HTTPS网站,此时https只用来让客户端检测
* 服务器是否假冒的钓鱼网站,这里是严格双向校验,有单向检验接口
*/
/**
* 请求地址
*/
public static final String NEW_ROOT = "https://xxx.xxx.com";
/**
* 备用请求地址
*/
public static final String NEW_ROOT_BAK = "https://xxx01.xxx.com";
/**
* 服务器信任库公钥证书类型,android只支持BKS,java支持jKS,cer等
*/
public static final String KEY_STORE_TRUST_TYPE_BKS = "BKS";
/**
* 客户端证书类型,android支持PKCS12,java支持jKS,cer,p12等
*/
public static final String KEY_STORE_CLIENT_TYPE_P12 = "PKCS12";
/**
* 客户端要给服务器端认证的证书位置,也即私有证书位置,服务器通过该证书对客户端鉴权assets
*/
public static final String KEY_STORE_CLIENT_PATH = "xxxx.png";
/**
* 客户端验证服务器端的证书库位置,也即公有证书位置,客户端通过该证书对服务器端是否假冒检测assets
*/
public static final String KEY_STORE_TRUST_PATH = "server.bks";
/**
* 客户端证书密码
*/
public static final String KEY_STORE_PASSWORD = "xxxxxx";
/**
* 服务器端证书库密码,可以为空
*/
public static final String KEY_STORE_TRUST_PASSWORD = "xxxxx";
/**
* 客户端要给服务器端认证的证书位置,也即私有证书位置,服务器通过该证书对客户端鉴权assets
*/
public static final int KEY_STORE_RAW_CLIENT_PATH = R.raw.toc_picture;
/**
* 客户端验证服务器端的证书库位置,也即公有证书位置,客户端通过该证书对服务器端是否假冒检测assets
*/
public static final int KEY_STORE_RAW_TRUST_PATH = R.raw.server;
/**
* 服务器端证书编码
*/
public static final String TRUST_BKS = "-----BEGIN CERTIFICATE-----\n" +
"LnpodWlzaHVzaGVucWkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2AODF0y\n" +
"cVVecUVavn6XDDQ7wimgzNlI7Td5vG7Ka/qs5cR/zGxNbLuPdGYVff2m0I2v4bL/TOJtmLuxXFJX\n" +
"6jU9x8yQAnPCF4eec3Skvih+riR/omtlA3+W6sYZkehad8x9ESQ5LQ6674re4eccFpyrf17XrFpV\n" +
"AwIrxsdm47F4m5MHugdXEehtKeQxpDEgku7TGrkeMkLcCysfioSLwGisd5NKlrxDIGKybfvtFf5n\n" +
"ARQEjQLVswdMIqKQ2wAeEnxSF2WEbgZdkQSbn1S7PU/kprNJ+sWJdwfbPUn2Rc3/pKc4qNIRhDUY\n" +
"JlAY/gmgwl6iWgzGyURHmqaTTDEYmdVY8ODNHf/tPQdoFXz99O0FuPbUmpX8ELnTnjVpGQCkMfQJ\n" +
"reOQcTGPy8U16ZA+jk36MMtL5sY=\n" +
"-----END CERTIFICATE-----";
/**
* 默认服务器端证书,用于不需要对服务器端验证的情况
*
* @return 服务器端证书
*/
private static TrustManager loadtrustAllCertsManage() {
TrustManager trustAllCerts = new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
};
return trustAllCerts;
}
/**
* 通过证书编码生成证书书
*
* @param trustStore_utf8 服务器端证书BASE64编码
* @return 服务器端证书
*/
private static KeyStore loadByteTrustStore(String trustStore_utf8) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
KeyStore TrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
TrustStore.load(null, null);
TrustStore.setCertificateEntry("ca", cf.generateCertificate(
new Buffer().writeUtf8(trustStore_utf8)
.inputStream()));
return TrustStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 加载raw资源类服务器端证书
*
* @param context 上下文
* @param truststore_type 证书类型
* @param truststore_id 证书名字
* @param trust_pswd 证书密码
* @return
*/
private static KeyStore loadTrustStore(Context context, String truststore_type, int truststore_id, String trust_pswd) {
try {
KeyStore localTrustStore = KeyStore.getInstance(truststore_type);
InputStream in = context.getResources().openRawResource(truststore_id);
;
try {
localTrustStore.load(in, trust_pswd.toCharArray());
} finally {
in.close();
}
return localTrustStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 加载asserts资源类服务器证书
* @param context 上下文
* @param truststore_type 证书类型,p12, bks,jks,cer等
* @param truststore_assets assets资源的证书名字
* @param trust_pswd 证书密码
* @return
*/
private static KeyStore loadAssetsTrustStore(Context context, String truststore_type, String truststore_assets, String trust_pswd) {
try {
KeyStore localTrustStore = KeyStore.getInstance(truststore_type);
InputStream in = context.getResources().getAssets().open(truststore_assets);
;
try {
localTrustStore.load(in, trust_pswd.toCharArray());
} finally {
in.close();
}
return localTrustStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 加载本地证书
* @param context 上下文
* @param keystore_type 证书类型,p12, bks,jks,cer等
* @param keystore_raw_id raw资源的证书资源ID
* @param keystore_pswd 证书密码
* @return
*/
private static KeyStore loadKeyStore(Context context, String keystore_type, int keystore_raw_id, String keystore_pswd) {
try {
KeyStore keyStore = null;
keyStore = KeyStore.getInstance(keystore_type);
InputStream in = context.getResources().openRawResource(keystore_raw_id);
try {
keyStore.load(in, keystore_pswd.toCharArray());
} finally {
in.close();
}
return keyStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 加载asserts类型客户端证书
* @param context 上下文
* @param keystore_type 证书类型,p12, bks,jks,cer等
* @param keystore_raw_id raw资源的证书资源ID
* @param keystore_pswd 证书密码
* @return
*/
private static KeyStore loadAssetsKeyStore(Context context, String keystore_type, String keystore_raw_id, String keystore_pswd) {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(keystore_type);
InputStream in = context.getResources().getAssets().open(keystore_raw_id);
try {
keyStore.load(in, keystore_pswd.toCharArray());
} finally {
in.close();
}
return keyStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 通过服务器端证书keystore和客户端证书truststore生成SSLContext
*
* @param keyStore 客户端证书
* @param trustStore 服务器端证书
* @param key_pswd 客户端证书密码
* @return SSLContext
* @throws GeneralSecurityException
*/
private static SSLContext createSslContext(KeyStore keyStore, KeyStore trustStore, String key_pswd)
throws GeneralSecurityException {
TrustManager tms = new MyTrustManager(trustStore);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, key_pswd.toCharArray());
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(kmf.getKeyManagers(), new TrustManager[]{tms}, new SecureRandom());
return sslcontext;
}
/**
* 通过证书信息生成SSLContext
*
* @param context 上下语言
* @param keystore_type 证书类型,p12, bks,jks,cer等
* @param keystore_raw_id 客户端raw资源的证书资源ID
* @param keystore_pswd 客户端证书密码
* @param truststore_type 服务器端证书类型bks等
* @param truststore_id 服务器端raw资源的证书资源ID
* @param trust_pswd 服务器端证书密码
* @return SSLContext
* @throws GeneralSecurityException
*/
private static SSLContext createSslContext(Context context, String keystore_type, int keystore_raw_id, String keystore_pswd,
String truststore_type, int truststore_id, String trust_pswd)
throws GeneralSecurityException {
KeyStore trustStore = loadTrustStore(context, keystore_type, truststore_id, trust_pswd);
KeyStore keyStore = loadKeyStore(context, truststore_type, keystore_raw_id, keystore_pswd);
return createSslContext(keyStore, trustStore, keystore_pswd);
}
/**
* 通过客户端证书消息和服务器端证书的BASE64信息生成SSLContext
*
* @param context 上下文
* @param keystore_type 证书类型,p12, bks,jks,cer等
* @param keystore_raw_id 客户端raw资源的证书资源ID
* @param keystore_pswd 客户端证书密码
* @param trust_utf8 服务器端
* @return
* @throws GeneralSecurityException
*/
private static SSLContext createSslContext(Context context, String keystore_type, int keystore_raw_id, String keystore_pswd,
String trust_utf8)
throws GeneralSecurityException {
KeyStore trustStore = loadByteTrustStore(trust_utf8);
KeyStore keyStore = loadKeyStore(context, keystore_type, keystore_raw_id, keystore_pswd);
return createSslContext(keyStore, trustStore, keystore_pswd);
}
/**
* 通过客户羰证书和空trust证书生成SSLContext
*
* @param context 上下文
* @param keystore_type 证书类型,p12, bks,jks,cer等
* @param keystore_raw_id 客户端raw资源的证书资源ID
* @param keystore_pswd 客户端证书密码
* @return
* @throws GeneralSecurityException
*/
private static SSLContext createSslContext(Context context, String keystore_type, int keystore_raw_id, String keystore_pswd)
throws GeneralSecurityException {
TrustManager tms = loadtrustAllCertsManage();
KeyStore keyStore = loadKeyStore(context, keystore_type, keystore_raw_id, keystore_pswd);
KeyManagerFactory kmf = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keystore_pswd.toCharArray());
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(kmf.getKeyManagers(), new TrustManager[]{tms}, new SecureRandom());
return sslcontext;
}
/**
* 通过客户端和服务器端证书信息生成SSLSocketFactory
*
* @param context 上下文
* @return SSLSocketFactory
* @throws GeneralSecurityException
*/
public static SSLSocketFactory getSSLSocketFactory(Context context) throws GeneralSecurityException {
return createSslContext(context, KEY_STORE_CLIENT_TYPE_P12, KEY_STORE_RAW_CLIENT_PATH, KEY_STORE_PASSWORD,
KEY_STORE_TRUST_TYPE_BKS, KEY_STORE_RAW_TRUST_PATH, KEY_STORE_TRUST_PASSWORD).getSocketFactory();
}
/**
* 通过服务器端base64和客户端证书信息生成SSLSocketFactory
*
* @param context 上下文
* @return SSLSocketFactory
* @throws GeneralSecurityException
*/
public static SSLSocketFactory getUtf8SSLSocketFactory(Context context) throws GeneralSecurityException {
return createSslContext(context, KEY_STORE_CLIENT_TYPE_P12, KEY_STORE_RAW_CLIENT_PATH, KEY_STORE_PASSWORD,
TRUST_BKS).getSocketFactory();
}
/**
* 生成对服务器端免除验证的SSLSocketFactory
*
* @param context 上下文
* @return SSLSocketFactory
* @throws GeneralSecurityException
*/
public static SSLSocketFactory getTrustAllSSLSocketFactory(Context context) throws GeneralSecurityException {
return createSslContext(context, KEY_STORE_CLIENT_TYPE_P12, KEY_STORE_RAW_CLIENT_PATH, KEY_STORE_PASSWORD)
.getSocketFactory();
}
/**
* 服务器端证书类
*/
public static class MyTrustManager implements X509TrustManager {
/**
* 服务器证书
*/
class LocalStoreX509TrustManager implements X509TrustManager {
private X509TrustManager trustManager;
LocalStoreX509TrustManager(KeyStore localTrustStore) {
try {
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(localTrustStore);
trustManager = findX509TrustManager(tmf);
if (trustManager == null) {
throw new IllegalStateException(
"Couldn't find X509TrustManager");
}
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
trustManager.checkServerTrusted(chain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
}
/**
* 系统默认证书
*/
static X509TrustManager findX509TrustManager(TrustManagerFactory tmf) {
TrustManager tms[] = tmf.getTrustManagers();
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
return (X509TrustManager) tms[i];
}
}
return null;
}
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
private X509Certificate[] acceptedIssuers;
/**
* 使用系统默认证书,失败时使用添加的证书
*
* @param localKeyStore
*/
private MyTrustManager(KeyStore localKeyStore) {
try {
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
defaultTrustManager = findX509TrustManager(tmf);
if (defaultTrustManager == null) {
throw new IllegalStateException(
"Couldn't find X509TrustManager");
}
localTrustManager = new MyTrustManager.LocalStoreX509TrustManager(localKeyStore);
List allIssuers = new ArrayList();
for (X509Certificate cert : defaultTrustManager
.getAcceptedIssuers()) {
allIssuers.add(cert);
}
for (X509Certificate cert : localTrustManager.getAcceptedIssuers()) {
allIssuers.add(cert);
}
acceptedIssuers = allIssuers.toArray(new X509Certificate[allIssuers
.size()]);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkClientTrusted(chain, authType);
}
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
}
}