多语言国密SM互通讲解java、C#、golang、NodeJS
前言
距离上一篇博客已经过去了一个多月了、总算抽空继续写了。第一期传送门:java国密 C#国密 golang国密 NodeJS国密汇总(一)。本期博客主要内容是四种语言java、C#、nodejs、golang的SM2加签验签、加密解密演示,大部分注意点或者容易出错的点,我都会在代码注释中写明,如果还有不明白的小伙伴,可以在评论区留言。
2、SM2加签验签
这四种语言的加签验签互通,我都会演示C#、nodejs、golang来验证java的签名,如果演示所有之间的互通,比较麻烦。当然,需要的小伙伴可以在评论区留言。
(1)java
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.encoders.Base64;
import java.math.BigInteger;
/**
* @author 九二战歌
* Time 1970-01-01 00:00
* Description:SM2加签验签代码演示
* @version 1.0
*/
public class Sm2SignTest {
public static void main(String[] args) {
// 第一期生成出来的公私钥对,base64格式的,所以在使用时需要解码
String privateKeyStr = "RWKoHHEUV2733f+2V7SSWRBTQW36Ow8UH0lnySHMt4U=";
String publicKeyStr = "BBkBeWWyCx5twDuWZvH7+Ai1r+JY37znq6KW6aaQjbagMGFib5wuJzHNmcMd57m/NQAD3SAZmrJhFG6WEOAh010=";
// 待加签字符串、添加了中文和标点符号和特殊字符,
String unsignStr = "你好,世界 hello world ,./;'<>?:\"!@#$%^&*()_+-=1234567890`";
// id,自定义参数
String id = "12345678123456781234567812345678";
// 解析私钥
CipherParameters privateParam = generateECPrivateKeyParameters(privateKeyStr, id);
SM2Signer signer = new SM2Signer();
// 初始化sm2加签对象
signer.init(true, privateParam);
String sign;
try {
// 计算签名
signer.update(unsignStr.getBytes(), 0, unsignStr.getBytes().length);
sign = new String(Base64.encode(signer.generateSignature()));
System.out.println("sign -->" + sign);
} catch (CryptoException e) {
throw new RuntimeException(e);
}
// 解析公钥
CipherParameters publicParam = generateECPublicKeyParameters(publicKeyStr, id);
// 初始化sm2验签对象
signer.init(false, publicParam);
// 验证签名
signer.update(unsignStr.getBytes(), 0, unsignStr.getBytes().length);
boolean flag = signer.verifySignature(Base64.decode(sign));
System.out.println("flag -->" + flag);
}
/**
* 解析私钥
*
* @param privateKeyStr 私钥字符串,base64格式
* @param id 自定义加签参数
* @return 私钥参数
*/
public static CipherParameters generateECPrivateKeyParameters(String privateKeyStr, String id) {
// 标准椭圆曲线参数
X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters sm2DomainParams = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH());
BigInteger privateKeyBigInteger = BigIntegers.fromUnsignedByteArray(Base64.decode(privateKeyStr));
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKeyBigInteger, sm2DomainParams);
CipherParameters privateParam = new ParametersWithRandom(ecPrivateKeyParameters);
// 自定义参数不为空
if (id != null) {
privateParam = new ParametersWithID(ecPrivateKeyParameters, id.getBytes());
}
return privateParam;
}
/**
* 解析公钥
*
* @param publicKeyStr 公钥字符串,base64格式
* @param id 自定义加签参数
* @return 公钥参数
*/
public static CipherParameters generateECPublicKeyParameters(String publicKeyStr, String id) {
// 标准椭圆曲线参数
X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters sm2DomainParams = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH());
final ECCurve curve = sm2DomainParams.getCurve();
CipherParameters publicParam = new ECPublicKeyParameters(curve.decodePoint(Base64.decode(publicKeyStr)), sm2DomainParams);
// 自定义参数不为空
if (id != null) {
publicParam = new ParametersWithID(publicParam, id.getBytes());
}
return publicParam;
}
演示结果:
sign -->MEYCIQCo3gaAOUAF9tEsXb+kfgTi4EWcm1qwAJEewRpVoyQ+XwIhAPInI41BnGnnmUk+a185PZylw/pqCaMt+FzsD3xmC7Wz
flag -->true
(2)C#
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Text;
namespace sm
{
class Sm2Sign
{
public void SignTest()
{
// 第一期生成出来的公私钥对,base64格式的,所以在使用时需要解码,为演示互通性,也使用了上次java生成的密钥
string privateKeyStr = "RWKoHHEUV2733f+2V7SSWRBTQW36Ow8UH0lnySHMt4U=";
string publicKeyStr = "BBkBeWWyCx5twDuWZvH7+Ai1r+JY37znq6KW6aaQjbagMGFib5wuJzHNmcMd57m/NQAD3SAZmrJhFG6WEOAh010=";
// 待加签字符串、添加了中文和标点符号和特殊字符,
string unsignStr = "你好,世界 hello world ,./;'<>?:\"!@#$%^&*()_+-=1234567890`";
// id,自定义参数
string id = "12345678123456781234567812345678";
// 解析私钥
ICipherParameters privateParam = GenerateECPrivateKeyParameters(privateKeyStr, id);
SM2Signer signer = new SM2Signer();
// 初始化sm2加签对象
signer.Init(true, privateParam);
// 计算签名
signer.BlockUpdate(Encoding.UTF8.GetBytes(unsignStr), 0, Encoding.UTF8.GetBytes(unsignStr).Length);
string sign = Base64.ToBase64String(signer.GenerateSignature());
Console.WriteLine("csharp sign -->" + sign);
// 解析公钥
ICipherParameters publicParam = GenerateECPublicKeyParameters(publicKeyStr, id);
// 初始化sm2验签对象
signer.Init(false, publicParam);
// 验证签名
signer.BlockUpdate(Encoding.UTF8.GetBytes(unsignStr)