文章目录
- Before Start
- Build with Maven
- QuickStart
- 密钥对生成
- 公钥验签
- 证书验签
- 密钥对的反序列化
Before Start
SM2算法使用请参考:《GMT 0009-2012 SM2密码算法使用规范 》
在bouncycastle - 1.57版本之后,加入了对 我国的SM2、SM3、SM4算法的支持。
Bouncycastle releasenotes
Build with Maven
适配JDK 1.5 版本
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
QuickStart
Github
密钥对生成
SM2 非对称算法密钥对生成。
// 获取SM2椭圆曲线的参数
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2参数初始化生成器
kpg.initialize(sm2Spec);
// 使用SM2的算法区域初始化密钥生成器
kpg.initialize(sm2Spec, new SecureRandom());
// 获取密钥对
KeyPair keyPair = kpg.generateKeyPair();
感谢 githuber Gsealy的指点,密钥生成器构造更加简单。
关于椭圆曲线的推荐参数请参考 IETF draft-shen-sm2-ecdsa-02 #appendix-D
在BC中已经为构造了SM2算法参数,并提供算法OID,请参考:国密算法OID及意义 国密算法OID 源码SM2算法推荐参数 源码
公钥验签
产生了密钥对之后,就可以使用JAVA security 提供的一些标准化的接口来完成签名验签操作。
/*
获取公私钥
*/
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 生成SM2sign with sm3 签名验签算法实例
Signature signature = Signature.getInstance(
GMObjectIdentifiers.sm2sign_with_sm3.toString()
, new BouncyCastleProvider());
/*
* 签名
*/
// 签名需要使用私钥,使用私钥 初始化签名实例
signature.initSign(privateKey);
// 签名原文
byte[] plainText = "Hello world".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));
你可以在 国密算法OID 中找到需要算法的OID字符串。 如: SM2Sign-with-SM3 1.2.156.10197.1.501
证书验签
证书格式多为一个DER编码,也就是一串BASE64串,我们可以通过BC包提供的解析功能来解析SM2证书并并验签。
// 证书串,通常通过读取证书文件获取到,这里是一张SM2证书。
String certStr = "MIICCTCCAay....t5H";
// 签名原文
String plaintext = "signdata";
// 签名产生签名值,此处的签名值实际上就是 R和S的sequence
String signValueStr = "MEUCI...DOeFE=";
byte[] signValue = Base64.decode(signValueStr);
/*
* 解析证书
*/
CertificateFactory factory = new CertificateFactory();
X509Certificate certificate = (X509Certificate) factory.engineGenerateCertificate(new ByteArrayInputStream(Base64.decode(certStr)));
System.out.println(certificate.getSigAlgName());
// 验证签名
Signature signature = Signature.getInstance(certificate.getSigAlgName(), new BouncyCastleProvider());
signature.initVerify(certificate);
signature.update(plaintext.getBytes(StandardCharsets.UTF_8));
System.out.println(signature.verify(signValue));
密钥对的反序列化
- 获取椭圆曲线类型的密钥工厂。
- 根据公私钥对的格式进行反序列化。
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
final BouncyCastleProvider bc = new BouncyCastleProvider();
/*
>> 公钥BASE64: MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAESic24soUECzuSh2aYH0e+hQYh+/I01NmfjOnm5mwyUEYQvNCPTzn3BlNyufgMV+DWLUKV+2h0+PVel9jYTfG8Q==
>> 私钥BASE64: MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg0dYU+I6IdiSe8bvWlsHuWfsjSn3XFZqOGWO3K1814O6gCgYIKoEcz1UBgi2hRANCAARKJzbiyhQQLO5KHZpgfR76FBiH78jTU2Z+M6ebmbDJQRhC80I9POfcGU3K5+AxX4NYtQpX7aHT49V6X2NhN8bx
signature:
3045022100ff9a872f21e47d4fba8f37b48a62cc2e6fdde843a40cbc96242536afc10a395e02203bbab982d1bb6a7ee5f5f6b34cd887c255ae4dcc14dd87ecae2e0392611b7a8c
*/
// // 公私钥是16进制情况下解码
// byte[] encPub = Hex.decode("...");
// byte[] encPriv = Hex.decode("...");
byte[] encPub = Base64.decode("MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAESic24soUECzuSh2aYH0e+hQYh+/I01NmfjOnm5mwyUEYQvNCPTzn3BlNyufgMV+DWLUKV+2h0+PVel9jYTfG8Q==");
byte[] encPriv = Base64.decode("MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg0dYU+I6IdiSe8bvWlsHuWfsjSn3XFZqOGWO3K1814O6gCgYIKoEcz1UBgi2hRANCAARKJzbiyhQQLO5KHZpgfR76FBiH78jTU2Z+M6ebmbDJQRhC80I9POfcGU3K5+AxX4NYtQpX7aHT49V6X2NhN8bx");
byte[] plainText = "你好".getBytes(StandardCharsets.UTF_8);
KeyFactory keyFact = KeyFactory.getInstance("EC", bc);
// 根据采用的编码结构反序列化公私钥
PublicKey pub = keyFact.generatePublic(new X509EncodedKeySpec(encPub));
PrivateKey priv = keyFact.generatePrivate(new PKCS8EncodedKeySpec(encPriv));
Signature signature = Signature.getInstance("SM3withSm2", bc);
signature.initVerify(pub);
signature.update(plainText);
// 验证签名值
boolean res = signature.verify(Hex.decode("3045022100ff9a872f21e47d4fba8f37b48a62cc2e6fdde843a40cbc96242536afc10a395e02203bbab982d1bb6a7ee5f5f6b34cd887c255ae4dcc14dd87ecae2e0392611b7a8c"));
System.out.println(">> 验证结果:" + res);
}
参考自 org.bouncycastle.jcajce.provider.test.GeneralKeyTest#testSM2