版本 | 修订人 | 修订内容 | 修订日期 |
---|---|---|---|
V1.0 | 王旭 | 创建文档 | 2023-11-14 |
概要
本文旨在说明java、golang、ts语言之间(不同库之间),国密算法的对接问题。
前提
项目 | 值 | 备注 |
---|---|---|
公钥 | 0468fcb75779d1ce6a5a9da7293db8d1bc4af2f40119f4b9fca28bcf02321ecd65c0fc55bef351135fd326faff9ab3c9f2bb2565f35758fcac340e0804e68060f5 | 16进制字符串,长度130(2位填充字符+128位16进制公钥字符串) |
私钥 | 008aef670bcb542dd17a43e0a3a5c51860739eca85fc464b276baf0b3c5951f2c1 | 16进制字符串,长度66(2位填充字符+64位16进制私钥字符串) |
模式 | C1C3C2编码 | |
摘要算法 | sm3 | |
签名数据编码 | der编码(asn.1) | |
密钥是否压缩 | 否 | |
加密数据编码 | 简单字节编码 | |
Java库 | bc库 | |
Go库 | gmsm库 | |
前端库 | sm-crypto库 | 其它库解密sm-crypto库生成的密文前,需要在16进制字符串前拼接04 |
名词&原理
参考文档:国密算法相关定义及原理浅析
JAVA 加解密、加验签示例(包括sm2和sm4)
public static void main(String[] args) {
//需要加密的明文
String text = "aaaa";
//私钥
String dHex = "008aef670bcb542dd17a43e0a3a5c51860739eca85fc464b276baf0b3c5951f2c1";
//公钥
String qHex = "0468fcb75779d1ce6a5a9da7293db8d1bc4af2f40119f4b9fca28bcf02321ecd65c0fc55bef351135fd326faff9ab3c9f2bb2565f35758fcac340e0804e68060f5";
//密文
String encryptStr = "04c1e1f1b52a871d643eeb48b523669e2a1a805468522c8453e49bfd36a43dfece3ce5e254be4a2c6c1af1248ea50b8e459cff1330ca3579d6b48102c31c76415c380697f4de7fba4d8c2d45d4ff04db2163c435ece586bb6dd4f0f33e5ae74babdec38a28";
//签名
String sign = "3046022100fafd79f3b6748c4269590db1d66fa6e7b0e3d03364984c0a2bf44595fdb2b61d022100c58416602b1f960474b27ffccefd0ed165cc24d64a0de287f452cc2e2aa1bcff";
//创建sm2 对象
SM2 sm2 = SmUtil.sm2(dHex,qHex);
//设置加密时,密文的结构是c1c3c2
sm2.setMode(SM2Engine.Mode.C1C3C2);
//设置使用标准格式编码,即使用asn1编码方式 如果使用PlainDSAEncoding,密文/签名直接转为字节数组
sm2.setEncoding(new StandardDSAEncoding());
//使用sm3生成摘要
sm2.setDigest(new SM3Digest());
String encryptHex = sm2.encryptHex(text, KeyType.PublicKey);
System.out.println("加密数据:"+encryptHex);
//得到明文对应的字节数组
String decryptStr = sm2.decryptStr(encryptStr, KeyType.PrivateKey);
System.out.println("解密数据:"+decryptStr);
byte[] signNew = sm2.sign(text.getBytes(), null);
System.out.println("签名: " + HexUtil.encodeHexStr(signNew));
boolean verify = sm2.verify(text.getBytes(), HexUtil.decodeHex(sign));
System.out.println("验签结果:"+verify);
SM4 sm4 = SmUtil.sm4("1234567890abcdef".getBytes(StandardCharsets.UTF_8));
String sm4EncryptHex = sm4.encryptHex(text);
System.out.println("sm4加密后的数据为:"+sm4EncryptHex);
String sm4DecryptStr = sm4.decryptStr("4ff374c836c052a9b95daeae722bb943");
System.out.println("sm解密后的数据为"+sm4DecryptStr);
}
GO 加解密、加验签示例(包括sm2和sm4)
func main() {
//原文
text := "aaaa"
//私钥
dHex := "008aef670bcb542dd17a43e0a3a5c51860739eca85fc464b276baf0b3c5951f2c1"
//公钥
qHex := "68fcb75779d1ce6a5a9da7293db8d1bc4af2f40119f4b9fca28bcf02321ecd65c0fc55bef351135fd326faff9ab3c9f2bb2565f35758fcac340e0804e68060f5"
//密文
encryptHex := "04a37cdf019c23aef3ea0998378c3ca9b1b5ebf194581e9d2b733f5761f9ad9f5e55388365fd45665272ffab5abaaca09082d467c96b2c8db336cdd8979634d56edacb704b54aca0248ff63a3c67187a1a9b94449e75241cb5aafea199b269a392f63aa58c"
//签名
sign := "3045022100927956bc86ce87b0e0cb926b321201847135c22ec8e06ca465a0c6a8ca109e610220321744991bb9e88ee58c74351ffef3b2d3b215973f201af11c0f4453d45dcb5f"
//使用x509库中的方法将公钥反解出来
publicKey, _ := x509.ReadPublicKeyFromHex(qHex)
//将私钥反解出来
privateKey, err2 := x509.ReadPrivateKeyFromHex(dHex)
//加签
signNew, err := privateKey.Sign(nil, []byte(text), nil)
if err != nil {
log.Errorf("加签失败:%v", err)
}
log.Infof("签名数据:%v", hex.EncodeToString(signNew))
//验签
asn1, err := hex.DecodeString(sign)
if err != nil {
log.Errorf("签名解码失败,%v", err)
return
}
verify := publicKey.Verify([]byte(text), asn1)
log.Infof("验签结果:%t\n", verify)
//加密
encrypt, _ := sm2.Encrypt(publicKey, []byte(text), nil, sm2.C1C3C2)
log.Infof("加密内容为:%v", hex.EncodeToString(encrypt))
//解密
ciphertext, err := hex.DecodeString(encryptHex)
if err != nil {
log.Errorf("密文解码失败,%v", err)
}
plaintxt, err2 := sm2.Decrypt(privateKey, ciphertext, sm2.C1C3C2)
if err2 == nil {
log.Infof("解密出的文本为:%s\n", plaintxt)
} else {
log.Infof("解密失败:%v", err2)
}
//sm4加解密
//对称密钥的key 在ts中需要进行16进制编码后使用
key := []byte("1234567890abcdef")
fmt.Printf("key = %v\n", key)
//加密数据
dataStr := "aaaa"
data := []byte(dataStr)
ecbMsg, err := sm4.Sm4Ecb(key, data, true) //sm4Ecb模式pksc7填充加密
if err != nil {
log.Errorf("sm4 enc error:%s", err)
return
}
log.Infof("sm4密文数据:%x", ecbMsg)
ecbDec, err := sm4.Sm4Ecb(key, ecbMsg, false) //sm4Ecb模式pksc7填充解密
if err != nil {
log.Errorf("sm4 dec error:%s", err)
return
}
log.Infof("sm4解密出的明文:%s", string(ecbDec))
}
ts加解密、加验签示例(包括sm2和sm4)
import "./styles.css";
import { useEffect } from 'react'
import { sm2, sm3, sm4 } from 'sm-crypto'
const SystemSettings: React.FC = () => {
useEffect(() => {
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1
//原文
let msgString = "aaaa"
//私钥
let privateKey = "008aef670bcb542dd17a43e0a3a5c51860739eca85fc464b276baf0b3c5951f2c1"
//公钥
let publicKey = "0468fcb75779d1ce6a5a9da7293db8d1bc4af2f40119f4b9fca28bcf02321ecd65c0fc55bef351135fd326faff9ab3c9f2bb2565f35758fcac340e0804e68060f5"
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
let sigValueHex = sm2.doSignature(msgString, privateKey, {
hash: true,
der: true
}) // 签名
let verify = sm2.doVerifySignature(msgString,sigValueHex,publicKey,{
hash: true,
der: true
}
) //验签
console.log("密文为:"+encryptData)
console.log("签名为:"+sigValueHex)
console.log("解密结果为:"+decryptData)
console.log("验签结果为:"+verify)
const decryptStr = '4ff374c836c052a9b95daeae722bb943' // 可以为 utf8 串或字节数组
const key = '31323334353637383930616263646566' // 可以为 16 进制串或字节数组,要求为 128 比特(原文为1234567890abcdef)
let sm4EncryptData = sm4.encrypt(msgString,key)
console.log("sm4加密后的数据为:"+sm4EncryptData)
let sm4DecryptData = sm4.decrypt(decryptStr, key)
// 解密,默认输出 utf8 字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
console.log("sm4解密后的数据为:"+sm4DecryptData)
}, [])
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
export default SystemSettings