Java实现7种常见密码算法( 六 )

读取PKCS12密钥库文件读取PKCS12规范的密钥库文件,可使用KeyStore类,如下:
public static void testPkcs12File() {KeyStore keyStore = KeyStore.getInstance("PKCS12");InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("cert/keystore.p12");char[] password = "123456".toCharArray();keyStore.load(is, password);//获取证书X509Certificate x509Certificate = (X509Certificate)keyStore.getCertificate("demo");System.out.println("X509Certificate: ");System.out.printf("SubjectDN: %s \n", x509Certificate.getSubjectDN());System.out.printf("IssuerDN: %s \n", x509Certificate.getIssuerDN());System.out.printf("SigAlgName: %s \n", x509Certificate.getSigAlgName());System.out.printf("Signature: %s \n", Hex.encodeHexString(x509Certificate.getSignature()));System.out.printf("PublicKey: %s \n", x509Certificate.getPublicKey());//获取私钥Key key = keyStore.getKey("demo", password);System.out.printf("PrivateKey: %s \n", key);}如果要读取.jks文件,只需要将KeyStore.getInstance("PKCS12")中的PKCS12更换为JKS即可 , 其它部分保持不变,不过由于JKS是java专有格式 , 目前java也不推荐使用了,所以能不用的话,就尽量不要用了 。
常见问题证书信任问题证书的绝大多数应用场景是Https协议 , 但在访问https接口时,有时会由于证书信任问题导致https握手失败 , 主要有以下2点原因:

  1. 有些公司会自建CA,使用自签证书,如早期的12306,而jdk只信任它预置的根证书,所以https握手时这种证书会认证失败 。
  2. 新成立的根CA机构证书 , 没预置在旧的jdk里面,导致这些CA机构签发的证书不被信任 。
要解决这种证书信任问题 , 有两种方法 , 如下:1. 将证书导致到jdk的预置证书库中
# 将cert.crt导入jdk预置密钥库文件 , 密钥库文件密码默认是changeitsudo keytool -importcert -file cert.crt -alias demo -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit# 查看密钥库文件,检查是否导入成功keytool -list -v -alias demo -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit2. 以编码的方式信任证书以jdk自带的https sdk为例,可在代码中手动将问题证书添加到信任列表中,如下:
public String testReqHttpsTrustCert() throws Exception {// 读取jdk预置证书KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());try(InputStream ksIs = new FileInputStream(System.getProperty("java.home") + "/lib/security/cacerts")) {keyStore.load(ksIs, "changeit".toCharArray());}// 读取证书文件CertificateFactory cf = CertificateFactory.getInstance("X.509");try(InputStream certIs = this.getClass().getResourceAsStream("/cert/cert.crt")) {Certificate c = cf.generateCertificate(certIs);keyStore.setCertificateEntry("demo", c);}// 生成信任管理器TrustManagerFactory tmFact = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());tmFact.init(keyStore);// 生成SSLSocketFactorySSLContext sslContext = SSLContext.getInstance("TLSv1.2");sslContext.init(null, tmFact.getTrustManagers(), new SecureRandom());SSLSocketFactory ssf = sslContext.getSocketFactory();// 发送https请求URL url = new URL("https://www.demo.com/user/list");HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();connection.setHostnameVerifier((hostname, session) -> hostname.endsWith("demo.com"));connection.setSSLSocketFactory(ssf);String result;try(InputStream inputStream = connection.getInputStream()){result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);}connection.disconnect();return result;}
注:虽然2种方法都可以解决问题,但第1种方法使得java程序对环境形成了依赖 , 一旦部署环境发生变化,java程序可能就报错了,因此更推荐使用第2种方法 。
总结到这里,JCA相关类的使用就介绍完了,如下表格中总结了JCA的常用类:
Java实现7种常见密码算法

文章插图
本篇花了近一周时间整理 , 内容较多,对这块不太熟悉的同学,可以先关注收藏起来当示例手册,待需要时再参阅即可 。
【Java实现7种常见密码算法】

推荐阅读