概述
package com.taikang.iot.re.security;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import javax.net.ssl.*;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;/**
* @Author: chengsh05
* @Date: 2019/3/1 17:51*/
public classSSLUtil {private static Logger logger = Logger.getLogger(OpensslHelper.class);/**
* 利用开源的工具类解析openssl私钥,openssl私钥文件格式为pem,需要去除页眉页脚后才能被java读取
*
* @param file
* @return*/
public staticPrivateKey getPrivateKey(File file) {if (file == null) {return null;
}
PrivateKey privKey= null;
PemReader pemReader= null;try{
pemReader= new PemReader(newFileReader(file));
PemObject pemObject=pemReader.readPemObject();byte[] pemContent =pemObject.getContent();//支持从PKCS#1或PKCS#8 格式的私钥文件中提取私钥
if (pemObject.getType().endsWith("RSA PRIVATE KEY")) {/** 取得私钥 for PKCS#1
* openssl genrsa 默认生成的私钥就是PKCS1的编码*/RSAPrivateKey asn1PrivKey=RSAPrivateKey.getInstance(pemContent);
RSAPrivateKeySpec rsaPrivKeySpec= newRSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
KeyFactory keyFactory= KeyFactory.getInstance("rsa");
privKey=keyFactory.generatePrivate(rsaPrivKeySpec);
}else if (pemObject.getType().endsWith("PRIVATE KEY")) {/** 通过openssl pkcs8 -topk8转换为pkcs8,例如(-nocrypt不做额外加密操作):
* openssl pkcs8 -topk8 -in pri.key -out pri8.key -nocrypt
*
* 取得私钥 for PKCS#8*/PKCS8EncodedKeySpec privKeySpec= newPKCS8EncodedKeySpec(pemContent);
KeyFactory kf= KeyFactory.getInstance("rsa");
privKey=kf.generatePrivate(privKeySpec);
}
}catch(FileNotFoundException e) {
logger.error("read private key fail,the reason is the file not exist");
e.printStackTrace();
}catch(IOException e) {
logger.error("read private key fail,the reason is :"+e.getMessage());
e.printStackTrace();
}catch(NoSuchAlgorithmException e) {
logger.error("read private key fail,the reason is :"+e.getMessage());
e.printStackTrace();
}catch(InvalidKeySpecException e) {
logger.error("read private key fail,the reason is :"+e.getMessage());
e.printStackTrace();
}finally{try{if (pemReader != null) {
pemReader.close();
}
}catch(IOException e) {
logger.error(e.getMessage());
}
}returnprivKey;
}/**
* 获取SSLContext,基于CA, Certificate, key及密码进行SSL上下文的创建
*
* @param caPath
* @param crtPath
* @param keyPath
* @param password
* @return
* @throws Exception*/
private staticSSLContext getSSLContext(String caPath, String crtPath, String keyPath, String password) throws Exception {/** CA证书是用来认证服务端的,这里的CA就是一个公认的认证证书
* TrustManagerFactory 管理的是授信的CA证书,所以KeyStore里面存放的不需要私钥信息,通常也不可能有*/CertificateFactory cAf= CertificateFactory.getInstance("X.509");
FileInputStream caIn= newFileInputStream(caPath);
X509Certificate ca=(X509Certificate) cAf.generateCertificate(caIn);
KeyStore caKs= KeyStore.getInstance("JKS");
caKs.load(null, password.toCharArray());
caKs.setCertificateEntry("ca1", ca); //可以通过设置alias不同,配置多个ca实例,即配置多个可信的root CA。
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(caKs);
caIn.close();//这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream crtIn= newFileInputStream(crtPath);
X509Certificate caCert=(X509Certificate) cf.generateCertificate(crtIn);
crtIn.close();//客户端私钥,是用来处理双向SSL验证中服务端用客户端证书加密的数据的解密(解析签名)工具
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, password.toCharArray());
ks.setCertificateEntry("certificate3", caCert);
String sslPath= "E:\HOWTO\emqtt-ssl\self1\";
FileInputStream crtIn1= new FileInputStream(sslPath + "chainca1.crt");
FileInputStream crtIn2= new FileInputStream(sslPath + "chainca2.crt");
X509Certificate caCert1=(X509Certificate) cf.generateCertificate(crtIn1);
X509Certificate caCert2=(X509Certificate) cf.generateCertificate(crtIn2);
crtIn1.close();
crtIn2.close();
ks.setCertificateEntry("certificate1", caCert1);
ks.setCertificateEntry("certificate2", caCert2);
PrivateKey privateKey= getPrivateKey(newFile(keyPath));/** 注意:下面这行代码中非常重要的一点是:
* setKeyEntry这个函数的第二个参数 password,他不是指私钥的加密密码,只是KeyStore对这个私钥进行管理设置的密码
*
* setKeyEntry中最后一个参数,chain的顺序是证书链中越靠近当前privateKey节点的证书,越靠近数字下标0的位置。即chain[0]是privateKey对应的证书,
* chain[1]是签发chain[0]的证书,以此类推,有chain[i+1]签发chain[i]的关系。*/ks.setKeyEntry("private-key", privateKey, password.toCharArray(), newCertificate[]{caCert, caCert2, caCert1});/** KeyManagerFactory必须是证书和私钥配对使用,即KeyStore里面装载客户端证书以及对应的私钥,双向SSL验证需要。*/KeyManagerFactory kmf= KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, password.toCharArray());/** 最后创建SSL套接字工厂 SSLSocketFactory
* 注意:这里,SSLContext不支持TLSv2创建*/SSLContext context= SSLContext.getInstance("TLSv1");
KeyManager[] kms=kmf.getKeyManagers();
TrustManager[] tms=tmf.getTrustManagers();
context.init(kms, tms,newSecureRandom());returncontext;
}/**
* 基于给定的CA文件,客户端证书文件以及客户端私钥文件,进行SSL上下文环境的构建, 此处创建的SSLSocketFactory是支持双向SSL验证的。
*
* NOTE: 证书及秘钥文件,都是通过openssl创建获取的。
*
* @param caPath CA证书文件
* @param crtPath 客户证书文件
* @param keyPath 客户私钥文件
* @param password KeyStore存储私钥配置的安全密码,类似数据库存了数据,想访问,需要密码一样。
* @return
* @throws Exception*/
public staticSSLSocketFactory getSSLSocketFactory(String caPath, String crtPath, String keyPath, String password) throws Exception {
SSLContext ctx=getSSLContext(caPath, crtPath, keyPath, password);
SSLSocketFactory factory=ctx.getSocketFactory();returnfactory;
}
}
最后
以上就是魁梧乌冬面为你收集整理的mqtt java ssl_MQTT研究之EMQ:【SSL证书链验证】的全部内容,希望文章能够帮你解决mqtt java ssl_MQTT研究之EMQ:【SSL证书链验证】所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复