mirror of
https://gitee.com/willfree/min-dev-java.git
synced 2026-06-18 04:50:25 +08:00
Merge branch 'master' of gitee.com:willfree/min-dev-java
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package minsecurity;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 16:40 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class Common {
|
||||
public static final String BC_NAME = "BC";
|
||||
public static final String X509 = "X.509";
|
||||
public static final String ALGO_NAME_EC = "EC";
|
||||
|
||||
/* Storage */
|
||||
public static final int SQLITE = 0;
|
||||
/* PublicKeyAlgorithm */
|
||||
public static final int SM2 = 0;
|
||||
/* SignatureAlgorithm */
|
||||
public static final int SM3withSM2 = 0;
|
||||
/* SymmetricAlgorithm */
|
||||
public static final int SM4ECB = 0;
|
||||
public static final int SM4CBC = 1;
|
||||
/* KeyUsage */
|
||||
public static final int ContentCommitment = 0;
|
||||
public static final int DataEncipherment = 1;
|
||||
public static final int CertSign = 2;
|
||||
// public enum Storage {
|
||||
// SQLITE;
|
||||
// }
|
||||
// public enum PublicKeyAlgorithm {
|
||||
// SM2;
|
||||
// }
|
||||
// public enum SignatureAlgorithm {
|
||||
// SM3withSM2
|
||||
// }
|
||||
// public enum SymmetricAlgorithm {
|
||||
// SM4ECB,
|
||||
// SM4CBC;
|
||||
// }
|
||||
// public enum KeyUsage {
|
||||
// ContentCommitment,
|
||||
// DataEncipherment,
|
||||
// CertSign;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
public class InvalidX500NameException extends Exception {
|
||||
private static final long serialVersionUID = 3192247087539921768L;
|
||||
|
||||
public InvalidX500NameException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public InvalidX500NameException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidX500NameException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidX500NameException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
*
|
||||
* This is simplified version of the RandomSerialNumberGenerator from the project
|
||||
* https://github.com/xipki/xipki.
|
||||
*/
|
||||
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Random serial number generator.
|
||||
*
|
||||
* This class is thread safe.
|
||||
*
|
||||
* @author Lijun Liao
|
||||
*/
|
||||
public class RandomSNAllocatorX509 implements X509CertSNAllocator {
|
||||
|
||||
/**
|
||||
* The highest bit is always set to 1, so the effective bit length is bitLen - 1. To ensure that
|
||||
* at least 64 bit entropy, bitLen must be at least 65.
|
||||
*/
|
||||
private final static int MIN_SERIALNUMBER_SIZE = 65;
|
||||
|
||||
/**
|
||||
* Since serial number should be positive and maximal 20 bytes, the maximal value of bitLen is
|
||||
* 159.
|
||||
*/
|
||||
private final static int MAX_SERIALNUMBER_SIZE = 159;
|
||||
|
||||
private static int[] AND_MASKS = new int[] {0xFF, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F};
|
||||
|
||||
private static int[] OR_MASKS = new int[] {0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40};
|
||||
|
||||
private final SecureRandom random;
|
||||
|
||||
private final int bitLen;
|
||||
|
||||
/**
|
||||
* Constructor with the bitLen = 65.
|
||||
*/
|
||||
public RandomSNAllocatorX509() {
|
||||
this(MIN_SERIALNUMBER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with the specification of bitLen.
|
||||
* @param bitLen bit length of the serial number. The highest bit is always set to 1, so the
|
||||
* effective bit length is bitLen - 1. Valid value is [65, 159].
|
||||
*/
|
||||
public RandomSNAllocatorX509(int bitLen) {
|
||||
if (bitLen < MIN_SERIALNUMBER_SIZE || bitLen > MAX_SERIALNUMBER_SIZE) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"%s may not be out of the range [%d, %d]: %d",
|
||||
"bitLen", MIN_SERIALNUMBER_SIZE, MAX_SERIALNUMBER_SIZE, bitLen));
|
||||
}
|
||||
|
||||
this.random = new SecureRandom();
|
||||
this.bitLen = bitLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger nextSerialNumber() {
|
||||
final byte[] rdnBytes = new byte[(bitLen + 7) / 8];
|
||||
final int ci = bitLen % 8;
|
||||
|
||||
random.nextBytes(rdnBytes);
|
||||
if (ci != 0) {
|
||||
rdnBytes[0] = (byte) (rdnBytes[0] & AND_MASKS[ci]);
|
||||
}
|
||||
rdnBytes[0] = (byte) (rdnBytes[0] | OR_MASKS[ci]);
|
||||
|
||||
return new BigInteger(1, rdnBytes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DERIA5String;
|
||||
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
||||
import org.bouncycastle.asn1.x509.*;
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SM2X509CertMakerBase {
|
||||
|
||||
private static enum CertLevel {
|
||||
RootCA,
|
||||
SubCA,
|
||||
EndEntity
|
||||
} // class CertLevel
|
||||
|
||||
public static final String SIGN_ALGO_SM3WITHSM2 = "SM3withSM2";
|
||||
|
||||
private long certExpire;
|
||||
private X500Name issuerDN;
|
||||
private X509CertSNAllocator snAllocator;
|
||||
private KeyPair issuerKeyPair;
|
||||
|
||||
/**
|
||||
* @param issuerKeyPair 证书颁发者的密钥对。
|
||||
* 其实一般的CA的私钥都是要严格保护的。
|
||||
* 一般CA的私钥都会放在加密卡/加密机里,证书的签名由加密卡/加密机完成。
|
||||
* 这里仅是为了演示BC库签发证书的用法,所以暂时不作太多要求。
|
||||
* @param certExpire 证书有效时间,单位毫秒
|
||||
* @param issuer 证书颁发者信息
|
||||
* @param snAllocator 维护/分配证书序列号的实例,证书序列号应该递增且不重复
|
||||
*/
|
||||
public SM2X509CertMakerBase(KeyPair issuerKeyPair, long certExpire, X500Name issuer,
|
||||
X509CertSNAllocator snAllocator) {
|
||||
this.issuerKeyPair = issuerKeyPair;
|
||||
this.certExpire = certExpire;
|
||||
this.issuerDN = issuer;
|
||||
this.snAllocator = snAllocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成根CA证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeRootCACert(byte[] csr)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||
return makeCertificate(CertLevel.RootCA, null, csr, usage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SubCA证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeSubCACert(byte[] csr)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign);
|
||||
return makeCertificate(CertLevel.SubCA, 0, csr, usage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成SSL用户证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeSSLEndEntityCert(byte[] csr)
|
||||
throws Exception {
|
||||
return makeEndEntityCert(csr,
|
||||
new KeyPurposeId[] {KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用户证书
|
||||
*
|
||||
* @param csr CSR
|
||||
* @param extendedKeyUsages 扩展指数用途。
|
||||
* @return 新的证书
|
||||
* @throws Exception 如果错误发生
|
||||
*/
|
||||
public X509Certificate makeEndEntityCert(byte[] csr,
|
||||
KeyPurposeId[] extendedKeyUsages)
|
||||
throws Exception {
|
||||
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
|
||||
| KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);
|
||||
return makeCertificate(CertLevel.SubCA, null, csr, usage, extendedKeyUsages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isCA 是否是颁发给CA的证书
|
||||
* @param keyUsage 证书用途
|
||||
* @param csr CSR
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private X509Certificate makeCertificate(CertLevel certLevel, Integer pathLenConstrain,
|
||||
byte[] csr, KeyUsage keyUsage, KeyPurposeId[] extendedKeyUsages)
|
||||
throws Exception {
|
||||
if (certLevel == CertLevel.EndEntity) {
|
||||
if (keyUsage.hasUsages(KeyUsage.keyCertSign)) {
|
||||
throw new IllegalArgumentException(
|
||||
"keyusage keyCertSign is not allowed in EndEntity Certificate");
|
||||
}
|
||||
}
|
||||
|
||||
PKCS10CertificationRequest request = new PKCS10CertificationRequest(csr);
|
||||
SubjectPublicKeyInfo subPub = request.getSubjectPublicKeyInfo();
|
||||
|
||||
PrivateKey issPriv = issuerKeyPair.getPrivate();
|
||||
PublicKey issPub = issuerKeyPair.getPublic();
|
||||
|
||||
X500Name subject = request.getSubject();
|
||||
String email = null;
|
||||
String commonName = null;
|
||||
/*
|
||||
* RFC 5280 §4.2.1.6 Subject
|
||||
* Conforming implementations generating new certificates with
|
||||
* electronic mail addresses MUST use the rfc822Name in the subject
|
||||
* alternative name extension (Section 4.2.1.6) to describe such
|
||||
* identities. Simultaneous inclusion of the emailAddress attribute in
|
||||
* the subject distinguished name to support legacy implementations is
|
||||
* deprecated but permitted.
|
||||
*/
|
||||
RDN[] rdns = subject.getRDNs();
|
||||
List<RDN> newRdns = new ArrayList<>(rdns.length);
|
||||
for (int i = 0; i < rdns.length; i++) {
|
||||
RDN rdn = rdns[i];
|
||||
|
||||
AttributeTypeAndValue atv = rdn.getFirst();
|
||||
ASN1ObjectIdentifier type = atv.getType();
|
||||
if (BCStyle.EmailAddress.equals(type)) {
|
||||
email = IETFUtils.valueToString(atv.getValue());
|
||||
} else {
|
||||
if (BCStyle.CN.equals(type)) {
|
||||
commonName = IETFUtils.valueToString(atv.getValue());
|
||||
}
|
||||
newRdns.add(rdn);
|
||||
}
|
||||
}
|
||||
|
||||
List<GeneralName> subjectAltNames = new LinkedList<>();
|
||||
if (email != null) {
|
||||
subject = new X500Name(newRdns.toArray(new RDN[0]));
|
||||
subjectAltNames.add(
|
||||
new GeneralName(GeneralName.rfc822Name,
|
||||
new DERIA5String(email, true)));
|
||||
}
|
||||
|
||||
boolean selfSignedEECert = false;
|
||||
switch (certLevel) {
|
||||
case RootCA:
|
||||
if (issuerDN.equals(subject)) {
|
||||
subject = issuerDN;
|
||||
} else {
|
||||
throw new IllegalArgumentException("subject != issuer for certLevel " + CertLevel.RootCA);
|
||||
}
|
||||
break;
|
||||
case SubCA:
|
||||
if (issuerDN.equals(subject)) {
|
||||
throw new IllegalArgumentException(
|
||||
"subject MUST not equals issuer for certLevel " + certLevel);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (issuerDN.equals(subject)) {
|
||||
selfSignedEECert = true;
|
||||
subject = issuerDN;
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger serialNumber = snAllocator.nextSerialNumber();
|
||||
Date notBefore = new Date();
|
||||
Date notAfter = new Date(notBefore.getTime() + certExpire);
|
||||
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
|
||||
issuerDN, serialNumber,
|
||||
notBefore, notAfter,
|
||||
subject, subPub);
|
||||
|
||||
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||
v3CertGen.addExtension(Extension.subjectKeyIdentifier, false,
|
||||
extUtils.createSubjectKeyIdentifier(subPub));
|
||||
if (certLevel != CertLevel.RootCA && !selfSignedEECert) {
|
||||
v3CertGen.addExtension(Extension.authorityKeyIdentifier, false,
|
||||
extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(issPub.getEncoded())));
|
||||
}
|
||||
|
||||
// RFC 5280 §4.2.1.9 Basic Constraints:
|
||||
// Conforming CAs MUST include this extension in all CA certificates
|
||||
// that contain public keys used to validate digital signatures on
|
||||
// certificates and MUST mark the extension as critical in such
|
||||
// certificates.
|
||||
BasicConstraints basicConstraints;
|
||||
if (certLevel == CertLevel.EndEntity) {
|
||||
basicConstraints = new BasicConstraints(false);
|
||||
} else {
|
||||
basicConstraints = pathLenConstrain == null
|
||||
? new BasicConstraints(true) : new BasicConstraints(pathLenConstrain.intValue());
|
||||
}
|
||||
v3CertGen.addExtension(Extension.basicConstraints, true, basicConstraints);
|
||||
|
||||
// RFC 5280 §4.2.1.3 Key Usage: When present, conforming CAs SHOULD mark this extension as critical.
|
||||
v3CertGen.addExtension(Extension.keyUsage, true, keyUsage);
|
||||
|
||||
if (extendedKeyUsages != null) {
|
||||
ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
|
||||
v3CertGen.addExtension(Extension.extendedKeyUsage, false, xku);
|
||||
|
||||
boolean forSSLServer = false;
|
||||
for (KeyPurposeId purposeId : extendedKeyUsages) {
|
||||
if (KeyPurposeId.id_kp_serverAuth.equals(purposeId)) {
|
||||
forSSLServer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (forSSLServer) {
|
||||
if (commonName == null) {
|
||||
throw new IllegalArgumentException("commonName must not be null");
|
||||
}
|
||||
GeneralName name = new GeneralName(GeneralName.dNSName,
|
||||
new DERIA5String(commonName, true));
|
||||
subjectAltNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!subjectAltNames.isEmpty()) {
|
||||
v3CertGen.addExtension(Extension.subjectAlternativeName, false,
|
||||
new GeneralNames(subjectAltNames.toArray(new GeneralName[0])));
|
||||
}
|
||||
|
||||
JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
|
||||
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME)
|
||||
.getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
|
||||
cert.verify(issPub);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
private JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) throws Exception {
|
||||
if (issPub.getAlgorithm().equals("EC")) {
|
||||
JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(SIGN_ALGO_SM3WITHSM2);
|
||||
contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
|
||||
return contentSignerBuilder;
|
||||
}
|
||||
throw new Exception("Unsupported PublicKey Algorithm:" + issPub.getAlgorithm());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import minsecurity.Common;
|
||||
import minsecurity.crypto.sm2.Sm2Base;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import util.FileUtil;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 21:15 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class X509CertMaker {
|
||||
|
||||
static {
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static X509Certificate makeRootCA(String c, String o,
|
||||
String ou, String cn,
|
||||
long expireTimeInMs, String signAlgo)
|
||||
throws Exception {
|
||||
KeyPair rootKP = Sm2Base.generateKeyPair();
|
||||
return makeRootCA(rootKP, c, o, ou, cn, expireTimeInMs, signAlgo);
|
||||
}
|
||||
|
||||
public static X509Certificate makeRootCA(KeyPair rootKP,
|
||||
String c, String o,
|
||||
String ou, String cn,
|
||||
long expireTimeInMs, String signAlgo) throws Exception {
|
||||
X509CertSNAllocator snAllocator = new RandomSNAllocatorX509();
|
||||
X500Name rootDN = getDN(c,o,ou,cn);
|
||||
SM2X509CertMakerBase rootCertMaker = new SM2X509CertMakerBase(rootKP, expireTimeInMs, rootDN, snAllocator);
|
||||
byte[] rootCSR = X509CommonUtil.createCSR(rootDN, rootKP.getPublic(), rootKP.getPrivate(),
|
||||
signAlgo).getEncoded();
|
||||
return rootCertMaker.makeRootCACert(rootCSR);
|
||||
}
|
||||
|
||||
// public static X509Certificate makeSubCACert(SM2X509CertMakerBase rootCertMaker,
|
||||
// KeyPair subKeyPair,
|
||||
// String c, String o,
|
||||
// String ou, String cn,
|
||||
// long expireTimeInMs, String signAlgo) throws Exception {
|
||||
// X500Name subDN = getDN(c, o, ou, cn);
|
||||
// byte[] midCSR = CommonUtil.createCSR(subDN, subKeyPair.getPublic(), subKeyPair.getPrivate(),
|
||||
// signAlgo).getEncoded();
|
||||
// CertSNAllocator snAllocator = new RandomSNAllocator();
|
||||
// return rootCertMaker.makeSubCACert(midCSR);
|
||||
// }
|
||||
|
||||
public static X509Certificate makeSubCACert(KeyPair rootKeyPair,
|
||||
X509Certificate rootCA,
|
||||
KeyPair subKeyPair,
|
||||
String c, String o,
|
||||
String ou, String cn,
|
||||
long expireTimeInMs, String signAlgo) throws Exception {
|
||||
X500Name subDN = getDN(c, o, ou, cn);
|
||||
X500Name rootDN = X500Name.getInstance(rootCA.getIssuerX500Principal().getEncoded());
|
||||
byte[] midCSR = X509CommonUtil.createCSR(subDN, subKeyPair.getPublic(), subKeyPair.getPrivate(),
|
||||
signAlgo).getEncoded();
|
||||
// BigInteger rootSerialNumber = rootCA.getSerialNumber();
|
||||
X509CertSNAllocator snAllocator = new RandomSNAllocatorX509();
|
||||
SM2X509CertMakerBase rootCertMaker =
|
||||
new SM2X509CertMakerBase(rootKeyPair, expireTimeInMs, rootDN, snAllocator);
|
||||
return rootCertMaker.makeSubCACert(midCSR);
|
||||
}
|
||||
|
||||
// public static X509Certificate makeSubCACert(SM2X509CertMakerBase rootCertMaker,
|
||||
// String c, String o,
|
||||
// String ou, String cn,
|
||||
// long expireTimeInMs, String signAlgo) throws Exception {
|
||||
// KeyPair subKeyPair = Sm2Base.generateKeyPair();
|
||||
// X500Name subDN = getDN(c, o, ou, cn);
|
||||
// byte[] midCSR = CommonUtil.createCSR(subDN, subKeyPair.getPublic(), subKeyPair.getPrivate(),
|
||||
// signAlgo).getEncoded();
|
||||
// CertSNAllocator snAllocator = new RandomSNAllocator();
|
||||
// return rootCertMaker.makeSubCACert(midCSR);
|
||||
// }
|
||||
|
||||
private static X500Name getDN(String c, String o, String ou, String cn){
|
||||
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
|
||||
builder.addRDN(BCStyle.C, c);
|
||||
builder.addRDN(BCStyle.O, o);
|
||||
builder.addRDN(BCStyle.OU, ou);
|
||||
builder.addRDN(BCStyle.CN, cn);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static X509Certificate generateCert(byte[] cert)
|
||||
throws CertificateException, NoSuchProviderException {
|
||||
CertificateFactory fact = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
|
||||
return (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(cert));
|
||||
}
|
||||
|
||||
public static X509Certificate generateCert(String filePath)
|
||||
throws IOException, CertificateException, NoSuchProviderException {
|
||||
byte[] cert = FileUtil.readFile(filePath);
|
||||
return generateCert(cert);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
KeyPair rootKP = Sm2Base.generateKeyPair();
|
||||
X509Certificate rootCA = makeRootCA(rootKP,"CN","MIN",
|
||||
"MIN-GROUP","MIN ROOT CA",
|
||||
365L * 24 * 60 * 60 * 1000, "SM3withSM2");
|
||||
FileUtil.writeFile("./target/root.cer",rootCA.getEncoded());
|
||||
FileUtil.writeFile("./target/root.priv",rootKP.getPrivate().getEncoded());
|
||||
rootCA.verify(rootKP.getPublic());
|
||||
|
||||
System.out.println(rootKP.getPrivate().getFormat());
|
||||
CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
|
||||
X509Certificate cert =
|
||||
(X509Certificate) fact.generateCertificate(
|
||||
new ByteArrayInputStream(rootCA.getEncoded()));
|
||||
|
||||
|
||||
|
||||
PublicKey publicKey = cert.getPublicKey();
|
||||
KeyFactory keyFac = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||
// PrivateKey privateKey = keyFac.generatePrivate();
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||
byte[] privBytes = FileUtil.readFile("./target/root.priv");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
|
||||
PrivateKey privateKey = keyFac.generatePrivate(keySpec);
|
||||
KeyPair rootKeyPair = new KeyPair(publicKey,privateKey);
|
||||
System.out.println(publicKey.getAlgorithm());
|
||||
System.out.println(privateKey.getAlgorithm());
|
||||
|
||||
// PrivateKey privateKey =
|
||||
// SM2X509CertMakerBase rootCertMaker = new SM2X509CertMakerBase(rootKP, expireTimeInMs, rootDN, snAllocator);
|
||||
|
||||
|
||||
// KeyPair subKeyPair = Sm2Base.generateKeyPair();
|
||||
// X509Certificate subCA = makeSubCACert(,"CN","MIN","MIN-GROUP","MIN SUB CA",365L * 24 * 60 * 60 * 1000,
|
||||
// SM3withSM2);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public interface X509CertSNAllocator {
|
||||
BigInteger nextSerialNumber() throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 15:59 2021/03/05
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class X509CertUtil {
|
||||
|
||||
public static boolean verify(X509Certificate certificate, PublicKey publicKey){
|
||||
try {
|
||||
certificate.verify(publicKey);
|
||||
|
||||
} catch (CertificateException | NoSuchAlgorithmException |
|
||||
InvalidKeyException | NoSuchProviderException | SignatureException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isExpired(X509Certificate certificate){
|
||||
Date notAfter = certificate.getNotAfter();
|
||||
Date notBefore = certificate.getNotBefore();
|
||||
long now = System.currentTimeMillis();
|
||||
return now < notBefore.getTime() || now > notAfter.getTime();
|
||||
}
|
||||
|
||||
public static boolean isValid(X509Certificate certificate, PublicKey publicKey){
|
||||
boolean isExpired = isExpired(certificate);
|
||||
boolean isVerify = verify(certificate, publicKey);
|
||||
return !isExpired & isVerify;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package minsecurity.certificate.X509;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class X509CommonUtil {
|
||||
/**
|
||||
* 如果不知道怎么填充names,可以查看org.bouncycastle.asn1.x500.style.BCStyle这个类,
|
||||
* names的key值必须是BCStyle.DefaultLookUp中存在的(可以不关心大小写)
|
||||
*
|
||||
* @param names
|
||||
* @return
|
||||
* @throws InvalidX500NameException
|
||||
*/
|
||||
public static X500Name buildX500Name(Map<String, String> names) throws InvalidX500NameException {
|
||||
if (names == null || names.size() == 0) {
|
||||
throw new InvalidX500NameException("names can not be empty");
|
||||
}
|
||||
try {
|
||||
X500NameBuilder builder = new X500NameBuilder();
|
||||
Iterator itr = names.entrySet().iterator();
|
||||
BCStyle x500NameStyle = (BCStyle) BCStyle.INSTANCE;
|
||||
Map.Entry entry;
|
||||
while (itr.hasNext()) {
|
||||
entry = (Map.Entry) itr.next();
|
||||
ASN1ObjectIdentifier oid = x500NameStyle.attrNameToOID((String) entry.getKey());
|
||||
builder.addRDN(oid, (String) entry.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
} catch (Exception ex) {
|
||||
throw new InvalidX500NameException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// public static PKCS10CertificationRequest createCSR(X500Name subject, SM2PublicKey pubKey, PrivateKey priKey,
|
||||
// String signAlgo) throws OperatorCreationException {
|
||||
// PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey);
|
||||
// ContentSigner signerBuilder = new JcaContentSignerBuilder(signAlgo)
|
||||
// .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey);
|
||||
// return csrBuilder.build(signerBuilder);
|
||||
// }
|
||||
|
||||
public static PKCS10CertificationRequest createCSR(X500Name subject, PublicKey pubKey, PrivateKey priKey,
|
||||
String signAlgo) throws OperatorCreationException {
|
||||
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, pubKey);
|
||||
ContentSigner signerBuilder = new JcaContentSignerBuilder(signAlgo)
|
||||
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(priKey);
|
||||
return csrBuilder.build(signerBuilder);
|
||||
}
|
||||
|
||||
public static AlgorithmIdentifier findSignatureAlgorithmIdentifier(String algoName) {
|
||||
DefaultSignatureAlgorithmIdentifierFinder sigFinder = new DefaultSignatureAlgorithmIdentifierFinder();
|
||||
return sigFinder.find(algoName);
|
||||
}
|
||||
|
||||
public static AlgorithmIdentifier findDigestAlgorithmIdentifier(String algoName) {
|
||||
DefaultDigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
|
||||
return digFinder.find(findSignatureAlgorithmIdentifier(algoName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package minsecurity.certificate.cert;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description: sm2的自定义异常
|
||||
* @Version: 1.0.0
|
||||
* @Date: 19:00 2021/03/03
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class CertException extends Exception {
|
||||
public CertException(String msg){
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package minsecurity.certificate.cert;
|
||||
|
||||
import minsecurity.Common;
|
||||
import minsecurity.crypto.PublicKeyInterface;
|
||||
import minsecurity.crypto.sm2.Sm2PublicKey;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 17:09 2021/03/05
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class CertUtils {
|
||||
|
||||
public static final int VERSION1 = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static Certificate parseInnerCertToCert(InnerCertificate i){
|
||||
Certificate certificate = new Certificate();
|
||||
|
||||
certificate.setVersion(i.getTbsCertificate().getVersion());
|
||||
certificate.setSerialNumber(i.getTbsCertificate().getSerialNumber());
|
||||
PublicKeyInterface publicKey = 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;
|
||||
}
|
||||
|
||||
public static PublicKeyInterface unMarshalPublicKey(byte[] bytesOfPublicKey, int algorithm) {
|
||||
if (bytesOfPublicKey == null || bytesOfPublicKey.length == 0) {
|
||||
return null;
|
||||
}
|
||||
switch (algorithm) {
|
||||
case Common.SM2:
|
||||
Sm2PublicKey sm2PublicKey = new Sm2PublicKey();
|
||||
sm2PublicKey.setBytes(bytesOfPublicKey);
|
||||
return sm2PublicKey;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkDuration(InnerCertificate innerCertificate){
|
||||
long curTime = System.currentTimeMillis();
|
||||
if(innerCertificate.getTbsCertificate().getNotAfter() > curTime)
|
||||
return false;
|
||||
if(innerCertificate.getTbsCertificate().getNotBefore() < curTime)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static boolean checkSign(InnerCertificate cert, PublicKeyInterface publicKey) throws Exception {
|
||||
switch (cert.getTbsCertificate().getSignatureAlgorithm()){
|
||||
case Common.SM3withSM2:
|
||||
// TODO: 证书序列化方法
|
||||
byte[] bytesOfCert = serialization(cert);
|
||||
// byte[] digest = HashAlgo.sm3(bytesOfCert);
|
||||
return publicKey.verify(bytesOfCert, cert.getSignatureValue());
|
||||
default:
|
||||
throw new Exception("未定义的签名方法");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 证书序列化方法
|
||||
public static byte[] serialization(InnerCertificate innerCertificate){
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
// TODO: 证书反序列化
|
||||
public static InnerCertificate deserialization(byte[] bytesOfCert){
|
||||
return new InnerCertificate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package minsecurity.certificate.cert;
|
||||
|
||||
import minsecurity.Common;
|
||||
import minsecurity.crypto.HashAlgo;
|
||||
import minsecurity.crypto.PrivateKeyInterface;
|
||||
import minsecurity.crypto.PublicKeyInterface;
|
||||
import minsecurity.crypto.Sm4;
|
||||
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.Base64;
|
||||
|
||||
import static minsecurity.certificate.cert.CertUtils.*;
|
||||
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 17:05 2021/03/05
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class Certificate {
|
||||
private int version;
|
||||
private long serialNumber;
|
||||
private PublicKeyInterface publicKey;
|
||||
private byte[] signature;
|
||||
private int signatureAlgorithm;
|
||||
private int publicKeyAlgorithm;
|
||||
private String issueTo;
|
||||
private String issuer;
|
||||
private long notBefore;
|
||||
private long notAfter;
|
||||
private int keyUsage;
|
||||
private boolean isCA;
|
||||
private long timestamp;
|
||||
|
||||
public Certificate() {
|
||||
}
|
||||
|
||||
public Certificate(int version, long serialNumber,
|
||||
PublicKeyInterface publicKey,
|
||||
byte[] signature, int signatureAlgorithm,
|
||||
int publicKeyAlgorithm,
|
||||
String issueTo, String issuer,
|
||||
long notBefore, long notAfter,
|
||||
int keyUsage, boolean isCA, long timestamp) {
|
||||
this.version = version;
|
||||
this.serialNumber = serialNumber;
|
||||
this.publicKey = publicKey;
|
||||
this.signature = signature;
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
this.publicKeyAlgorithm = publicKeyAlgorithm;
|
||||
this.issueTo = issueTo;
|
||||
this.issuer = issuer;
|
||||
this.notBefore = notBefore;
|
||||
this.notAfter = notAfter;
|
||||
this.keyUsage = keyUsage;
|
||||
this.isCA = isCA;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public long getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public void setSerialNumber(long serialNumber) {
|
||||
this.serialNumber = serialNumber;
|
||||
}
|
||||
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(byte[] signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getSignatureAlgorithm() {
|
||||
return signatureAlgorithm;
|
||||
}
|
||||
|
||||
public void setSignatureAlgorithm(int signatureAlgorithm) {
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
}
|
||||
|
||||
public int getPublicKeyAlgorithm() {
|
||||
return publicKeyAlgorithm;
|
||||
}
|
||||
|
||||
public void setPublicKeyAlgorithm(int publicKeyAlgorithm) {
|
||||
this.publicKeyAlgorithm = publicKeyAlgorithm;
|
||||
}
|
||||
|
||||
public String getIssueTo() {
|
||||
return issueTo;
|
||||
}
|
||||
|
||||
public void setIssueTo(String issueTo) {
|
||||
this.issueTo = issueTo;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public void setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
public long getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(long notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public long getNotAfter() {
|
||||
return notAfter;
|
||||
}
|
||||
|
||||
public void setNotAfter(long notAfter) {
|
||||
this.notAfter = notAfter;
|
||||
}
|
||||
|
||||
public int getKeyUsage() {
|
||||
return keyUsage;
|
||||
}
|
||||
|
||||
public void setKeyUsage(int keyUsage) {
|
||||
this.keyUsage = keyUsage;
|
||||
}
|
||||
|
||||
public boolean isCA() {
|
||||
return isCA;
|
||||
}
|
||||
|
||||
public void setCA(boolean CA) {
|
||||
isCA = CA;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public PublicKeyInterface getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(PublicKeyInterface publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
|
||||
public void signCert(PrivateKeyInterface priv) throws Exception {
|
||||
InnerCertificate inner = CertUtils.parseCertToInnerCert(this);
|
||||
byte[] bytesOfCert = serialization(CertUtils.parseCertToInnerCert(this));
|
||||
switch (this.signatureAlgorithm){
|
||||
case Common.SM3withSM2:
|
||||
// byte[] digest = HashAlgo.sm3(bytesOfCert);
|
||||
byte[] sign = priv.sign(bytesOfCert);
|
||||
this.signature = sign;
|
||||
return;
|
||||
default:
|
||||
throw new Exception("未定义的签名方法");
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
if(!isValid)
|
||||
return false;
|
||||
if(sub.isCA()){
|
||||
return checkSign(innerCertificate, sub.getPublicKey());
|
||||
} else {
|
||||
return checkSign(innerCertificate, ca.getPublicKey());
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String toPem(byte[] passwd, int symAlgoMode) throws CertException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
|
||||
if(this.getSignature() == null){
|
||||
throw new CertException("该证书未签名");
|
||||
}
|
||||
byte[] bytesOfCert = serialization(CertUtils.parseCertToInnerCert(this));
|
||||
byte[] ret = new byte[0];
|
||||
if(passwd.length > 0){
|
||||
byte[] hashPasswd = HashAlgo.sm3(passwd);
|
||||
if( hashPasswd.length == 32){
|
||||
for(int i = 0; i < 16; i++){
|
||||
hashPasswd[i] += hashPasswd[i + 16];
|
||||
}
|
||||
}
|
||||
byte[] sm4Key = new byte[16];
|
||||
System.arraycopy(hashPasswd,0,sm4Key,0,16);
|
||||
|
||||
switch (symAlgoMode){
|
||||
case Common.SM4CBC:{
|
||||
// TODO: PADDING??? IV?????
|
||||
ret = Sm4.encrypt_CBC_NoPadding(sm4Key, new byte[16],bytesOfCert);
|
||||
}
|
||||
case Common.SM4ECB:{
|
||||
ret = Sm4.encrypt_ECB_NoPadding(sm4Key, bytesOfCert);
|
||||
}
|
||||
default:
|
||||
throw new CertException("未定义的对称加密算法");
|
||||
}
|
||||
} else {
|
||||
ret = bytesOfCert;
|
||||
}
|
||||
return Base64.getEncoder().encodeToString(ret);
|
||||
}
|
||||
|
||||
public 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 = new byte[0];
|
||||
if(passwd.length > 0){
|
||||
byte[] hashPasswd = HashAlgo.sm3(passwd);
|
||||
if( hashPasswd.length == 32){
|
||||
for(int i = 0; i < 16; i++){
|
||||
hashPasswd[i] += hashPasswd[i + 16];
|
||||
}
|
||||
}
|
||||
byte[] sm4Key = new byte[16];
|
||||
System.arraycopy(hashPasswd,0,sm4Key,0,16);
|
||||
|
||||
switch (symAlgoMode){
|
||||
case Common.SM4CBC:
|
||||
bytesOfCert = Sm4.decrypt_CBC_Padding(sm4Key,new byte[16], bytesOfPem);
|
||||
case Common.SM4ECB:
|
||||
bytesOfCert = Sm4.decrypt_ECB_Padding(sm4Key, bytesOfPem);
|
||||
}
|
||||
} else {
|
||||
bytesOfCert = bytesOfPem;
|
||||
}
|
||||
InnerCertificate innerCertificate = deserialization(bytesOfCert);
|
||||
return CertUtils.parseInnerCertToCert(innerCertificate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package minsecurity.certificate.cert;
|
||||
|
||||
import minsecurity.Common;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 17:07 2021/03/05
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class InnerCertificate {
|
||||
private TbsCertificate tbsCertificate;
|
||||
private int signatureAlgorithm;
|
||||
private byte[] signatureValue;
|
||||
|
||||
public TbsCertificate getTbsCertificate() {
|
||||
return tbsCertificate;
|
||||
}
|
||||
|
||||
public void setTbsCertificate(TbsCertificate tbsCertificate) {
|
||||
this.tbsCertificate = tbsCertificate;
|
||||
}
|
||||
|
||||
public int getSignatureAlgorithm() {
|
||||
return signatureAlgorithm;
|
||||
}
|
||||
|
||||
public void setSignatureAlgorithm(int signatureAlgorithm) {
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
}
|
||||
|
||||
public byte[] getSignatureValue() {
|
||||
return signatureValue;
|
||||
}
|
||||
|
||||
public void setSignatureValue(byte[] signatureValue) {
|
||||
this.signatureValue = signatureValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package minsecurity.certificate.cert;
|
||||
|
||||
import minsecurity.Common;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 17:00 2021/03/05
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class TbsCertificate {
|
||||
private int version;
|
||||
private long serialNumber;
|
||||
private byte[] publicKey;
|
||||
private int signatureAlgorithm;
|
||||
private int publicKeyAlgorithm;
|
||||
private String issueTo;
|
||||
private String issuer;
|
||||
private long notBefore;
|
||||
private long notAfter;
|
||||
private int keyUsage;
|
||||
private boolean isCA;
|
||||
private long timestamp;
|
||||
|
||||
|
||||
public TbsCertificate() {
|
||||
}
|
||||
|
||||
public TbsCertificate(int version, long serialNumber,
|
||||
byte[] publicKey, int signatureAlgorithm,
|
||||
int publicKeyAlgorithm,
|
||||
String issueTo, String issuer,
|
||||
long notBefore, long notAfter,
|
||||
int keyUsage, boolean isCA, long timestamp) {
|
||||
this.version = version;
|
||||
this.serialNumber = serialNumber;
|
||||
this.publicKey = publicKey;
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
this.publicKeyAlgorithm = publicKeyAlgorithm;
|
||||
this.issueTo = issueTo;
|
||||
this.issuer = issuer;
|
||||
this.notBefore = notBefore;
|
||||
this.notAfter = notAfter;
|
||||
this.keyUsage = keyUsage;
|
||||
this.isCA = isCA;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public long getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
public void setSerialNumber(long serialNumber) {
|
||||
this.serialNumber = serialNumber;
|
||||
}
|
||||
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public void setPublicKey(byte[] publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public int getSignatureAlgorithm() {
|
||||
return signatureAlgorithm;
|
||||
}
|
||||
|
||||
public void setSignatureAlgorithm(int signatureAlgorithm) {
|
||||
this.signatureAlgorithm = signatureAlgorithm;
|
||||
}
|
||||
|
||||
public int getPublicKeyAlgorithm() {
|
||||
return publicKeyAlgorithm;
|
||||
}
|
||||
|
||||
public void setPublicKeyAlgorithm(int publicKeyAlgorithm) {
|
||||
this.publicKeyAlgorithm = publicKeyAlgorithm;
|
||||
}
|
||||
|
||||
public String getIssueTo() {
|
||||
return issueTo;
|
||||
}
|
||||
|
||||
public void setIssueTo(String issueTo) {
|
||||
this.issueTo = issueTo;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public void setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
public long getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(long notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public long getNotAfter() {
|
||||
return notAfter;
|
||||
}
|
||||
|
||||
public void setNotAfter(long notAfter) {
|
||||
this.notAfter = notAfter;
|
||||
}
|
||||
|
||||
public int getKeyUsage() {
|
||||
return keyUsage;
|
||||
}
|
||||
|
||||
public void setKeyUsage(int keyUsage) {
|
||||
this.keyUsage = keyUsage;
|
||||
}
|
||||
|
||||
public boolean isCA() {
|
||||
return isCA;
|
||||
}
|
||||
|
||||
public void setCA(boolean CA) {
|
||||
isCA = CA;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package minsecurity.crypto;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.codec.digest.HmacAlgorithms;
|
||||
import org.apache.commons.codec.digest.HmacUtils;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description: 封装常用的hash算法,包括sm3、sha256、sha512、md5, 大量应用apache commons-codec库
|
||||
* @Version: 1.0.0
|
||||
* @Date: 15:10 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class HashAlgo {
|
||||
// TODO: 单元测试
|
||||
///////////////
|
||||
///// SM3 /////
|
||||
///////////////
|
||||
public static byte[] sm3(byte[] content){
|
||||
SM3Digest digest = new SM3Digest();
|
||||
digest.update(content, 0, content.length);
|
||||
byte[] hash = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(hash, 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static byte[] sm3(String content){
|
||||
return sm3(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static byte[] sm3(String content, Charset charset){
|
||||
return sm3(content.getBytes(charset));
|
||||
}
|
||||
|
||||
////////////////////
|
||||
///// SM3-HMAC /////
|
||||
////////////////////
|
||||
public static byte[] hmacSM3(byte[] content, byte[] key){
|
||||
KeyParameter keyParameter = new KeyParameter(key);
|
||||
SM3Digest digest = new SM3Digest();
|
||||
HMac mac = new HMac(digest);
|
||||
mac.init(keyParameter);
|
||||
mac.update(content, 0, content.length);
|
||||
byte[] result = new byte[mac.getMacSize()];
|
||||
mac.doFinal(result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] hmacSM3(String content, byte[] key){
|
||||
return hmacSM3(content.getBytes(StandardCharsets.UTF_8),key);
|
||||
}
|
||||
|
||||
public static byte[] hmacSM3(String content, byte[] key, Charset charset){
|
||||
return hmacSM3(content.getBytes(charset),key);
|
||||
}
|
||||
|
||||
///////////////
|
||||
///// MD5 /////
|
||||
///////////////
|
||||
public static byte[] md5(String content, Charset charset){
|
||||
byte[] bytesOfContent = content.getBytes(charset);
|
||||
return md5(bytesOfContent);
|
||||
}
|
||||
|
||||
public static byte[] md5(String content){
|
||||
return DigestUtils.md5(content);
|
||||
}
|
||||
|
||||
public static byte[] md5(byte[] content){
|
||||
|
||||
return DigestUtils.md5(content);
|
||||
}
|
||||
|
||||
////////////////
|
||||
//// SHA256 ////
|
||||
////////////////
|
||||
public static byte[] sha256(byte[] content){
|
||||
return DigestUtils.sha256(content);
|
||||
}
|
||||
|
||||
public static byte[] sha256(String content){
|
||||
return DigestUtils.sha256(content);
|
||||
}
|
||||
|
||||
public static byte[] sha256(String content, Charset charset){
|
||||
byte[] bytesOfContent = content.getBytes(charset);
|
||||
return DigestUtils.sha256(bytesOfContent);
|
||||
}
|
||||
|
||||
////////////////
|
||||
//// SHA512 ////
|
||||
////////////////
|
||||
public static byte[] sha512(byte[] content){
|
||||
return DigestUtils.sha512(content);
|
||||
}
|
||||
|
||||
public static byte[] sha512(String content){
|
||||
return DigestUtils.sha512(content);
|
||||
}
|
||||
|
||||
public static byte[] sha512(String content, Charset charset){
|
||||
byte[] bytesOfContent = content.getBytes(charset);
|
||||
return DigestUtils.sha512(bytesOfContent);
|
||||
}
|
||||
|
||||
//////////////////
|
||||
//// HMAC-MD5 ////
|
||||
//////////////////
|
||||
public static byte[] hmacMD5(String content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_MD5,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacMD5(byte[] content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_MD5,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacMD5(String content, byte[] key, Charset charset){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_MD5,key);
|
||||
return hmacMd5Util.hmac(content.getBytes(charset));
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
//// SHA256-MD5 ////
|
||||
////////////////////
|
||||
public static byte[] hmacSHA256(String content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_256,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA256(byte[] content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_256,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA256(String content, byte[] key, Charset charset){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_256,key);
|
||||
return hmacMd5Util.hmac(content.getBytes(charset));
|
||||
}
|
||||
|
||||
////////////////////
|
||||
//// SHA512-MD5 ////
|
||||
////////////////////
|
||||
public static byte[] hmacSHA512(String content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_512,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA512(byte[] content, byte[] key){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_512,key);
|
||||
return hmacMd5Util.hmac(content);
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA512(String content, byte[] key, Charset charset){
|
||||
HmacUtils hmacMd5Util = new HmacUtils(HmacAlgorithms.HMAC_SHA_512,key);
|
||||
return hmacMd5Util.hmac(content.getBytes(charset));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package minsecurity;
|
||||
package minsecurity.crypto;
|
||||
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package minsecurity;
|
||||
package minsecurity.crypto;
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description: 所有对称加密方法必须实现的基本方法
|
||||
@@ -0,0 +1,120 @@
|
||||
package minsecurity.crypto;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.*;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description: sm4算法
|
||||
* @Version: 1.0.0
|
||||
* @Date: 16:26 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class Sm4 {
|
||||
static {
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
public static final String ALGORITHM_NAME = "SM4";
|
||||
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
|
||||
public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";
|
||||
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
|
||||
public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";
|
||||
|
||||
/**
|
||||
* SM4算法目前只支持128位(即密钥16字节)
|
||||
*/
|
||||
public static final int DEFAULT_KEY_SIZE = 128;
|
||||
|
||||
public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||
return generateKey(DEFAULT_KEY_SIZE);
|
||||
}
|
||||
|
||||
public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
|
||||
kg.init(keySize, new SecureRandom());
|
||||
return kg.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
|
||||
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText)
|
||||
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
|
||||
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
|
||||
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText)
|
||||
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
|
||||
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
|
||||
InvalidAlgorithmParameterException {
|
||||
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText)
|
||||
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||
InvalidAlgorithmParameterException {
|
||||
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
|
||||
InvalidAlgorithmParameterException {
|
||||
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText)
|
||||
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
|
||||
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||
InvalidAlgorithmParameterException {
|
||||
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)
|
||||
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
|
||||
InvalidKeyException {
|
||||
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||
cipher.init(mode, sm4Key);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
|
||||
throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
|
||||
NoSuchProviderException, NoSuchPaddingException {
|
||||
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
||||
cipher.init(mode, sm4Key, ivParameterSpec);
|
||||
return cipher;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,29 @@
|
||||
package minsecurity.crypto.sm2;
|
||||
|
||||
import minsecurity.Common;
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
|
||||
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.KeyGenerator;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.*;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import static minsecurity.Common.ALGO_NAME_EC;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
@@ -20,6 +33,11 @@ import java.security.SecureRandom;
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class Sm2Base {
|
||||
|
||||
static {
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Sm2Base.class);
|
||||
public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
|
||||
public final static BigInteger SM2_ECC_P = CURVE.getQ();
|
||||
@@ -35,6 +53,9 @@ public class Sm2Base {
|
||||
public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT,
|
||||
SM2_ECC_N, SM2_ECC_H);
|
||||
|
||||
private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
|
||||
private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";
|
||||
|
||||
public static AsymmetricCipherKeyPair generateKeyPairParameter(){
|
||||
SecureRandom random = new SecureRandom();
|
||||
ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(DOMAIN_PARAMS,
|
||||
@@ -42,8 +63,55 @@ public class Sm2Base {
|
||||
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
|
||||
keyGen.init(keyGenerationParams);
|
||||
AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair();
|
||||
ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) keyPair.getPrivate();
|
||||
logger.debug("priv D: bitlen = {}, d.len = {}",privateKeyParameters.getD().bitLength(),privateKeyParameters.getD().toByteArray().length);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
SecureRandom random = new SecureRandom();
|
||||
return generateKeyPair(DOMAIN_PARAMS, random);
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random)
|
||||
throws NoSuchProviderException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
|
||||
ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(),
|
||||
domainParameters.getN(), domainParameters.getH());
|
||||
kpg.initialize(parameterSpec, random);
|
||||
return kpg.generateKeyPair();
|
||||
}
|
||||
|
||||
|
||||
public static KeyPair convertBcToJceKeyPair(AsymmetricCipherKeyPair bcKeyPair) throws Exception {
|
||||
byte[] pkcs8Encoded = PrivateKeyInfoFactory.createPrivateKeyInfo(bcKeyPair.getPrivate()).getEncoded();
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(pkcs8Encoded);
|
||||
byte[] spkiEncoded = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(bcKeyPair.getPublic()).getEncoded();
|
||||
X509EncodedKeySpec spkiKeySpec = new X509EncodedKeySpec(spkiEncoded);
|
||||
KeyFactory keyFac = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
|
||||
return new KeyPair(keyFac.generatePublic(spkiKeySpec), keyFac.generatePrivate(pkcs8KeySpec));
|
||||
}
|
||||
|
||||
public static AsymmetricCipherKeyPair convertKeyPair2Bc(KeyPair keyPair) {
|
||||
ECPrivateKeyParameters priKey = convertPrivateKeyToParameters((BCECPrivateKey) keyPair.getPrivate());
|
||||
ECPublicKeyParameters pubKey = convertPublicKeyToParameters((BCECPublicKey) keyPair.getPublic());
|
||||
return new AsymmetricCipherKeyPair(priKey,pubKey);
|
||||
}
|
||||
|
||||
|
||||
public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) {
|
||||
ECParameterSpec parameterSpec = ecPriKey.getParameters();
|
||||
ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
|
||||
parameterSpec.getN(), parameterSpec.getH());
|
||||
return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
|
||||
}
|
||||
|
||||
public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) {
|
||||
ECParameterSpec parameterSpec = ecPubKey.getParameters();
|
||||
ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
|
||||
parameterSpec.getN(), parameterSpec.getH());
|
||||
return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package minsecurity.crypto.sm2;
|
||||
|
||||
import minsecurity.PrivateKeyInterface;
|
||||
import minsecurity.crypto.PrivateKeyInterface;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
||||
import org.bouncycastle.crypto.signers.SM2Signer;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package minsecurity.crypto.sm2;
|
||||
|
||||
import minsecurity.PublicKeyInterface;
|
||||
import minsecurity.crypto.PublicKeyInterface;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
@@ -22,7 +22,7 @@ import java.security.SecureRandom;
|
||||
*/
|
||||
public class Sm2PublicKey implements PublicKeyInterface {
|
||||
|
||||
Sm2PublicKey(){}
|
||||
public Sm2PublicKey(){}
|
||||
|
||||
public Sm2PublicKey(byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters){
|
||||
publicKey = createPublicKey(xBytes,yBytes,curve,domainParameters);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class FileUtil {
|
||||
public static void writeFile(String filePath, byte[] data) throws IOException {
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
raf = new RandomAccessFile(filePath, "rw");
|
||||
raf.write(data);
|
||||
} finally {
|
||||
if (raf != null) {
|
||||
raf.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readFile(String filePath) throws IOException {
|
||||
RandomAccessFile raf = null;
|
||||
byte[] data;
|
||||
try {
|
||||
raf = new RandomAccessFile(filePath, "r");
|
||||
data = new byte[(int) raf.length()];
|
||||
raf.read(data);
|
||||
return data;
|
||||
} finally {
|
||||
if (raf != null) {
|
||||
raf.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package minsecurity.crypto;
|
||||
|
||||
import minsecurity.Common;
|
||||
import minsecurity.certificate.X509.X509CertMaker;
|
||||
import minsecurity.certificate.X509.X509CertUtil;
|
||||
import minsecurity.crypto.sm2.Sm2Base;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemWriter;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import util.FileUtil;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
|
||||
import static minsecurity.certificate.X509.X509CertMaker.makeRootCA;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description:
|
||||
* @Version: 1.0.0
|
||||
* @Date: 22:55 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class TestCert {
|
||||
static {
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(TestCert.class);
|
||||
private static final Provider BC = new BouncyCastleProvider();
|
||||
|
||||
@Test
|
||||
public void testRebuildKeyPair(){
|
||||
try {
|
||||
KeyPair rootKP = Sm2Base.generateKeyPair();
|
||||
X509Certificate rootCA = makeRootCA(rootKP,"CN","MIN",
|
||||
"MIN-GROUP","MIN ROOT CA",
|
||||
365L * 24 * 60 * 60 * 1000, "SM3withSM2");
|
||||
FileUtil.writeFile("./target/root.cer",rootCA.getEncoded());
|
||||
FileUtil.writeFile("./target/root.priv",rootKP.getPrivate().getEncoded());
|
||||
rootCA.verify(rootKP.getPublic());
|
||||
|
||||
System.out.println(rootKP.getPrivate().getFormat());
|
||||
CertificateFactory fact = CertificateFactory.getInstance(Common.X509, Common.BC_NAME);
|
||||
X509Certificate cert =
|
||||
(X509Certificate) fact.generateCertificate(
|
||||
new ByteArrayInputStream(rootCA.getEncoded()));
|
||||
|
||||
PublicKey publicKey = cert.getPublicKey();
|
||||
KeyFactory keyFac = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||
byte[] privBytes = FileUtil.readFile("./target/root.priv");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
|
||||
PrivateKey privateKey = keyFac.generatePrivate(keySpec);
|
||||
KeyPair rootKeyPair = new KeyPair(publicKey, privateKey);
|
||||
|
||||
logger.debug("origin: {}", ByteUtils.toHexString(rootKP.getPrivate().getEncoded()));
|
||||
logger.debug("rebuild:{}", ByteUtils.toHexString(rootKeyPair.getPrivate().getEncoded()));
|
||||
|
||||
logger.debug("origin: {}", ByteUtils.toHexString(rootKP.getPublic().getEncoded()));
|
||||
logger.debug("rebuild:{}", ByteUtils.toHexString(rootKeyPair.getPublic().getEncoded()));
|
||||
|
||||
KeyPair subKeyPair = Sm2Base.generateKeyPair();
|
||||
X509Certificate subCert =
|
||||
X509CertMaker.makeSubCACert(rootKeyPair,
|
||||
cert,
|
||||
subKeyPair,
|
||||
"c","o","ou","cn",
|
||||
365L * 24 * 60 * 60 * 1000,
|
||||
"SM3withSM2");
|
||||
FileUtil.writeFile("./target/sub.cer",subCert.getEncoded());
|
||||
|
||||
subCert.verify(rootKeyPair.getPublic());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testVerify() throws IOException, CertificateException, NoSuchProviderException {
|
||||
// 保存在本地的已有证书
|
||||
byte[] bytesOfCert = FileUtil.readFile("./target/root.cer");
|
||||
CertificateFactory fact = CertificateFactory.getInstance(Common.X509, "BC");
|
||||
X509Certificate cert =
|
||||
(X509Certificate) fact.generateCertificate(
|
||||
new ByteArrayInputStream(bytesOfCert));
|
||||
assertFalse(X509CertUtil.isExpired(cert));
|
||||
assertTrue(X509CertUtil.isValid(cert,cert.getPublicKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testX509toPem() throws IOException, CertificateException, NoSuchProviderException {
|
||||
byte[] bytesOfCert = FileUtil.readFile("./target/root.cer");
|
||||
CertificateFactory fact = CertificateFactory.getInstance(Common.X509, "BC");
|
||||
X509Certificate cert =
|
||||
(X509Certificate) fact.generateCertificate(
|
||||
new ByteArrayInputStream(bytesOfCert));
|
||||
StringWriter sw = new StringWriter();
|
||||
PemObject pemCSR = new PemObject("CERTIFICATE REQUEST", cert.getEncoded());
|
||||
PemWriter pemWriter = new PemWriter(sw);
|
||||
pemWriter.writeObject(pemCSR);
|
||||
pemWriter.close();
|
||||
logger.debug(sw.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package minsecurity.crypto;
|
||||
|
||||
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
* @Description: sm4单元测试
|
||||
* @Version: 1.0.0
|
||||
* @Date: 16:28 2021/03/04
|
||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||
*/
|
||||
public class TestSm4 {
|
||||
public static final byte[] SRC_DATA = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
|
||||
public static final byte[] SRC_DATA_16B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
public static final byte[] SRC_DATA_24B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
public static final byte[] SRC_DATA_32B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
public static final byte[] SRC_DATA_48B = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 , 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
public static final byte[] WITH_ID = new byte[]{1, 2, 3, 4};
|
||||
|
||||
@Test
|
||||
public void testEncryptAndDecrypt() {
|
||||
try {
|
||||
byte[] key = Sm4.generateKey();
|
||||
byte[] iv = Sm4.generateKey();
|
||||
byte[] cipherText = null;
|
||||
byte[] decryptedData = null;
|
||||
System.out.println("key: " + ByteUtils.toHexString(key));
|
||||
System.out.println("iv: " + ByteUtils.toHexString(iv));
|
||||
|
||||
cipherText = Sm4.encrypt_ECB_NoPadding(key, SRC_DATA_16B);
|
||||
System.out.println("SM4 ECB NoPadding encrypt result:\n" + Arrays.toString(cipherText));
|
||||
System.out.println(ByteUtils.toHexString(cipherText));
|
||||
decryptedData = Sm4.decrypt_ECB_NoPadding(key, cipherText);
|
||||
System.out.println("SM4 ECB NoPadding decrypt result:\n" + Arrays.toString(decryptedData));
|
||||
if (!Arrays.equals(decryptedData, SRC_DATA_16B)) {
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
cipherText = Sm4.encrypt_ECB_Padding(key, SRC_DATA_16B);
|
||||
System.out.println("SM4 ECB Padding encrypt result:\n" + Arrays.toString(cipherText));
|
||||
System.out.println(ByteUtils.toHexString(cipherText));
|
||||
decryptedData = Sm4.decrypt_ECB_Padding(key, cipherText);
|
||||
System.out.println("SM4 ECB Padding decrypt result:\n" + Arrays.toString(decryptedData));
|
||||
if (!Arrays.equals(decryptedData, SRC_DATA_16B)) {
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
cipherText = Sm4.encrypt_CBC_Padding(key, iv, SRC_DATA);
|
||||
System.out.println("SM4 CBC Padding encrypt result:\n" + Arrays.toString(cipherText));
|
||||
decryptedData = Sm4.decrypt_CBC_Padding(key, iv, cipherText);
|
||||
System.out.println("SM4 CBC Padding decrypt result:\n" + Arrays.toString(decryptedData));
|
||||
if (!Arrays.equals(decryptedData, SRC_DATA)) {
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
cipherText = Sm4.encrypt_CBC_NoPadding(key, iv, SRC_DATA_16B);
|
||||
System.out.println("SM4 CBC NoPadding encrypt result:\n" + Arrays.toString(cipherText));
|
||||
decryptedData = Sm4.decrypt_CBC_NoPadding(key, iv, cipherText);
|
||||
System.out.println("SM4 CBC NoPadding decrypt result:\n" + Arrays.toString(decryptedData));
|
||||
if (!Arrays.equals(decryptedData, SRC_DATA_16B)) {
|
||||
Assert.fail();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,7 @@ import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
/*
|
||||
* @Author: hongyu guo
|
||||
|
||||
@@ -130,9 +130,9 @@
|
||||
<!-- 控制台输出 -->
|
||||
<appender-ref ref="console" />
|
||||
<!-- 文件输出 -->
|
||||
<appender-ref ref="ERROR" />
|
||||
<appender-ref ref="INFO" />
|
||||
<appender-ref ref="WARN" />
|
||||
<!-- <appender-ref ref="ERROR" />-->
|
||||
<!-- <appender-ref ref="INFO" />-->
|
||||
<!-- <appender-ref ref="WARN" />-->
|
||||
<!-- <appender-ref ref="DEBUG" />-->
|
||||
<!-- <appender-ref ref="TRACE" />-->
|
||||
</root>
|
||||
Reference in New Issue
Block a user