Merge branch 'master' of gitee.com:willfree/min-dev-java

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