diff --git a/pom.xml b/pom.xml
index 3d48976..1776a55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,15 @@
logback-classic
1.2.3
+
+
+
+
+ commons-codec
+ commons-codec
+ 1.15
+
+
\ No newline at end of file
diff --git a/src/main/java/minsecurity/Common.java b/src/main/java/minsecurity/Common.java
new file mode 100644
index 0000000..2c00384
--- /dev/null
+++ b/src/main/java/minsecurity/Common.java
@@ -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;
+ // }
+
+}
diff --git a/src/main/java/minsecurity/certificate/X509/InvalidX500NameException.java b/src/main/java/minsecurity/certificate/X509/InvalidX500NameException.java
new file mode 100644
index 0000000..29e7d88
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/InvalidX500NameException.java
@@ -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);
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/X509/RandomSNAllocatorX509.java b/src/main/java/minsecurity/certificate/X509/RandomSNAllocatorX509.java
new file mode 100644
index 0000000..62d3274
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/RandomSNAllocatorX509.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/minsecurity/certificate/X509/SM2X509CertMakerBase.java b/src/main/java/minsecurity/certificate/X509/SM2X509CertMakerBase.java
new file mode 100644
index 0000000..4512eb4
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/SM2X509CertMakerBase.java
@@ -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 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 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());
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/X509/X509CertMaker.java b/src/main/java/minsecurity/certificate/X509/X509CertMaker.java
new file mode 100644
index 0000000..dabc5ab
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/X509CertMaker.java
@@ -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();
+ }
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/X509/X509CertSNAllocator.java b/src/main/java/minsecurity/certificate/X509/X509CertSNAllocator.java
new file mode 100644
index 0000000..5b16409
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/X509CertSNAllocator.java
@@ -0,0 +1,7 @@
+package minsecurity.certificate.X509;
+
+import java.math.BigInteger;
+
+public interface X509CertSNAllocator {
+ BigInteger nextSerialNumber() throws Exception;
+}
diff --git a/src/main/java/minsecurity/certificate/X509/X509CertUtil.java b/src/main/java/minsecurity/certificate/X509/X509CertUtil.java
new file mode 100644
index 0000000..441e1a9
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/X509CertUtil.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/minsecurity/certificate/X509/X509CommonUtil.java b/src/main/java/minsecurity/certificate/X509/X509CommonUtil.java
new file mode 100644
index 0000000..e05e1ac
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/X509/X509CommonUtil.java
@@ -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 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));
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/cert/CertException.java b/src/main/java/minsecurity/certificate/cert/CertException.java
new file mode 100644
index 0000000..108b6bd
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/cert/CertException.java
@@ -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);
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/cert/CertUtils.java b/src/main/java/minsecurity/certificate/cert/CertUtils.java
new file mode 100644
index 0000000..061a80c
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/cert/CertUtils.java
@@ -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();
+ }
+
+}
diff --git a/src/main/java/minsecurity/certificate/cert/Certificate.java b/src/main/java/minsecurity/certificate/cert/Certificate.java
new file mode 100644
index 0000000..4d836d2
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/cert/Certificate.java
@@ -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);
+ }
+
+}
diff --git a/src/main/java/minsecurity/certificate/cert/InnerCertificate.java b/src/main/java/minsecurity/certificate/cert/InnerCertificate.java
new file mode 100644
index 0000000..1ecd54f
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/cert/InnerCertificate.java
@@ -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;
+ }
+}
diff --git a/src/main/java/minsecurity/certificate/cert/TbsCertificate.java b/src/main/java/minsecurity/certificate/cert/TbsCertificate.java
new file mode 100644
index 0000000..a2bf3e7
--- /dev/null
+++ b/src/main/java/minsecurity/certificate/cert/TbsCertificate.java
@@ -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;
+ }
+}
diff --git a/src/main/java/minsecurity/crypto/HashAlgo.java b/src/main/java/minsecurity/crypto/HashAlgo.java
new file mode 100644
index 0000000..3f52396
--- /dev/null
+++ b/src/main/java/minsecurity/crypto/HashAlgo.java
@@ -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));
+ }
+}
diff --git a/src/main/java/minsecurity/PrivateKeyInterface.java b/src/main/java/minsecurity/crypto/PrivateKeyInterface.java
similarity index 95%
rename from src/main/java/minsecurity/PrivateKeyInterface.java
rename to src/main/java/minsecurity/crypto/PrivateKeyInterface.java
index 6d99120..7cd3cbd 100644
--- a/src/main/java/minsecurity/PrivateKeyInterface.java
+++ b/src/main/java/minsecurity/crypto/PrivateKeyInterface.java
@@ -1,4 +1,4 @@
-package minsecurity;
+package minsecurity.crypto;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
diff --git a/src/main/java/minsecurity/PublicKeyInterface.java b/src/main/java/minsecurity/crypto/PublicKeyInterface.java
similarity index 95%
rename from src/main/java/minsecurity/PublicKeyInterface.java
rename to src/main/java/minsecurity/crypto/PublicKeyInterface.java
index 3e1be32..c077f90 100644
--- a/src/main/java/minsecurity/PublicKeyInterface.java
+++ b/src/main/java/minsecurity/crypto/PublicKeyInterface.java
@@ -1,4 +1,4 @@
-package minsecurity;
+package minsecurity.crypto;
/*
* @Author: hongyu guo
* @Description: 所有对称加密方法必须实现的基本方法
diff --git a/src/main/java/minsecurity/crypto/Sm4.java b/src/main/java/minsecurity/crypto/Sm4.java
new file mode 100644
index 0000000..d0cc585
--- /dev/null
+++ b/src/main/java/minsecurity/crypto/Sm4.java
@@ -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;
+ }
+}
diff --git a/src/main/java/minsecurity/crypto/sm2/Sm2Base.java b/src/main/java/minsecurity/crypto/sm2/Sm2Base.java
index b489aba..9304228 100644
--- a/src/main/java/minsecurity/crypto/sm2/Sm2Base.java
+++ b/src/main/java/minsecurity/crypto/sm2/Sm2Base.java
@@ -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);
+ }
+
+
}
diff --git a/src/main/java/minsecurity/crypto/sm2/Sm2PrivateKey.java b/src/main/java/minsecurity/crypto/sm2/Sm2PrivateKey.java
index 60377d8..8dd70ef 100644
--- a/src/main/java/minsecurity/crypto/sm2/Sm2PrivateKey.java
+++ b/src/main/java/minsecurity/crypto/sm2/Sm2PrivateKey.java
@@ -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;
diff --git a/src/main/java/minsecurity/crypto/sm2/Sm2PublicKey.java b/src/main/java/minsecurity/crypto/sm2/Sm2PublicKey.java
index a954d0c..9251ff2 100644
--- a/src/main/java/minsecurity/crypto/sm2/Sm2PublicKey.java
+++ b/src/main/java/minsecurity/crypto/sm2/Sm2PublicKey.java
@@ -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);
diff --git a/src/main/java/util/FileUtil.java b/src/main/java/util/FileUtil.java
new file mode 100644
index 0000000..a7c4f8e
--- /dev/null
+++ b/src/main/java/util/FileUtil.java
@@ -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();
+ }
+ }
+ }
+}
diff --git a/src/test/java/minsecurity/crypto/TestCert.java b/src/test/java/minsecurity/crypto/TestCert.java
new file mode 100644
index 0000000..cfebe2c
--- /dev/null
+++ b/src/test/java/minsecurity/crypto/TestCert.java
@@ -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());
+ }
+}
diff --git a/src/test/java/minsecurity/crypto/TestSm4.java b/src/test/java/minsecurity/crypto/TestSm4.java
new file mode 100644
index 0000000..34cd1a8
--- /dev/null
+++ b/src/test/java/minsecurity/crypto/TestSm4.java
@@ -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();
+ }
+ }
+}
diff --git a/src/test/java/minsecurity/crypto/sm2/TestSm2.java b/src/test/java/minsecurity/crypto/sm2/TestSm2.java
index 9ab52f7..d28b6d0 100644
--- a/src/test/java/minsecurity/crypto/sm2/TestSm2.java
+++ b/src/test/java/minsecurity/crypto/sm2/TestSm2.java
@@ -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
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback-test.xml
similarity index 97%
rename from src/test/resources/logback.xml
rename to src/test/resources/logback-test.xml
index efa15ce..b23a673 100644
--- a/src/test/resources/logback.xml
+++ b/src/test/resources/logback-test.xml
@@ -130,9 +130,9 @@
-
-
-
+
+
+