mirror of
https://gitee.com/willfree/min-dev-java.git
synced 2026-10-04 19:42:01 +08:00
313 lines
12 KiB
Java
313 lines
12 KiB
Java
package minsecurity.certificate.cert;
|
|
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import minsecurity.Common;
|
|
import minsecurity.crypto.*;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.crypto.BadPaddingException;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.NoSuchPaddingException;
|
|
import java.io.IOException;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.NoSuchProviderException;
|
|
import java.util.Base64;
|
|
|
|
/*
|
|
* @Author: hongyu guo
|
|
* @Description:
|
|
* @Version: 1.0.0
|
|
* @Date: 17:09 2021/03/05
|
|
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
|
*/
|
|
public class CertUtils {
|
|
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CertUtils.class);
|
|
public static final int VERSION1 = 1;
|
|
|
|
/**
|
|
* Certificate -> InnerCertificate
|
|
* @param certificate
|
|
* @return InnerCertificate
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static InnerCertificate parseCertToInnerCert(Certificate certificate){
|
|
TbsCertificate tbsCertificate = new TbsCertificate();
|
|
tbsCertificate.setVersion(certificate.getVersion());
|
|
tbsCertificate.setSerialNumber(certificate.getSerialNumber());
|
|
tbsCertificate.setPublicKey(certificate.getPublicKey().getBytes());
|
|
tbsCertificate.setSignatureAlgorithm(certificate.getSignatureAlgorithm());
|
|
tbsCertificate.setPublicKeyAlgorithm(certificate.getPublicKeyAlgorithm());
|
|
tbsCertificate.setIssueTo(certificate.getIssueTo());
|
|
tbsCertificate.setIssuer(certificate.getIssuer());
|
|
tbsCertificate.setNotAfter(certificate.getNotAfter());
|
|
tbsCertificate.setNotBefore(certificate.getNotBefore());
|
|
tbsCertificate.setKeyUsage(certificate.getKeyUsage());
|
|
tbsCertificate.setCA(certificate.isCA());
|
|
tbsCertificate.setTimestamp(certificate.getTimestamp());
|
|
|
|
InnerCertificate innerCertificate = new InnerCertificate();
|
|
innerCertificate.setSignatureAlgorithm(certificate.getSignatureAlgorithm());
|
|
innerCertificate.setSignatureValue(certificate.getSignature());
|
|
innerCertificate.setTbsCertificate(tbsCertificate);
|
|
return innerCertificate;
|
|
}
|
|
|
|
|
|
/**
|
|
* InnerCertificate -> Certificate
|
|
* @param i
|
|
* @return minsecurity.certificate.cert.Certificate
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static Certificate parseInnerCertToCert(InnerCertificate i){
|
|
Certificate certificate = new Certificate();
|
|
|
|
certificate.setVersion(i.getTbsCertificate().getVersion());
|
|
certificate.setSerialNumber(i.getTbsCertificate().getSerialNumber());
|
|
PublicKeyInterface publicKey = KeyUtils.unMarshalPublicKey(i.getTbsCertificate().getPublicKey(),i.getSignatureAlgorithm());
|
|
certificate.setPublicKey(publicKey);
|
|
certificate.setSignature(i.getSignatureValue());
|
|
certificate.setSignatureAlgorithm(i.getTbsCertificate().getSignatureAlgorithm());
|
|
certificate.setPublicKeyAlgorithm(i.getTbsCertificate().getPublicKeyAlgorithm());
|
|
certificate.setIssueTo(i.getTbsCertificate().getIssueTo());
|
|
certificate.setIssuer(i.getTbsCertificate().getIssuer());
|
|
certificate.setNotBefore(i.getTbsCertificate().getNotBefore());
|
|
certificate.setNotAfter(i.getTbsCertificate().getNotAfter());
|
|
certificate.setKeyUsage(i.getTbsCertificate().getKeyUsage());
|
|
certificate.setCA(i.getTbsCertificate().isCA());
|
|
certificate.setTimestamp(i.getTbsCertificate().getTimestamp());
|
|
|
|
return certificate;
|
|
}
|
|
|
|
/**
|
|
* 检查证书是否在有效期内
|
|
* @param innerCertificate
|
|
* @return boolean
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static boolean checkDuration(InnerCertificate innerCertificate){
|
|
long curTime = System.currentTimeMillis();
|
|
// logger.debug("current time: {}", curTime);
|
|
if(innerCertificate.getTbsCertificate().getNotAfter() < curTime)
|
|
return false;
|
|
if(innerCertificate.getTbsCertificate().getNotBefore() > curTime)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* 使用publicKey验证签名
|
|
* @param cert
|
|
* @param publicKey
|
|
* @return boolean
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static boolean checkSign(InnerCertificate cert, PublicKeyInterface publicKey) throws Exception {
|
|
switch (cert.getTbsCertificate().getSignatureAlgorithm()){
|
|
case Common.SM3withSM2:
|
|
byte[] bytesOfCert = serialization(cert.getTbsCertificate());
|
|
return publicKey.verify(bytesOfCert, cert.getSignatureValue());
|
|
default:
|
|
throw new Exception("未定义的签名方法");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 将TbsCertificate类型实例转化为字节流
|
|
* @param tbsCertificate
|
|
* @return byte[]
|
|
* @throws CertException
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static byte[] serialization(TbsCertificate tbsCertificate) throws CertException {
|
|
ObjectMapper mapper = new ObjectMapper();
|
|
byte[] bytesOfCertJson;
|
|
try {
|
|
bytesOfCertJson = mapper.writeValueAsBytes(tbsCertificate);
|
|
return bytesOfCertJson;
|
|
} catch (JsonProcessingException e) {
|
|
e.printStackTrace();
|
|
}
|
|
throw new CertException("tbsCertificate serialization failed");
|
|
}
|
|
|
|
/**
|
|
* 将InnerCertificate类型实例转化为字节流
|
|
* @param innerCertificate
|
|
* @return byte[]
|
|
* @throws CertException
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static byte[] serialization(InnerCertificate innerCertificate) throws CertException {
|
|
ObjectMapper mapper = new ObjectMapper();
|
|
byte[] bytesOfCertJson;
|
|
try {
|
|
bytesOfCertJson = mapper.writeValueAsBytes(innerCertificate);
|
|
return bytesOfCertJson;
|
|
} catch (JsonProcessingException e) {
|
|
e.printStackTrace();
|
|
}
|
|
throw new CertException("innerCertificate serialization failed");
|
|
}
|
|
|
|
/**
|
|
* 根据typeOfCert可将字节流反序列化为TbsCertificate或InnerCertificate,
|
|
* 返回Object类型, 调用者调用时需要手动强制类型转化
|
|
* @param bytesOfCert 字节流
|
|
* @param typeOfCert Common.TbsCertificate或Common.InnerCertificate
|
|
* @return java.lang.Object
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static Object deserialization(byte[] bytesOfCert, int typeOfCert){
|
|
ObjectMapper mapper = new ObjectMapper();
|
|
try {
|
|
switch (typeOfCert) {
|
|
case Common.TbsCertificate:
|
|
return mapper.readValue(bytesOfCert, TbsCertificate.class);
|
|
case Common.InnerCertificate:
|
|
return mapper.readValue(bytesOfCert, InnerCertificate.class);
|
|
default:
|
|
throw new CertException("未知证书类型");
|
|
}
|
|
} catch (IOException | CertException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* 使用priv私钥给Certificate类型实例签名
|
|
* @param certificate 未签名证书
|
|
* @param priv 私钥
|
|
* @return void
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static void signCert(Certificate certificate, PrivateKeyInterface priv) throws Exception {
|
|
byte[] bytesOfCert = serialization(CertUtils.parseCertToInnerCert(certificate).getTbsCertificate());
|
|
// logger.debug("签名序列化bytes: {}", ByteUtils.toHexString(bytesOfCert));
|
|
switch (certificate.getSignatureAlgorithm()){
|
|
case Common.SM3withSM2:
|
|
certificate.setSignature(priv.sign(bytesOfCert));
|
|
break;
|
|
default:
|
|
logger.debug("signAlgo: {}", certificate.getSignatureAlgorithm());
|
|
throw new Exception("未定义的签名方法");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 使用ca的公钥对sub进行验签, 如果时根证书验签, 调用时ca与sub指向同一个实例
|
|
* @param ca
|
|
* @param sub
|
|
* @return boolean
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static boolean verifyCert(Certificate ca, Certificate sub) throws Exception {
|
|
InnerCertificate innerCertificate = CertUtils.parseCertToInnerCert(sub);
|
|
switch (innerCertificate.getTbsCertificate().getVersion()){
|
|
case CertUtils.VERSION1:
|
|
boolean isValid = CertUtils.checkDuration(innerCertificate);
|
|
// logger.debug("有效期内:{}", isValid);
|
|
if(!isValid)
|
|
return false;
|
|
if(sub.isCA()){ // 根证书, 自验证
|
|
return checkSign(innerCertificate, sub.getPublicKey());
|
|
} else { // 使用ca验证sub
|
|
return checkSign(innerCertificate, ca.getPublicKey());
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 证书序列化为String类型, 密码为null或为空时不对证书进行加密保存
|
|
* @param certificate
|
|
* @param passwd
|
|
* @param symAlgoMode Common.SM4CBC / Common.SM4ECB
|
|
* @return java.lang.String
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static String toPem(Certificate certificate, byte[] passwd, int symAlgoMode) throws CertException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
|
|
if(certificate.getSignature() == null){
|
|
throw new CertException("该证书未签名");
|
|
}
|
|
byte[] bytesOfCert = serialization(CertUtils.parseCertToInnerCert(certificate));
|
|
byte[] ret;
|
|
if(passwd != null && passwd.length > 0){
|
|
// byte[] sm4Key;
|
|
byte[] sm4Key = KeyUtils.get16bytePasswd(passwd);
|
|
|
|
switch (symAlgoMode){
|
|
case Common.SM4CBC:{
|
|
// TODO: PADDING??? IV?????
|
|
ret = SM4.encrypt_CBC_Padding(sm4Key, new byte[16],bytesOfCert);
|
|
break;
|
|
}
|
|
case Common.SM4ECB:{
|
|
ret = SM4.encrypt_ECB_Padding(sm4Key, bytesOfCert);
|
|
break;
|
|
}
|
|
default:
|
|
throw new CertException("未定义的对称加密算法");
|
|
}
|
|
} else {
|
|
ret = bytesOfCert;
|
|
}
|
|
return Base64.getEncoder().encodeToString(ret);
|
|
}
|
|
|
|
/**
|
|
* String类型证书转化为Certificate对象
|
|
* @param pemString
|
|
* @param passwd
|
|
* @param symAlgoMode Common.SM4CBC / Common.SM4ECB
|
|
* @return minsecurity.certificate.cert.Certificate
|
|
* @author hongyu guo
|
|
* @date 2021/3/7
|
|
**/
|
|
public static Certificate fromPem(String pemString, byte[] passwd, int symAlgoMode)
|
|
throws CertException, BadPaddingException, NoSuchPaddingException,
|
|
InvalidAlgorithmParameterException, NoSuchAlgorithmException,
|
|
IllegalBlockSizeException, NoSuchProviderException, InvalidKeyException {
|
|
if(pemString == null || pemString.equals("")){
|
|
throw new CertException("pem串为空");
|
|
}
|
|
byte[] bytesOfPem = Base64.getDecoder().decode(pemString);
|
|
byte[] bytesOfCert;
|
|
if(passwd != null && passwd.length > 0){
|
|
byte[] sm4Key = KeyUtils.get16bytePasswd(passwd);
|
|
switch (symAlgoMode){
|
|
case Common.SM4CBC:
|
|
bytesOfCert = SM4.decrypt_CBC_Padding(sm4Key,new byte[16], bytesOfPem);
|
|
break;
|
|
case Common.SM4ECB:
|
|
bytesOfCert = SM4.decrypt_ECB_Padding(sm4Key, bytesOfPem);
|
|
break;
|
|
default:
|
|
throw new CertException("未知的对称加密算法");
|
|
}
|
|
} else {
|
|
bytesOfCert = bytesOfPem;
|
|
}
|
|
InnerCertificate innerCertificate = (InnerCertificate) deserialization(bytesOfCert, Common.InnerCertificate);
|
|
return CertUtils.parseInnerCertToCert(innerCertificate);
|
|
}
|
|
}
|