[SM2/SM3 ]加解密/加解签,部分记录
Posted iOSTianNan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SM2/SM3 ]加解密/加解签,部分记录相关的知识,希望对你有一定的参考价值。
最近哥们问起SM2签名,发来一张图
一开始看不懂…
后来找到了js的库
npm install -save sm-crypto
这里直接说理解吧
sm2的加签, 会调用let sigValueHex = sm2.doSignature(msg,privateKey)
sm2的解签, 会调用
sm2.doSignature(msg,sigValueHex,publicKey)
复杂一点的加签 解签, 就是带sm3() 杂凑的模式
// 纯签名 + 生成椭圆曲线点 + sm3杂凑 let sigValueHex4 = sm2.doSignature(msg, privateKey, hash: true, ) // 签名 let verifyResult4 = sm2.doVerifySignature(msg, sigValueHex4, publicKey, hash: true, ) // 验签结果
带杂凑模式的要开启hash,同时传入的msg 可以通过 sm3(string)进行处理
加签操作
因此
针对上图的 SM2加解签步骤就好理解了
a. 通过特定的字符串组合, 计算出ZAlet ZA = sm3("某一串符合要求的字符串...")
b. 将ZA与MSG报文数据拼接, 再次进行 杂凑计算, 获取到 报文MSG的hash值 h
let h = sm3(ZA+MSG)
c. 执行 doSignature进行签名
//c. 计算 SingValue, 即执行加签 (hash模式启动 sm3杂凑) // 大S 存在 r/s两个子对象 let S = sm2.doSignature(h, s_privateKey, hash: true, );
S 就是加签数据了, 其实是一串数值 , 可以通过取前后64 获取, 获取出来的就是上图中说的 小r和小s
r: new BigInteger(S.substring(0, 64), 16), s: new BigInteger(S.substring(64), 16),
解签操作
// 转字符串 S = S + "" let verify = sm2.doVerifySignature(msg_hash, S, publicKey, hash: true, ) //判断 verify true/false即可
完整代码
const BigInteger = require('jsbn')
const sm2 = require('sm-crypto').sm2
const sm3 = require('sm-crypto').sm3
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey // 公钥
let s_privateKey = keypair.privateKey // 私钥
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1
const text = "王老师带我洗小头~"
var encryptData;
// 加密
function sm2_enc()
let encryptData = sm2.doEncrypt(text, publicKey, cipherMode) // 加密结果
return encryptData
// 解密
function sm2_dec(encString, privateKey = s_privateKey, cipherMode = 1)
let decryptData = sm2.doDecrypt(encString, privateKey, cipherMode) // 解密结果
return decryptData;
/**
* 加签
* @returns
*/
function sign()
let MSG = "二哥带我洗小头~"
//a.计算ZA
//[ENTLA||IDA||a||b||xG||yG||xA||yA] 此处理解为一串拼接的字符串 ,
// 暂且用 aaabbbcccddddeeefff代替
let ZA = sm3("123")
//b.计算报告MSG的32位hash值
let h = sm3(ZA + MSG)
//c. 计算 SingValue, 即执行加签 (hash模式启动 sm3杂凑)
// 大S 存在 r/s两个子对象
let S = sm2.doSignature(h, s_privateKey,
hash: true,
);
return
S: S,
r: new BigInteger(S.substring(0, 64), 16),
s: new BigInteger(S.substring(64), 16),
h: h
//
/**
* 解签
* @param * msg_hash :msg 或者msg的hash值
* @param * S :加签值
* @returns
*/
function verify(msg_hash, S)
// 转字符串
S = S + ""
let verify = sm2.doVerifySignature(msg_hash, S, publicKey,
hash: true,
)
if (verify)
return "验签成功"
else
return "验签失败"
module.exports =
sm2_enc,
sm2_dec,
publicKey,
s_privateKey,
sign,
verify,
函数调用
console.log("------------ 加签解签分割线 start ------------ ") let obj = sm2.sign() console.log("S:" + obj.S) console.log("r:" + obj.r) console.log("s:" + obj.s) console.log("h:" + obj.h) let result = sm2.verify(obj.h,obj.S) console.log("verify结果: " + result) console.log("------------ 加签解签分割线 end ------------ ")
日志输出
生成的公钥: 041f35d6fbf23e7db90864d95e5a99b317a98a088a99ab41f09a1cb97a43285566adb69d4095e32b289f5251eaade303888400c77278f5a9155a91dce6d272c3a5
生成的私钥: 3e6d34a6389ae4938b56f40b9ffa2f6cab63c63fd9034727a5fe7d72a238e642
加密后数据: ff92da49241569ebd3483c65ed102533c7260161dd47f89b10928ddce3143c08c66348eb77f49ba6a28b9421c1978a0508ca9f74a155c478e847c79d69475ead7198508a553485a29230e9f6ed1b3b2fbd28d4f81fbead59674fc7c1695682f507da39507e80b7b34c8f60f73f158de60d24e662ab5d74794d
解密后数据: 王老师带我洗小头~
------------ 加签解签分割线 start ------------
S:5160412328614ef30dc93ed5c9c885d7ca8231a7c23a8ac9b542983603cf0f4ee3bbdb2aca7855d34a8ca90dd1079ed52bd44f6f5c27154cefc08ce1dc15062a
r:36807407614826499109896364779734999601457810896173275142267850220542740074318
s:103006929665605321153971343965334528499223286175375383041883505413666888812074
h:185d501c23dc1c597d48da01781273b2abf45172f276866f5cf2e267217febd5
verify结果: 验签成功
------------ 加签解签分割线 end ------------
本文部分 END
(所以, ZA 我理解就是 杂凑的 za? 🤣)
官方文档如下
sm-crypto
国密算法sm2、sm3和sm4的js版。
PS: 小程序移植版:https://github.com/wechat-miniprogram/sm-crypto
PS: java 移植版(感谢 @antherd 提供):https://github.com/antherd/sm-crypto/
安装
npm install --save sm-crypto
sm2
获取密钥对
const sm2 = require('sm-crypto').sm2
let keypair = sm2.generateKeyPairHex()
publicKey = keypair.publicKey // 公钥
privateKey = keypair.privateKey // 私钥
// 默认生成公钥 130 位太长,可以压缩公钥到 66 位
const compressedPublicKey = sm2.compressPublicKeyHex(publicKey) // compressedPublicKey 和 publicKey 等价
sm2.comparePublicKeyHex(publicKey, compressedPublicKey) // 判断公钥是否等价
// 自定义随机数,参数会直接透传给 jsbn 库的 BigInteger 构造器
// 注意:开发者使用自定义随机数,需要自行确保传入的随机数符合密码学安全
let keypair2 = sm2.generateKeyPairHex('123123123123123')
let keypair3 = sm2.generateKeyPairHex(256, SecureRandom)
let verifyResult = sm2.verifyPublicKey(publicKey) // 验证公钥
verifyResult = sm2.verifyPublicKey(compressedPublicKey) // 验证公钥
加密解密
const sm2 = require('sm-crypto').sm2
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
encryptData = sm2.doEncrypt(msgArray, publicKey, cipherMode) // 加密结果,输入数组
decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode, output: 'array') // 解密结果,输出数组
签名验签
ps:理论上来说,只做纯签名是最快的。
const sm2 = require('sm-crypto').sm2
// 纯签名 + 生成椭圆曲线点
let sigValueHex = sm2.doSignature(msg, privateKey) // 签名
let verifyResult = sm2.doVerifySignature(msg, sigValueHex, publicKey) // 验签结果
// 纯签名
let sigValueHex2 = sm2.doSignature(msg, privateKey,
pointPool: [sm2.getPoint(), sm2.getPoint(), sm2.getPoint(), sm2.getPoint()], // 传入事先已生成好的椭圆曲线点,可加快签名速度
) // 签名
let verifyResult2 = sm2.doVerifySignature(msg, sigValueHex2, publicKey) // 验签结果
// 纯签名 + 生成椭圆曲线点 + der编解码
let sigValueHex3 = sm2.doSignature(msg, privateKey,
der: true,
) // 签名
let verifyResult3 = sm2.doVerifySignature(msg, sigValueHex3, publicKey,
der: true,
) // 验签结果
// 纯签名 + 生成椭圆曲线点 + sm3杂凑
let sigValueHex4 = sm2.doSignature(msg, privateKey,
hash: true,
) // 签名
let verifyResult4 = sm2.doVerifySignature(msg, sigValueHex4, publicKey,
hash: true,
) // 验签结果
// 纯签名 + 生成椭圆曲线点 + sm3杂凑(不做公钥推导)
let sigValueHex5 = sm2.doSignature(msg, privateKey,
hash: true,
publicKey, // 传入公钥的话,可以去掉sm3杂凑中推导公钥的过程,速度会比纯签名 + 生成椭圆曲线点 + sm3杂凑快
)
let verifyResult5 = sm2.doVerifySignature(msg, sigValueHex5, publicKey,
hash: true,
publicKey,
)
// 纯签名 + 生成椭圆曲线点 + sm3杂凑 + 不做公钥推 + 添加 userId(长度小于 8192)
// 默认 userId 值为 1234567812345678
let sigValueHex6 = sm2.doSignature(msgString, privateKey,
hash: true,
publicKey,
userId: 'testUserId',
)
let verifyResult6 = sm2.doVerifySignature(msgString, sigValueHex6, publicKey,
hash: true,
userId: 'testUserId',
)
获取椭圆曲线点
const sm2 = require('sm-crypto').sm2
let point = sm2.getPoint() // 获取一个椭圆曲线点,可在sm2签名时传入
sm3
const sm3 = require('sm-crypto').sm3
let hashData = sm3('abc') // 杂凑
// hmac
hashData = sm3('abc',
key: 'daac25c1512fe50f79b0e4526b93f5c0e1460cef40b6dd44af13caec62e8c60e0d885f3c6d6fb51e530889e6fd4ac743a6d332e68a0f2a3923f42585dceb93e9', // 要求为 16 进制串或字节数组
)
sm4
加密
const sm4 = require('sm-crypto').sm4
const msg = 'hello world! 我是 juneandgreen.' // 可以为 utf8 串或字节数组
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特
let encryptData = sm4.encrypt(msg, key) // 加密,默认输出 16 进制字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
let encryptData = sm4.encrypt(msg, key, padding: 'none') // 加密,不使用 padding
let encryptData = sm4.encrypt(msg, key, padding: 'none', output: 'array') // 加密,不使用 padding,输出为字节数组
let encryptData = sm4.encrypt(msg, key, mode: 'cbc', iv: 'fedcba98765432100123456789abcdef') // 加密,cbc 模式
解密
const sm4 = require('sm-crypto').sm4
const encryptData = '0e395deb10f6e8a17e17823e1fd9bd98a1bff1df508b5b8a1efb79ec633d1bb129432ac1b74972dbe97bab04f024e89c' // 可以为 16 进制串或字节数组
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特
let decryptData = sm4.decrypt(encryptData, key) // 解密,默认输出 utf8 字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
let decryptData = sm4.decrypt(encryptData, key, padding: 'none') // 解密,不使用 padding
let decryptData = sm4.decrypt(encryptData, key, padding: 'none', output: 'array') // 解密,不使用 padding,输出为字节数组
let decryptData = sm4.decrypt(encryptData, key, mode: 'cbc', iv: 'fedcba98765432100123456789abcdef') // 解密,cbc 模式
协议
MIT
以上是关于[SM2/SM3 ]加解密/加解签,部分记录的主要内容,如果未能解决你的问题,请参考以下文章
SM1SM2SM3SM4同态加密密态计算隐私计算和安全多方计算的概念