0.程序员专用开头:
1.项目结构:
2. 源码:
AlgSm2Demo.java (签名验签)
package alg.sm2demo;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
/**
* SM2 算法 Demo
*
* @author Cliven
* @date 2018-12-20 10:42:22
*/
public class AlgSm2Demo {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, SignatureException {
// 获取SM2椭圆曲线的参数
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2参数初始化生成器
kpg.initialize(sm2Spec);
// 获取密钥对
KeyPair keyPair = kpg.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// ------------------------ SM2未压缩公钥 ----------------------------
// 椭圆曲线公钥的点坐标
ECPoint pubKeyPointQ = ((BCECPublicKey) publicKey).getQ();
System.out.println("X: \n" + pubKeyPointQ.getXCoord());
System.out.println("Y: \n" + pubKeyPointQ.getYCoord());
// 将其表示为SM2未压缩的公钥为
System.out.println("SM2 public key: \n"
+ "04"
+ pubKeyPointQ.getXCoord().toString()
+ pubKeyPointQ.getYCoord().toString()
);
// ------------------------ SM2未压缩公钥 -------------------------------
System.out.println("Public key: \n" + Hex.toHexString(publicKey.getEncoded()));
System.out.println("Private key: \n" + Hex.toHexString(privateKey.getEncoded()));
// 生成SM2sign with sm3 签名验签算法实例
Signature signature = Signature.getInstance("SM3withSm2", new BouncyCastleProvider());
/*
* 签名
*/
// 签名需要使用私钥,使用私钥 初始化签名实例
signature.initSign(privateKey);
// 签名原文
byte[] plainText = "你好".getBytes(StandardCharsets.UTF_8);
// 写入签名原文到算法中
signature.update(plainText);
// 计算签名值
byte[] signatureValue = signature.sign();
System.out.println("signature: \n" + Hex.toHexString(signatureValue));
/*
* 验签
*/
// 签名需要使用公钥,使用公钥 初始化签名实例
signature.initVerify(publicKey);
// 写入待验签的签名原文到算法中
signature.update(plainText);
// 验签
System.out.println("Signature verify result: " + signature.verify(signatureValue));
}
}
SM2CertDemo.java (签发证书)
package alg.sm2demo;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.misc.NetscapeCertType;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Base64;
/**
* SM2 X.509签名制作Demo
*
* @author jy
* @date 2018-12-21 14:04
*/
public class SM2CertDemo {
/**
* BouncyCastle算法提供者
*/
private static final Provider BC = new BouncyCastleProvider();
/**
* 获取扩展密钥用途
*
* @return 增强密钥用法ASN.1对象
* @author Cliven
* @date 2018-12-21 16:04:58
*/
public static DERSequence extendedKeyUsage() {
// // 客户端身份认证
// ASN1ObjectIdentifier clientAuth = new
// ASN1ObjectIdentifier("1.3.6.1.5.5.7.3.2");
// // 安全电子邮件
// ASN1ObjectIdentifier emailProtection = new
// ASN1ObjectIdentifier("1.3.6.1.5.5.7.3.4");
// 构造容器对象
ASN1EncodableVector vector = new ASN1EncodableVector();
// 客户端身份认证
vector.add(KeyPurposeId.id_kp_clientAuth);
// 安全电子邮件
vector.add(KeyPurposeId.id_kp_emailProtection);
return new DERSequence(vector);
}
/**
* 生成证书文件
*
* @param x509Certificate
* X.509格式证书
* @param savePath
* 证书保存路径
* @throws CertificateEncodingException
* @throws IOException
* @author Cliven
* @date 2018-12-21 17:21:50
*/
public static void makeCertFile(X509Certificate x509Certificate, Path savePath)
throws CertificateEncodingException, IOException {
if (Files.exists(savePath)) {
// 删除已存在文件
Files.deleteIfExists(savePath);
}
// 创建文件
Files.createFile(savePath);
// 获取ASN.1编码的证书字节码
byte[] asn1BinCert = x509Certificate.getEncoded();
// 编码为BASE64 便于传输
byte[] base64EncodedCert = Base64.encode(asn1BinCert);
// 写入文件
Files.write(savePath, base64EncodedCert);
}
public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
OperatorCreationException, IOException, CertificateException {
// 生成密钥生成器, 产生密钥对
KeyPairGenerator keyPairGenerator = SM2KeyGenerateFactory.generator();
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 证书签名实现类 附加了 SM3WITHSM2 和 PrivateKey
JcaContentSignerBuilder jcaContentSB = new JcaContentSignerBuilder("SM3withSM2");
jcaContentSB.setProvider(new BouncyCastleProvider());
ContentSigner sigGen = jcaContentSB.build(keyPair.getPrivate());
// 准备证书信息
BigInteger sn = BigInteger.valueOf(System.currentTimeMillis());
X500Name issuer = createX500Name("yan");
X500Name subject = createX500Name("yan");
Date notBefore = new Date();
Date notAfter = new Date(System.currentTimeMillis() + 50 * 1000);
PublicKey publickey = keyPair.getPublic();
// 构造证书信息
JcaX509v3CertificateBuilder jcaX509v3Cert = new JcaX509v3CertificateBuilder(issuer, sn, notBefore, notAfter,
subject, publickey);
jcaX509v3Cert.addExtension(Extension.keyUsage, false,
new X509KeyUsage(X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation));
jcaX509v3Cert.addExtension(Extension.extendedKeyUsage, false, extendedKeyUsage());
jcaX509v3Cert.addExtension(MiscObjectIdentifiers.netscapeCertType, false,
new NetscapeCertType(NetscapeCertType.sslClient));
// 构造X.509 第3版的证书构建者
X509v3CertificateBuilder x509v3Cert = jcaX509v3Cert;
// 将证书构造参数装换为X.509证书对象
X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BC)
.getCertificate(x509v3Cert.build(sigGen));
// 保存为证书文件
makeCertFile(certificate, Paths.get("sm2_jy.cer"));
System.out.println("gen cert succ!");
}
// 构造 主题名称
public static X500Name createX500Name(String cn) {
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
// 国家代码
builder.addRDN(BCStyle.C, "CN");
// 组织
builder.addRDN(BCStyle.O, "info");
// 省份
builder.addRDN(BCStyle.ST, "beijing");
// 地区
builder.addRDN(BCStyle.L, "beijing");
// 身份
builder.addRDN(BCStyle.CN, cn);
X500Name subject = builder.build();
return subject;
}
}
SM2KeyGenerateFactory.java (产生SM2 密钥对)
package alg.sm2demo;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* SM2密钥对生成器
*
* @author Cliven
* @date 2018-12-21 14:05
*/
public class SM2KeyGenerateFactory {
/**
* 获取SM2密钥对生成器
*
* @return SM2密钥对生成器
* @throws NoSuchAlgorithmException
* @author Cliven
* @date 2019-6-10 15:56:36
*/
public static KeyPairGenerator generator() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
/*
// 获取SM2 椭圆曲线推荐参数
X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
// 构造EC 算法参数
ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
// 设置SM2 算法的 OID
GMObjectIdentifiers.sm2p256v1.toString()
// 设置曲线方程
, ecParameters.getCurve()
// 椭圆曲线G点
, ecParameters.getG()
// 大整数N
, ecParameters.getN());
// 创建 密钥对生成器
final KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2的算法区域初始化密钥生成器
kpg.initialize(sm2Spec, new SecureRandom());
return gen;
*/
/*
* 上面过程为原始构造过程参考,BC已经为我们封装好了
* 只需要下面三行代码即可
*/
// 获取SM2椭圆曲线的参数
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2参数初始化生成器
kpg.initialize(sm2Spec);
return kpg;
}
}
执行 SM2CertDemo 的 main 方法即可,产生 SM2 证书。
3. 效果: