前段时间做设备证书服务端生成,网上copy一堆代码,但是结果生成的证书在emqx里都是认证失败,无奈,请教同事,请教度娘,请教各种论坛,废话有点多,下面说正题。
使用服务器上生成的SSL证书格式为PKCS1的格式,而java代码内提供的是PKCS8 具体两种格式的差异可以百度 PKCS8 和PKCS1。
找到问题的根源那么就容易了,然后开始问度娘 两种格式的转换,废话不多说,直接上代码,以下代码均来源于网络:
引入相关依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
java内转换代码
/**
* PKCS8转PKCS1
*
* @param key
* @return
* @throws Exception
*/
private String pkcs8To1(PublicKey key) throws Exception {
byte[] pubBytes = Base64.decodeBase64(Base64.encodeBase64URLSafeString(key.getEncoded()));
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
ASN1Primitive primitive = spkInfo.parsePublicKey();
byte[] publicKeyPKCS1 = primitive.getEncoded();
return pkcs1ToPem(publicKeyPKCS1, false);
}
/**
* PKCS8转PKCS1
*
* @param key
* @return
* @throws Exception
*/
private String pkcs8To1(PrivateKey key) throws Exception {
String privateKey = Base64.encodeBase64URLSafeString(key.getEncoded());
return privatePem(privateKey);
}
public String privatePem(String privateKey) throws Exception {
byte[] privBytes = Base64.decodeBase64(privateKey);
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
return pkcs1ToPem(privateKeyPKCS1, false);
}
private String pkcs1ToPem(byte[] pcks1KeyBytes, boolean isPublic) throws Exception {
String type;
if (isPublic) {
type = "RSA PUBLIC KEY";
} else {
type = "RSA PRIVATE KEY";
}
PemObject pemObject = new PemObject(type, pcks1KeyBytes);
StringWriter stringWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(stringWriter);
pemWriter.writeObject(pemObject);
pemWriter.close();
String pemString = stringWriter.toString();
return pemString;
}
后边是完整的生成证书的代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.util.Calendar;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import sun.misc.BASE64Encoder;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.AlgorithmId;
import sun.security.x509.AuthorityKeyIdentifierExtension;
import sun.security.x509.BasicConstraintsExtension;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.KeyIdentifier;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
@SuppressWarnings("restriction")
public class X509CertService {
private static final String NEW_LINE = System.getProperty("line.separator");
/** 在将java生成的证书导出到文件的时候,需要将下面两行信息对应的添加到证书内容的头部后尾部 */
private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";
/** 证书签名算法, e.g. SHA256withRSA */
private static final String SIGN_ALGO = "SHA256withRSA";
/** 私钥安全算法,e.g. RSA */
private static final String RSA_ALGORITHM = "RSA";
/** 验证有效期 */
public static final int VALIDITY_DAY = 365;
/** subject默认信息 */
public static final String SUBJECT_FORMAT = "CN=%s/O=OwnTracks.org/OU=generate-CA/emailAddress=nobody@example.net";
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class X509Sign {
/** 私钥 */
private PrivateKey privateKey;
/** 签发的证书 */
private X509Certificate certificate;
/** 签发的私钥字符串 */
private String privateCer;
/** 签发的公钥字符串 */
private String publicKeyCer;
}
/**
* 生成证书的公钥和私钥
*
* @param algorithm
* @param digestSignAlgo
* @param keySize
* @return
* @throws Exception
*/
public CertAndKeyGen generateCertAndKeyGen() throws Exception {
CertAndKeyGen certAndKeyGen = new CertAndKeyGen(RSA_ALGORITHM, SIGN_ALGO, null);
// 生成一对key 参数为key的长度 对于rsa不能小于512
certAndKeyGen.generate(2048);
certAndKeyGen.setRandom(new SecureRandom());
return certAndKeyGen;
}
/**
* 创建根证书, 并保存根证书到指定路径的文件中, crt和key分开存储文件。
* 创建SSL根证书的逻辑,很重要,此函数调用频次不高,创建根证书,也就是自签名证书。
*
* @param algorithm 私钥安全算法,e.g. RSA
* @param keySize 私钥长度,越长越安全,RSA要求不能小于512, e.g. 2048
* @param digestSignAlgo 信息摘要以及签名算法 e.g. SHA256withRSA
* @param subj 证书所有者信息描述,e.g.
* CN=iotp,OU=tkcloud,O=taikang,L=wuhan,S=hubei,C=CN
* @param validDays 证书有效期天数,e.g. 3650即10年
* @param rootCACrtPath 根证书所要存入的全路径,e.g. /opt/certs/iot/rootCA.crt
* @param rootCAKeyPath 根证书对应秘钥key所要存入的全路径,e.g. /opt/certs/iot/rootCA.key
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidKeyException
* @throws IOException
* @throws CertificateException
* @throws SignatureException
* @throws UnrecoverableKeyException
* @return 私钥和证书对的map对象
* @throws Exception
*/
public X509Sign signRoot(String subject) throws Exception {
Date now = new Date();
// 参数分别为 公钥算法 签名算法 providerName(因为不知道确切的 只好使用null 既使用默认的provider)
CertAndKeyGen cak = generateCertAndKeyGen();
// 证书拥有者subject的描述name
sun.security.x509.X500Name _subject = new X500Name(subject);
// 给证书配置扩展信息
PublicKey publicKey = cak.getPublicKey();
PrivateKey privateKey = cak.getPrivateKey();
CertificateExtensions exts = new CertificateExtensions();
exts.set(SubjectKeyIdentifierExtension.NAME,
new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
exts.set(AuthorityKeyIdentifierExtension.NAME,
new AuthorityKeyIdentifierExtension(new KeyIdentifier(publicKey), null, null));
// 设置是否根证书
BasicConstraintsExtension bce = new BasicConstraintsExtension(true, -1);
exts.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(false, bce.getExtensionValue()));
// 配置证书的有效期,并生成根证书(自签名证书)
long validate = 3650 * 24L * 60L * 60L;
X509Certificate certificate = cak.getSelfCertificate(_subject, now, validate, exts);
String privatePkcs1 = pkcs8To1(privateKey);
String publicPkcs1 = pkcs8To1(publicKey);
return new X509Sign(privateKey, certificate, privatePkcs1, publicPkcs1);
}
public static void main(String[] args) throws Exception {
X509CertService service = new X509CertService();
String deviceId = "1.2.156.156.11.11.55";
String caSubject = "CN=An MQTT broker/O=jd.com/OU=iot-platform/emailAddress=iot-platform@example.com";
X509Sign ca = service.signRoot(caSubject);
service.export(ca, "ca");
String serverSubject = "CN=localhost/O=jd.com/OU=iot-platform/emailAddress=iot-platform@example.com";
X509Sign server = service.sign(serverSubject);
service.export(server, "server");
String clientSubject = String.format("CN=%s/O=OwnTracks.org/OU=generate-CA/emailAddress=nobody@example.net", deviceId);
X509Sign device = service.sign(clientSubject);
service.export(device, "1.2.156.156.11.11.55");
}
private KeyPair createKeyPair() throws Exception {
// 为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
// 初始化KeyPairGenerator对象,密钥长度
kpg.initialize(2048);
// 生成密匙对
return kpg.generateKeyPair();
}
/**
* 签发证书
*
* @param subject
* @param extend
* @return
* @throws Exception
*/
public X509Sign sign(String subject) throws Exception {
String path = "/work/admin/x509";
X509Certificate ca = getCertficate(new File(path + "/ca.crt"));
// 根证书key
PrivateKey key = getPrivateKey(path + "/ca.key");
KeyPair gen = createKeyPair();
PrivateKey privateKey = gen.getPrivate();
PublicKey publicKey = gen.getPublic();
return sign(ca, key, privateKey, publicKey, subject, VALIDITY_DAY);
}
/**
* 签发证书
*
* @param subject
* @param extend
* @return
* @throws Exception
*/
public X509Sign sign(String subject, int day) throws Exception {
String path = "/work/admin/x509";
X509Certificate ca = getCertficate(new File(path + "/ca.crt"));
// 根证书key
PrivateKey key = getPrivateKey(path + "/ca.key");
KeyPair gen = createKeyPair();
PrivateKey privateKey = gen.getPrivate();
PublicKey publicKey = gen.getPublic();
return sign(ca, key, privateKey, publicKey, subject, day);
}
/**
* 创建X509的证书, 由ca证书完成签名。
*
* subject,issuer都遵循X500Principle规范, 即: X500Principal由可分辨名称表示,例如“CN = Duke,OU =
* JavaSoft,O = Sun Microsystems,C = US”。
*
* @param ca 根证书对象
* @param caKey CA证书对应的私钥对象
* @param publicKey 待签发证书的公钥对象
* @param subj 证书拥有者的主题信息,签发者和主题拥有者名称都转写X500Principle规范,格式:CN=country,ST=state,L=Locality,OU=OrganizationUnit,O=Organization
* @param validDays 证书有效期天数
* @param sginAlgo 证书签名算法, e.g. SHA256withRSA
*
* @return cert 新创建得到的X509证书
*/
public static X509Certificate createUserCert(X509Certificate ca, PrivateKey caKey, PublicKey publicKey, String subj,
long validDays, String sginAlgo) {
// 获取ca证书
X509Certificate caCert = ca;
X509CertInfo x509CertInfo = new X509CertInfo();
try {
// 设置证书的版本号
x509CertInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
// 设置证书的序列号,基于当前时间计算
x509CertInfo.set(X509CertInfo.SERIAL_NUMBER,
new CertificateSerialNumber((int) (System.currentTimeMillis() / 1000L)));
/**
* 下面这个设置算法ID的代码,是错误的,会导致证书验证失败,但是报错不是很明确。 若将生成的证书存为keystore,让后keytool转换 会出现异常。
* AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.SHA256_oid);
*/
AlgorithmId algorithmId = AlgorithmId.get(sginAlgo);
x509CertInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithmId));
// 设置证书的签发者信息
X500Name issuer = new X500Name(caCert.getIssuerX500Principal().toString());
x509CertInfo.set(X509CertInfo.ISSUER, issuer);
// 设置证书的拥有者信息
X500Name subject = new X500Name(subj);
x509CertInfo.set(X509CertInfo.SUBJECT, subject);
// 设置证书的公钥
x509CertInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
// 设置证书有效期
Date beginDate = new Date();
Date endDate = new Date(beginDate.getTime() + validDays * 24 * 60 * 60 * 1000L);
CertificateValidity cv = new CertificateValidity(beginDate, endDate);
x509CertInfo.set(X509CertInfo.VALIDITY, cv);
CertificateExtensions exts = new CertificateExtensions();
/*
* 以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
*
*/
exts.set("SubjectKeyIdentifier",
new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
exts.set("AuthorityKeyIdentifier",
new AuthorityKeyIdentifierExtension(new KeyIdentifier(ca.getPublicKey()), null, null));
exts.set("BasicConstraints", new BasicConstraintsExtension(false, false, 0));
x509CertInfo.set("extensions", exts);
} catch (CertificateException cee) {
cee.printStackTrace();
} catch (IOException eio) {
eio.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 获取CA私钥
PrivateKey caPrivateKey = caKey;
// 用CA的私钥给当前证书进行签名,获取最终的下游证书(证书链的下一节点)
X509CertImpl cert = new X509CertImpl(x509CertInfo);
try {
cert.sign(caPrivateKey, sginAlgo);
} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException
| SignatureException e3) {
e3.printStackTrace();
}
return cert;
}
/**
* 签证用户证书
*
* @param ca 根证书对象
* @param privateKey待签发证书的私钥对象
* @param publicKey 待签发证书的公钥对象
* @param subject 证书拥有者的主题信息,签发者和主题拥有者名称都转写X500Principle规范,格式:CN=country,ST=state,L=Locality,OU=OrganizationUnit,O=Organization
* @return X509Certificate 新创建得到的X509证书
*/
public X509Sign sign(X509Certificate ca, PrivateKey caKey, PrivateKey privateKey, PublicKey publicKey,
String subject, int day) throws Exception {
X509CertInfo x509CertInfo = new X509CertInfo();
// 证书版本,此方案采用V3
x509CertInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
// 设置证书的序列号,基于当前时间计算
x509CertInfo.set(X509CertInfo.SERIAL_NUMBER, getCertSerualNumber());
/**
* 下面这个设置算法ID的代码,是错误的,会导致证书验证失败,但是报错不是很明确。 若将生成的证书存为keystore,让后keytool转换 会出现异常。
* AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.SHA256_oid);
*/
AlgorithmId algorithmId = AlgorithmId.get(SIGN_ALGO);
x509CertInfo.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algorithmId));
// 设置证书的签发者信息
X500Name issuer = new X500Name(ca.getIssuerX500Principal().toString());
x509CertInfo.set(X509CertInfo.ISSUER, issuer);
// 设置证书的拥有者信息
x509CertInfo.set(X509CertInfo.SUBJECT, new X500Name(subject));
// 设置证书的公钥
x509CertInfo.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
// 设置证书有效期
x509CertInfo.set(X509CertInfo.VALIDITY, getCertValidity(day));
// 以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤
CertificateExtensions exts = new CertificateExtensions();
exts.set(SubjectKeyIdentifierExtension.NAME,
new SubjectKeyIdentifierExtension((new KeyIdentifier(publicKey)).getIdentifier()));
exts.set(AuthorityKeyIdentifierExtension.NAME,
new AuthorityKeyIdentifierExtension(new KeyIdentifier(publicKey), null, null));
exts.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(false, false, -1));
x509CertInfo.set(CertificateExtensions.NAME, exts);
// 用CA的私钥给当前证书进行签名,获取最终的下游证书(证书链的下一节点)
X509CertImpl certificate = new X509CertImpl(x509CertInfo);
certificate.sign(caKey, SIGN_ALGO);
String privatePkcs1 = pkcs8To1(privateKey);
String publicPkcs1 = pkcs8To1(publicKey);
return new X509Sign(privateKey, certificate, privatePkcs1, publicPkcs1);
}
public byte[] readCaCert() throws Exception {
String output = "/work/admin/x509/ca.crt";
File file = new File(output);
if (!file.exists()) {
throw new Exception("证书不存在");
}
// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
FileInputStream input = null;
byte[] b = null;
try {
input = new FileInputStream(file);
b = new byte[input.available()];
input.read(b);
} finally {
if (input != null) {
input.close();
}
}
return b;
}
/**
* 读取证书
*
* @param name
* @return
* @throws Exception
*/
public byte[] readDeviceCertificate(String name) throws Exception {
String output = getDeviceOutput();
File file = new File(output + "/" + name + ".crt");
if (!file.exists()) {
throw new Exception("设备证书不存在!");
}
// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
FileInputStream input = null;
byte[] b = null;
try {
input = new FileInputStream(file);
b = new byte[input.available()];
input.read(b);
} finally {
if (input != null) {
input.close();
}
}
return b;
}
/**
* 获取设备证书
*
* @param name
* @return
* @throws Exception
*/
public Certificate getDeviceCertificate(String name) throws Exception {
String output = getDeviceOutput();
File file = new File(output + "/" + name + ".crt");
if (!file.exists()) {
throw new Exception("设备证书不存在!");
}
return getCertficate(file);
}
private void bakFile(File file) {
if (file.exists()) {
file.renameTo(new File(file.getAbsolutePath() + "." + System.currentTimeMillis()));
}
}
/**
* 将JAVA创建的证书内容导出到文件, 基于BASE64转码了。
*
*
* @param devCrt 设备证书对象
* @param crtPath 设备证书存储路径
*/
private void exportCrt(Certificate devCrt, String exportPath) throws Exception {
BASE64Encoder base64Crt = new BASE64Encoder();
FileOutputStream fosCrt = null;
File file = new File(exportPath);
bakFile(file);
try {
fosCrt = new FileOutputStream(file);
String cont = BEGIN_CERTIFICATE + NEW_LINE;
fosCrt.write(cont.getBytes());
base64Crt.encodeBuffer(devCrt.getEncoded(), fosCrt);
cont = END_CERTIFICATE;
fosCrt.write(cont.getBytes());
} finally {
if (fosCrt != null) {
try {
fosCrt.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* PKCS8转PKCS1
*
* @param key
* @return
* @throws Exception
*/
private String pkcs8To1(PublicKey key) throws Exception {
byte[] pubBytes = Base64.decodeBase64(Base64.encodeBase64URLSafeString(key.getEncoded()));
SubjectPublicKeyInfo spkInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
ASN1Primitive primitive = spkInfo.parsePublicKey();
byte[] publicKeyPKCS1 = primitive.getEncoded();
return pkcs1ToPem(publicKeyPKCS1, false);
}
/**
* PKCS8转PKCS1
*
* @param key
* @return
* @throws Exception
*/
private String pkcs8To1(PrivateKey key) throws Exception {
String privateKey = Base64.encodeBase64URLSafeString(key.getEncoded());
return privatePem(privateKey);
}
/**
* 导出私钥内容到文件中,以base64编码。 注意,java生成的私钥文件默认是PKCS#8的格式,加载的时候,要注意对应关系。
*
* @param key
* @param keyPath
*/
private void exportKey(String key, String exportPath) throws Exception {
FileOutputStream fosKey = null;
File file = new File(exportPath);
bakFile(file);
try {
fosKey = new FileOutputStream(file);
fosKey.write(key.getBytes());
} finally {
if (fosKey != null) {
try {
fosKey.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public String privatePem(String privateKey) throws Exception {
byte[] privBytes = Base64.decodeBase64(privateKey);
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privBytes);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
return pkcs1ToPem(privateKeyPKCS1, false);
}
private String pkcs1ToPem(byte[] pcks1KeyBytes, boolean isPublic) throws Exception {
String type;
if (isPublic) {
type = "RSA PUBLIC KEY";
} else {
type = "RSA PRIVATE KEY";
}
PemObject pemObject = new PemObject(type, pcks1KeyBytes);
StringWriter stringWriter = new StringWriter();
PemWriter pemWriter = new PemWriter(stringWriter);
pemWriter.writeObject(pemObject);
pemWriter.close();
String pemString = stringWriter.toString();
return pemString;
}
private String getDeviceOutput() {
return "/work/admin/x509/device";
}
/**
* 读取设备私钥
*
* @param deviceId
* @return
* @throws Exception
*/
public String readDeviceKey(String deviceId) throws Exception {
String output = getDeviceOutput() + "/" + deviceId + ".key";
File file = new File(output);
byte[] bytes = FileUtils.readFileToByteArray(file);
return new String(bytes);
}
/**
* 利用开源的工具类BC解析私钥,例如openssl私钥文件格式为pem,需要去除页眉页脚后才能被java读取
*
* @param file 私钥文件
* @return 私钥对象
* @throws Exception
*/
public PrivateKey getPrivateKey(String privateKeyPath) throws Exception {
File file = new File(privateKeyPath);
PrivateKey privKey = null;
PemReader pemReader = null;
try {
pemReader = new PemReader(new FileReader(file));
PemObject pemObject = pemReader.readPemObject();
byte[] pemContent = pemObject.getContent();
// 支持从PKCS#1或PKCS#8 格式的私钥文件中提取私钥, PKCS#1的私钥,主要是openssl默认生成的编码格式
if (pemObject.getType().endsWith("RSA PRIVATE KEY")) {
// 取得私钥 for PKCS#1 openssl genrsa 默认生成的私钥就是PKCS1的编码
org.bouncycastle.asn1.pkcs.RSAPrivateKey asn1PrivateKey = org.bouncycastle.asn1.pkcs.RSAPrivateKey
.getInstance(pemContent);
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(asn1PrivateKey.getModulus(),
asn1PrivateKey.getPrivateExponent());
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
privKey = keyFactory.generatePrivate(rsaPrivateKeySpec);
} else if (pemObject.getType().endsWith("PRIVATE KEY")) {
/*
* java创建的私钥,默认是PKCS#8格式 通过openssl pkcs8 -topk8转换为pkcs8,例如(-nocrypt不做额外加密操作):
* openssl pkcs8 -topk8 -in pri.key -out pri8.key -nocrypt 取得私钥 for PKCS#8
*/
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pemContent);
KeyFactory kf = KeyFactory.getInstance(RSA_ALGORITHM);
privKey = kf.generatePrivate(privKeySpec);
}
} finally {
if (pemReader != null) {
pemReader.close();
}
}
return privKey;
}
/**
* 导出
*
* @param sign
* @param name
* @throws Exception
*/
public void export(X509Sign sign, String name) throws Exception {
String output = getDeviceOutput();
exportCrt(sign.getCertificate(), output + "/" + name + ".crt");
exportKey(sign.getPrivateCer(), output + "/" + name + ".key");
}
/**
* 从经过base64转化后的证书文件中构建证书对象,是一个标准的X509证书,
*
* 且非常重要的是,文件头部含有-----BEGIN CERTIFICATE----- 文件的尾部含有 -----END CERTIFICATE-----
* 若没有上述头和尾部,证书验证的时候会报certificate_unknown。
*
* @param crtFile 经过base64处理的证书文件
* @return X509的证书
*/
public X509Certificate getCertficate(File crtFile) throws Exception {
// 这个客户端证书,是用来发送给服务端的,准备做双向验证用的。
CertificateFactory cf;
X509Certificate cert = null;
FileInputStream crtIn = null;
try {
cf = CertificateFactory.getInstance("X509");
crtIn = new FileInputStream(crtFile);
cert = (X509Certificate) cf.generateCertificate(crtIn);
} finally {
if (crtIn != null) {
try {
crtIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return cert;
}
/**
* 得到新证书的序列号
*
* @return CertificateSerialNumber
*/
private CertificateSerialNumber getCertSerualNumber() {
Calendar vCal = null;
vCal = Calendar.getInstance();
int vSerialNum = 0;
vSerialNum = (int) (vCal.getTimeInMillis() / 1000);
return new CertificateSerialNumber(vSerialNum);
}
/**
*
* 得到新证书有效日期
*
* @throws Exception
* @return CertificateValidity
*/
private CertificateValidity getCertValidity(int day) throws Exception {
long vValidity = (60 * 60 * 24 * 1000L) * day;
Calendar vCal = null;
Date vBeginDate = null, vEndDate = null;
vCal = Calendar.getInstance();
vBeginDate = vCal.getTime();
vEndDate = vCal.getTime();
vEndDate.setTime(vBeginDate.getTime() + vValidity);
return new CertificateValidity(vBeginDate, vEndDate);
}
}