[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. 通过特定的字符串组合, 计算出ZA

let 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 ]加解密/加解签,部分记录的主要内容,如果未能解决你的问题,请参考以下文章

国密算法 SM2 SM3 SM4分别用作什么

python实现RSA加密和签名以及分段加解密的方案

国密算法说明SM2SM3SM4

SM1SM2SM3SM4同态加密密态计算隐私计算和安全多方计算的概念

密码算法(SM1SM2SM3SM4同态加密密态计算隐私计算和安全多方计算)

国密算法实现