From 3868d65fae6e7d38074c62246c4fed76134ef727 Mon Sep 17 00:00:00 2001 From: ghy Date: Fri, 5 Mar 2021 20:02:38 +0800 Subject: [PATCH] =?UTF-8?q?hash=20&=20sm4=20&=20cert(=E6=9C=AA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 9 + src/main/java/minsecurity/Common.java | 47 +++ .../X509/InvalidX500NameException.java | 21 ++ .../X509/RandomSNAllocatorX509.java | 78 +++++ .../X509/SM2X509CertMakerBase.java | 273 ++++++++++++++++++ .../certificate/X509/X509CertMaker.java | 158 ++++++++++ .../certificate/X509/X509CertSNAllocator.java | 7 + .../certificate/X509/X509CertUtil.java | 42 +++ .../certificate/X509/X509CommonUtil.java | 77 +++++ .../certificate/cert/CertException.java | 14 + .../certificate/cert/CertUtils.java | 107 +++++++ .../certificate/cert/Certificate.java | 269 +++++++++++++++++ .../certificate/cert/InnerCertificate.java | 40 +++ .../certificate/cert/TbsCertificate.java | 145 ++++++++++ .../java/minsecurity/crypto/HashAlgo.java | 167 +++++++++++ .../{ => crypto}/PrivateKeyInterface.java | 2 +- .../{ => crypto}/PublicKeyInterface.java | 2 +- src/main/java/minsecurity/crypto/Sm4.java | 120 ++++++++ .../java/minsecurity/crypto/sm2/Sm2Base.java | 74 ++++- .../minsecurity/crypto/sm2/Sm2PrivateKey.java | 3 +- .../minsecurity/crypto/sm2/Sm2PublicKey.java | 4 +- src/main/java/util/FileUtil.java | 33 +++ .../java/minsecurity/crypto/TestCert.java | 114 ++++++++ src/test/java/minsecurity/crypto/TestSm4.java | 73 +++++ .../java/minsecurity/crypto/sm2/TestSm2.java | 3 - .../{logback.xml => logback-test.xml} | 6 +- 26 files changed, 1873 insertions(+), 15 deletions(-) create mode 100644 src/main/java/minsecurity/Common.java create mode 100644 src/main/java/minsecurity/certificate/X509/InvalidX500NameException.java create mode 100644 src/main/java/minsecurity/certificate/X509/RandomSNAllocatorX509.java create mode 100644 src/main/java/minsecurity/certificate/X509/SM2X509CertMakerBase.java create mode 100644 src/main/java/minsecurity/certificate/X509/X509CertMaker.java create mode 100644 src/main/java/minsecurity/certificate/X509/X509CertSNAllocator.java create mode 100644 src/main/java/minsecurity/certificate/X509/X509CertUtil.java create mode 100644 src/main/java/minsecurity/certificate/X509/X509CommonUtil.java create mode 100644 src/main/java/minsecurity/certificate/cert/CertException.java create mode 100644 src/main/java/minsecurity/certificate/cert/CertUtils.java create mode 100644 src/main/java/minsecurity/certificate/cert/Certificate.java create mode 100644 src/main/java/minsecurity/certificate/cert/InnerCertificate.java create mode 100644 src/main/java/minsecurity/certificate/cert/TbsCertificate.java create mode 100644 src/main/java/minsecurity/crypto/HashAlgo.java rename src/main/java/minsecurity/{ => crypto}/PrivateKeyInterface.java (95%) rename src/main/java/minsecurity/{ => crypto}/PublicKeyInterface.java (95%) create mode 100644 src/main/java/minsecurity/crypto/Sm4.java create mode 100644 src/main/java/util/FileUtil.java create mode 100644 src/test/java/minsecurity/crypto/TestCert.java create mode 100644 src/test/java/minsecurity/crypto/TestSm4.java rename src/test/resources/{logback.xml => logback-test.xml} (97%) 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 @@ - - - + + +