本文共 7440 字,大约阅读时间需要 24 分钟。
加载本地证书,准备用于SSL校验
/** * getSSLSocketFactory */ public SSLSocketFactory getSSLSocketFactory() { try { //本地证书流,实现本地证书的加载 InputStream certificate = AppManager.getContext().getAssets().open("root.cer"); //CertificateFactory 用来证书生成 CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); //创建一个包含可信ca的密钥存储库 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); String certificateAlias = Integer.toString(1); //读取本地证书,创建一个包含可信ca的密钥存储库 try { keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); certificate.close(); } catch (IOException e) { e.printStackTrace(); } //创建一个信任管理器,它信任密钥存储库中的ca TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //创建一个使用我们的TrustManager的SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{ x509TrustManager}, new SecureRandom()); return sslContext.getSocketFactory(); } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) { e.printStackTrace(); } return null; }
获取本地证书流
获取CertificateFactory,此处目的是: 生成一个实现指定证书类型的 CertificateFactory 对象。
用途:生成一个证书对象并使用从输入流 inStream 中读取的数据对它进行初始化,如下:
Certificate certificate1 = certificateFactory.generateCertificate(certificate);
KeyStore,表示密钥和证书的存储设施
创建一个包含可信ca的密钥存储库
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
使用给定 LoadStoreParameter 加载此 keystore
param - 指定如何存储 keystore 的 LoadStoreParameter,该参数可以为 nullKeyStore.LoadStoreParameter
用于 KeyStore load 和 store 参数的标记接口。keyStore.load(null);
setCertificateEntry(String alias, Certificate cert)
TrustManagerFactory,此类充当基于信任材料源的信任管理器的工厂
生成实现指定的信任管理算法的 TrustManagerFactory 对象。
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{ x509TrustManager}, new SecureRandom());
sslContext.getSocketFactory()
证书的校验
private X509TrustManager x509TrustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { //do nothing,接受任意客户端证书 LogUtils.i(TAG, "checkClientTrusted"); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { //do nothing,接受任意服务端证书 LogUtils.i(TAG, "checkServerTrusted"); //TODO:使用白名单对证书做验证 if (chain == null) { throw new IllegalArgumentException("check server X509Certificate is null"); } if (chain.length < 1) { throw new IllegalArgumentException("check server X509Certificate is empty"); } LogUtils.i(TAG, "chain =====" + chain.length); int errorNum = 0; //验证证书 for (X509Certificate cert : chain) { // LogUtils.i(TAG, "cert ===" + cert); // Make sure that it hasn't expired. cert.checkValidity(); try { PublicKey publicKey = getServerCert().getPublicKey(); // Verify the certificate's public key chain. cert.verify(publicKey); } catch (InvalidKeyException e) { LogUtils.e(TAG, "使用 AES 加密时,密钥大于128bit的话会抛出java.security.InvalidKeyException异常。因为密钥长度是受限的,所以长度超过时就会抛出这个异常"); e.printStackTrace();// throw new CertificateException(e); errorNum++; } catch (NoSuchAlgorithmException e) { LogUtils.e(TAG, "意思是导入证书有问题,或者是未寻找到证书."); e.printStackTrace();// throw new CertificateException(e); errorNum++; } catch (NoSuchProviderException e) { LogUtils.e(TAG, "NoSuchProviderException."); e.printStackTrace();// throw new CertificateException(e); errorNum++; } catch (SignatureException e) { LogUtils.e(TAG, "签名长度不正确."); e.printStackTrace();// throw new CertificateException(e); errorNum++; } } if (errorNum == chain.length){ LogUtils.i(TAG, "证书=========== 异常 "); throw new CertificateException(new SignatureException()); }else { LogUtils.i(TAG, "证书=========== 正常 "); } } @Override public X509Certificate[] getAcceptedIssuers() { LogUtils.i(TAG, "getAcceptedIssuers"); return new X509Certificate[0]; } }; private X509Certificate serverCert; private X509Certificate getServerCert() { if (serverCert == null) { try { InputStream inputStream = new BufferedInputStream(AppManager.getContext().getAssets().open("root.cer")); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); serverCert = (X509Certificate) certificateFactory.generateCertificate(inputStream); } catch (IOException | CertificateException e) { e.printStackTrace(); } } return serverCert; }
X509TrustManager
此接口的实例管理可用于对安全套接字的远程端进行身份验证的X509证书。决策可能基于受信任的证书颁发机构、证书撤销列表、在线状态检查或其他方法。
void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException
该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。
void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
X509Certificate[] getAcceptedIssuers()
返回受信任的X509证书数组。
使用
自己实现了信任管理器类,如何使用呢?类HttpsURLConnection似乎并没有提供方法设置信任管理器。其实,HttpsURLConnection通过SSLSocket来建立与HTTPS的安全连接,SSLSocket对象是由SSLSocketFactory生成的。
HttpsURLConnection提供了方法setSSLSocketFactory(SSLSocketFactory)设置它使用的SSLSocketFactory对象。
SSLSocketFactory通过SSLContext对象来获得,在初始化SSLContext对象时,可指定信任管理器对象。(第一部分,第9条)
Okhttp也提供了setSSLSocketFactory(SSLSocketFactory)…
第二部分代码解析
没有校验客户端证书,只是校验了服务器证书,解析服务器证书时,如果证书异常,会抛出异常,否则即使校验通过
检验证书是否有效
检查证书目前是否有效。即当前的日期和时间是否仍在证书中所给定的有效期内。
抛出:
certificateexpiredexception - 如果证书已过期。 certificatenotyetvalidexception - 如果证书不再有效。cert.checkValidity();
PublicKey publicKey = getServerCert().getPublicKey();// Verify the certificate's public key chain.cert.verify(publicKey);
getServerCert()
转载地址:http://ztvcz.baihongyu.com/