ssl协议安全基础(安卓下安全通信)(1)

安卓系统BKS证书的使用一、 概述

手机默认内置了主流的根证书,一般在使用OkHttp时,使用本地根证书进行校验,项目不需要内置CA根证书。

手机可以在设置停用某个根证书,这时与一些服务器通信,就会报ssl异常。用浏览器打开网页时,会提示 未授信的网站。

当使用自己制作的证书的时候,可以把证书放到项目里打包到APK中。

二、keytool支持BKS的设置

安卓通用使用的是BKS格式的证书。但JDK默认情况下不支持BKS证书格式,如果要使用BKS需要进行以下操作:

  1. 下载 org.bouncycastle.jce.provider.BouncyCastleProvider 包,网址:https://www.bouncycastle.org
  2. 把下载的 bcprov-ext-jdk15to18-166.jar , bcprov-jdk15to18-166.jar 放到 %JAVA_HOME%/jre/lib/ext 下。(我也不知道为啥我下载有两个包)
  3. 修改%JAVA_HOME%/jre/lib/security/java.security文件我的配置是这样的:

jdk.TLS.disabledAlgorithms= security.provider.1=sun.security.provider.Sun security.provider.2=sun.security.rsa.SunRsaSign security.provider.3=sun.security.ec.SunEC security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=com.sun.security.sasl.Provider security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI security.provider.9=sun.security.smartcardio.SunPCSC security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider

三、cer转换为bks格式1. 格式转换命令

keytool -importcert -trustcacerts -keystore cNetty.bks -file cNetty.cer -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider # 按提示输入密钥库指令 keytool -importcert -trustcacerts -keystore sNetty.bks -file sNetty.cer -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider # 按提示输入密钥库指令

2. 导入信任证书库

这时要把证书导入对方信任证书的仓库中,使用命令:

keytool -import -alias securenettyclient_bks -file cNetty.cer -storepass 你的storepass密码 -keystore sNetty.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider keytool -import -alias securenettyservice_bks -file sNetty.cer -storepass 你的storepass密码 -keystore cNetty.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

3. 查看bks证书

# 查看证书链 keytool -list -rfc -keystore cNetty.bks -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -storepass 你的storepass证书 # 验证BKS keytool -list -v -storetype BKS -keystore sNetty.bks

四、java socket使用bks证书的核心客户端代码

以下代码客户端支持使用jks和bks两种格式、支持windows和android端使用,可以用在Netty框架:

import lombok.extern.slf4j.Slf4j; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.security.*; import java.security.cert.CertificateException; /** * 加密通讯,获取SSLContext的方法 * @author Cade */ @Slf4j public final class SecureNettySslContextfactory { private static final String PROTOCOL = "TLS"; private static final String FILE_JKS ="jks"; private static final String KEYSTORE_BKS ="BKS"; private static final String KEYSTORE_JKS ="JKS"; /** * 服务器安全上下文 */ private static SSLContext SERVER_CONTEXT; /** * 客户端安全上下文 */ private static SSLContext CLIENT_CONTEXT; /** * * @param pkPath pkPath * @param caPath caPath * @param jksPass jksPass * @param storePass storePass * @return SSL上下文 */ public static SSLContext getClientContext(String pkPath,String caPath,String jksPass,String storePass){ if(CLIENT_CONTEXT!=null) { return CLIENT_CONTEXT; } final String osAndroid="android"; KeyManagerFactory kmf; TrustManagerFactory tf; KeyStore ks; KeyStore tks ; try { if(caPath.contains(FILE_JKS)) { ks = KeyStore.getInstance(KEYSTORE_JKS); tks = KeyStore.getInstance(KEYSTORE_JKS); }else{ Security.addProvider(new BouncyCastleProvider()); ks = KeyStore.getInstance(KEYSTORE_BKS); tks = KeyStore.getInstance(KEYSTORE_BKS); } // 如果是安卓系统,读取资源的方式有所不同 if (osAndroid.equals(Constants.OS)) { try (InputStream in = Constants.class.getResourceAsStream(pkPath)) { ks.load(in, jksPass.toCharArray()); } try(InputStream in = Constants.class.getResourceAsStream(caPath)){ tks.load(in, jksPass.toCharArray()); } } else { try (InputStream stream = Files.newInputStream(Paths.get(pkPath))) { ks.load(stream, jksPass.toCharArray()); } try (InputStream stream = Files.newInputStream(Paths.get(caPath))) { tks.load(stream, jksPass.toCharArray()); } } if(log.isInfoEnabled()) { log.info(TrustManagerFactory.getDefaultAlgorithm()); } kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, storePass.toCharArray()); tf = TrustManagerFactory.getInstance("SunX509"); tf.init(tks); CLIENT_CONTEXT = SSLContext.getInstance(PROTOCOL); //初始化此上下文 //参数一:认证的密钥 参数二:对等信任认证 参数三:伪随机数生成器 。 由于单向认证,服务端不用验证客户端,所以第二个参数为null CLIENT_CONTEXT.init(kmf.getKeyManagers(), tf.getTrustManagers(), null); }catch (IOException | KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | KeyManagementException e){ if(log.isErrorEnabled()){ log.error(e.toString()); } } return CLIENT_CONTEXT; } }

代码里Constants类是自定义的常量类。

五、OKHttp使用证书

bks一般可以放在assets下。

public class MyOkhttpClient { private static MyOkhttpClient singleton; public static OkHttpClient getInstance() { if (singleton == null) { synchronized (CBOkhttpClient.class) { if (singleton == null) { OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); builder.connectTimeout(50000, TimeUnit.MILLISECONDS); builder.writeTimeout(50000, TimeUnit.MILLISECONDS); builder.readTimeout(50000, TimeUnit.MILLISECONDS); try { SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore trustStore = KeyStore.getInstance("BKS"); InputStream ksinstream = CBFramework.getApplication().getResources().openRawResource(R.raw.cbframework_trustcerts); trustStore.load(ksinstream, "".toCharArray()); ksinstream.close(); trustManagerFactory.init(trustStore); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); builder.sslSocketFactory(sslContext.getSocketFactory(), new 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[0]; } }); } catch (Exception e) { Log.e(e); } singleton = builder.build(); } } } return singleton; } }

,