国密算法的跨语言

版本修订人修订内容修订日期
V1.0王旭创建文档2023-11-14

概要

本文旨在说明java、golang、ts语言之间(不同库之间),国密算法的对接问题。

前提

项目备注
公钥0468fcb75779d1ce6a5a9da7293db8d1bc4af2f40119f4b9fca28bcf02321ecd65c0fc55bef351135fd326faff9ab3c9f2bb2565f35758fcac340e0804e68060f516进制字符串,长度130(2位填充字符+128位16进制公钥字符串)
私钥008aef670bcb542dd17a43e0a3a5c51860739eca85fc464b276baf0b3c5951f2c116进制字符串,长度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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值