Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

后端生成的密钥对,前端加密成功,但无法解密 #42

Open
yhyzgn opened this issue Jun 16, 2021 · 29 comments
Open

后端生成的密钥对,前端加密成功,但无法解密 #42

yhyzgn opened this issue Jun 16, 2021 · 29 comments

Comments

@yhyzgn
Copy link

yhyzgn commented Jun 16, 2021

实际开发中遇到个问题,想请教下大佬们:
后端用Go语言生成的密钥对,前端用公钥能加密成功,在Go和Java上也能用对应的私钥解密成功,但前端无法解密

公钥:(取后130位)3059301306072a8648ce3d020106082a811ccf5501822d0342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388
私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420ff54e3914a41191006633b5f08fcc5e42dbdf2c26c18d55dd9501f0d889c151ea00a06082a811ccf5501822da1440342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388

原文:你好啊
密文:(已添加 04 标识)044fd2407ae9b39d7e8080c15fa39ad392ce0ba26829345c6422c9fa1a8746ad4c5f42c3a1d6aaab0a8f16405afdcb3fb708c2a200b48ebd9b12adcaf686e4a060c6182903dd43e9b401afccc8504d9360d572a14d372dc7665fc2eb762f2e34c45cf50283c7c07b6e4d

Go和Java均能解密出原文,但是用sm-crypto解密后结果是空的。。

@JuneAndGreen
Copy link
Owner

此库格式:公钥是 '04' + 128位 hex 串(x坐标 + y坐标);私钥是64位 hex 串,你可以对比一下。解密结果为空表示解密不出来。

@yhyzgn
Copy link
Author

yhyzgn commented Jun 22, 2021

嗯嗯,我试了下,貌似并不是单纯的其中某64位

@antherds
Copy link
Contributor

已经提了pr,可以到我的仓库看看: https://github.com/antherd/sm-crypto
WX20210813-093905

@yhyzgn
Copy link
Author

yhyzgn commented Aug 19, 2021

@antherd-admin 我Java端和Golang端已经对接完成,你的这个仓库也只是用java调作者的js方法呀。。

@pronebel
Copy link

java的公私钥,需要

String publicQ = HexUtil.encodeHexStr(sm2.getQ(false));//未压缩的
String privateD = HexUtil.encodeHexStr(sm2.getD());

取出 Q和D 才和 js端的公私钥对应的,
可以看看是不是类似的原因,我之前也遇到过

@antherds
Copy link
Contributor

@antherd-admin 我Java端和Golang端已经对接完成,你的这个仓库也只是用java调作者的js方法呀。。

没错,java直接调用js方法。保证前后端所有方法一致

@antherds
Copy link
Contributor

java的公私钥,需要

String publicQ = HexUtil.encodeHexStr(sm2.getQ(false));//未压缩的
String privateD = HexUtil.encodeHexStr(sm2.getD());

取出 Q和D 才和 js端的公私钥对应的,
可以看看是不是类似的原因,我之前也遇到过

因为前段js生成的公私钥是压缩的,所以用前段后端生成的公钥,获得的压缩Q加密的密文,用后端私钥是解不了的

@pronebel
Copy link

java的公私钥,需要

String publicQ = HexUtil.encodeHexStr(sm2.getQ(false));//未压缩的
String privateD = HexUtil.encodeHexStr(sm2.getD());

取出 Q和D 才和 js端的公私钥对应的,
可以看看是不是类似的原因,我之前也遇到过

cn.hutool.crypto.SmUtil;
我这里是用的这个hutool工具包里的方法,和本sm的js库之间对接下来没有问题。及时要注意这个Q,D的压缩密匙,还有 04的问题

@antherds
Copy link
Contributor

java的公私钥,需要
String publicQ = HexUtil.encodeHexStr(sm2.getQ(false));//未压缩的
String privateD = HexUtil.encodeHexStr(sm2.getD());
取出 Q和D 才和 js端的公私钥对应的,
可以看看是不是类似的原因,我之前也遇到过

cn.hutool.crypto.SmUtil;
我这里是用的这个hutool工具包里的方法,和本sm的js库之间对接下来没有问题。及时要注意这个Q,D的压缩密匙,还有 04的问题

前段加密,后端可以解密吗?可以就挺好,不过还是需要注意很多细节。。

@ypwlal
Copy link

ypwlal commented Feb 21, 2022

实际开发中遇到个问题,想请教下大佬们: 后端用Go语言生成的密钥对,前端用公钥能加密成功,在Go和Java上也能用对应的私钥解密成功,但前端无法解密

公钥:(取后130位)3059301306072a8648ce3d020106082a811ccf5501822d0342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388 私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420ff54e3914a41191006633b5f08fcc5e42dbdf2c26c18d55dd9501f0d889c151ea00a06082a811ccf5501822da1440342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388

原文:你好啊 密文:(已添加 04 标识)044fd2407ae9b39d7e8080c15fa39ad392ce0ba26829345c6422c9fa1a8746ad4c5f42c3a1d6aaab0a8f16405afdcb3fb708c2a200b48ebd9b12adcaf686e4a060c6182903dd43e9b401afccc8504d9360d572a14d372dc7665fc2eb762f2e34c45cf50283c7c07b6e4d

Go和Java均能解密出原文,但是用sm-crypto解密后结果是空的。。

兄弟可以介绍下你用的golang哪个库么?
我的情况跟你相反,我用的go库(gmsm)生成的公私钥可以各自在go端加解密成功,在js端加解密成功,但是却没法做到前后互通,即在前端加密后端解密或者后端加密前端解密

@yhyzgn
Copy link
Author

yhyzgn commented Feb 22, 2022

@ypwlal 当时用的是这个库 https://github.com/tjfoc/gmsm

@ypwlal
Copy link

ypwlal commented Feb 22, 2022

@ypwlal 当时用的是这个库 https://github.com/tjfoc/gmsm

我这边已经成功了,你看下我的情况对你有没有帮助。
一开始我用的gmsm的EncryptAsn1、DecryptAsn1方法,前后端对应不上;然后我debug出来后,使用gmsm的Encrypt和Decrypt方法,和sm-crypto(js加密的密文前补上04到后端解密)就完全可以对应上了。因为gmsm文档里的方法会对入参的明文/密文字节做marshal和unmarshal,跳过这一步就没问题了(要注意参数错误会panic)

@yhyzgn
Copy link
Author

yhyzgn commented Feb 23, 2022

@ypwlal 多谢多谢!目前我这边已采用其他方案,抽空我也试试你的方法

@goodweather0
Copy link

@ypwlal 情况说明很有帮助,按照你的描述成功了,多谢!

@changhr2013
Copy link
Contributor

实际开发中遇到个问题,想请教下大佬们: 后端用Go语言生成的密钥对,前端用公钥能加密成功,在Go和Java上也能用对应的私钥解密成功,但前端无法解密

公钥:(取后130位)3059301306072a8648ce3d020106082a811ccf5501822d0342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388 私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420ff54e3914a41191006633b5f08fcc5e42dbdf2c26c18d55dd9501f0d889c151ea00a06082a811ccf5501822da1440342000410ee64f73e0acb2380bada2697e80a22b39968ac72833b7692968ff2c21bc88e8afcbdc1b89c3382089415cea907e61b02060194675a16c4d48c5e3a57c49388

原文:你好啊 密文:(已添加 04 标识)044fd2407ae9b39d7e8080c15fa39ad392ce0ba26829345c6422c9fa1a8746ad4c5f42c3a1d6aaab0a8f16405afdcb3fb708c2a200b48ebd9b12adcaf686e4a060c6182903dd43e9b401afccc8504d9360d572a14d372dc7665fc2eb762f2e34c45cf50283c7c07b6e4d

Go和Java均能解密出原文,但是用sm-crypto解密后结果是空的。。

公钥使用 DER 编码了,解析 DER 之后把 X 和 Y 还原回来即可,私钥使用的 PKCS#8 格式,也需要解析后还原出裸私钥。

@ZongweiBai
Copy link

@ypwlal 多谢多谢!目前我这边已采用其他方案,抽空我也试试你的方法

请教一下你这边是怎么解决的,我目前遇到的问题和你类似,使用golang生成的公钥和私钥,golang和java能互相加解密,golang和js能互相加解密,java可以解密js的密文,唯一的问题是js无法解密java的密文。

@changhr2013
Copy link
Contributor

@ZongweiBai Java 加密出来的密文格式丢一个出来看看

@ZongweiBai
Copy link

@ZongweiBai Java 加密出来的密文格式丢一个出来看看

我这边后端用的是gmhelper,生成的密文是,对应的加密格式是c1c3c2:

040B16C625F649F154D59B610DB390F30AADA21B50F1D88CB5BD7CEE1671AEF9225DEFF40FE9ECE4D39FDB811C9560C7BA557B6A95D77E53F1F2D15CF77066EA6011BD53F21066E99EAD0BB31DCB978A74D5878FD7F61953A6928A14E591120C9B81240D3E9AA67D716827DB095D

js对应的公钥和私钥分别是:

js公钥: 04721982C46CBF2A424456081944C58165543847119E629ECBC2B1C70400DF05A6661848019F13A1B2B370D60168A162A97D49FA2F2F56EF8F6BEC0035206BF440
js私钥: 4EA87F7B575ECE76D51CADB061CE3B4D7700BFC6231F5C844D7611F123D07879

@changhr2013
Copy link
Contributor

@ZongweiBai 去除密文前的 04 标识,然后密文换成小写即可:

const Sm2Crypto = require("sm-crypto").sm2;

let cipherText = "0b16c625f649f154d59b610db390f30aada21b50f1d88cb5bd7cee1671aef9225deff40fe9ece4d39fdb811c9560c7ba557b6a95d77e53f1f2d15cf77066ea6011bd53f21066e99ead0bb31dcb978a74d5878fd7f61953a6928a14e591120c9b81240d3e9aa67d716827db095d";

let privateKeyHex = "4EA87F7B575ECE76D51CADB061CE3B4D7700BFC6231F5C844D7611F123D07879";

let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);

console.log(decryptText);
helloword!sm2

如果要追因的话,就是这个 js 库 在最后比较 c3 的时候,是用 === 比较的,但是库内部本身算出来的形式是小写的。😂

@ZongweiBai
Copy link

@changhr2013 按照你的方法成功了,感谢。

目前sm2\sm3\sm4的golang\java\js三端都调试成功了,我这边用到的golang库和java库是:
https://github.com/Hyperledger-TWGC/ccs-gm
https://github.com/ZZMarquis/gmhelper

希望对大家有帮助

@daLingZhong
Copy link

@ypwlal 当时用的是这个库 https://github.com/tjfoc/gmsm

我这边已经成功了,你看下我的情况对你有没有帮助。 一开始我用的gmsm的EncryptAsn1、DecryptAsn1方法,前后端对应不上;然后我debug出来后,使用gmsm的Encrypt和Decrypt方法,和sm-crypto(js加密的密文前补上04到后端解密)就完全可以对应上了。因为gmsm文档里的方法会对入参的明文/密文字节做marshal和unmarshal,跳过这一步就没问题了(要注意参数错误会panic)

兄弟我按照你说的做了,但是还是出现了上述的情况,golang生成的公私钥两边都正常,但是前后端不能互通,前端加密后补上04也不行

@changhr2013
Copy link
Contributor

changhr2013 commented May 12, 2022

感觉有必要写个跨语言说明了,要不然这个 issue 无解 😂。

Golang 使用 gmsm 加/解密:

func TestGmsm2Encrypt(t *testing.T) {

	// 原文
	dataBytes := []byte("hello world")

	// 生成密钥对
	sm2PriKey, err := sm2.GenerateKey(nil)
	if err != nil {
		return
	}

	// 私钥 hex
	s := hex.EncodeToString(sm2PriKey.D.Bytes())
	fmt.Println(s)

	// 公钥 hex
	sm2PubKey := sm2PriKey.PublicKey
	s2 := "04" + hex.EncodeToString(sm2PubKey.X.Bytes()) + hex.EncodeToString(sm2PubKey.Y.Bytes())
	fmt.Println(s2)

	// 公钥加密
	encrypt, err := sm2.Encrypt(&sm2PubKey, dataBytes, nil, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(hex.EncodeToString(encrypt))

	// 私钥解密
	decrypt, err := sm2.Decrypt(sm2PriKey, encrypt, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(string(decrypt))
}

输出示例:

// 私钥
edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc
// 公钥
0416c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd04631791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07
// golang 加密的密文
04f6f663b4ca467c1b0053ae6d22079ef3a9612253829eee84f32846067630aeeb95f19119a52b816ecf71ffdd9cdb68ca6fa2ab748a50acde6d9c93249288558645636ef1d77bbd32b419b87a7cd2337bb6362b6122746c0b313f461b373f7c61ea5a27b2dc2846ccd88f43
// golang 解密的明文
hello world

js 使用 sm-crypto 解密 golang 密文:

const Sm2Crypto = require("sm-crypto").sm2;

// golang 加密的密文,需要移除前面的 04 标识
let cipherText = "f6f663b4ca467c1b0053ae6d22079ef3a9612253829eee84f32846067630aeeb95f19119a52b816ecf71ffdd9cdb68ca6fa2ab748a50acde6d9c93249288558645636ef1d77bbd32b419b87a7cd2337bb6362b6122746c0b313f461b373f7c61ea5a27b2dc2846ccd88f43";
// 私钥
let privateKeyHex = "edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc";
// 解密
let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);
console.log(decryptText);

// 公钥
let publicKeyHex = "0416c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd04631791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07"
// 加密
let encryptText = Sm2Crypto.doEncrypt("hello world", publicKeyHex, 1);
console.log(encryptText);

输出示例:

// js 解密的明文
hello world
// js 加密的密文
17b8d0bde323c8b9bb593e0916e8f60ba2ac765fcc10ef1a88985c8a89e13bd9be256b8948761fbb8dbf531c99d62148f391f90cf1037e6d4cfb6934faed4172a37e5e4466b3a139330c9f9c5927216d2460733eedf6c2cd1ef54cf5d5e1f5b88e17c6e77a503c342d3cb6

使用 golang 解密 js 密文:

func TestGmsm2Decrypt(t *testing.T) {

	// js 加密的密文,需要添加 04 标识
	cipherBytes, _ := hex.DecodeString("0417b8d0bde323c8b9bb593e0916e8f60ba2ac765fcc10ef1a88985c8a89e13bd9be256b8948761fbb8dbf531c99d62148f391f90cf1037e6d4cfb6934faed4172a37e5e4466b3a139330c9f9c5927216d2460733eedf6c2cd1ef54cf5d5e1f5b88e17c6e77a503c342d3cb6")

	// 还原私钥结构体
	X, _ := hex.DecodeString("16c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd0463")
	Y, _ := hex.DecodeString("1791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07")
	D, _ := hex.DecodeString("edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc")
	pubX := new(big.Int).SetBytes(X)
	pubY := new(big.Int).SetBytes(Y)
	priD := new(big.Int).SetBytes(D)

	sm2PriKey := &sm2.PrivateKey{
		PublicKey: sm2.PublicKey{
			Curve: sm2.P256Sm2(),
			X:     pubX,
			Y:     pubY,
		},
		D: priD,
	}

	// 私钥解密
	decrypt, err := sm2.Decrypt(sm2PriKey, cipherBytes, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(string(decrypt))
}

输出示例:

hello world

@daLingZhong

@Montaro2017
Copy link

java的公私钥,需要

String publicQ = HexUtil.encodeHexStr(sm2.getQ(false));//未压缩的 String privateD = HexUtil.encodeHexStr(sm2.getD());

取出 Q和D 才和 js端的公私钥对应的, 可以看看是不是类似的原因,我之前也遇到过

我按你的代码拿到了公钥和私钥然后加密,前端解密不出来啊,是我的加密方式有问题吗?

SM2 sm2 = SmUtil.sm2();
String publicKey = HexUtil.encodeHexStr(sm2.getQ(false));
String privateKey = HexUtil.encodeHexStr(sm2.getD());
System.out.println("publicKey = " + publicKey);
System.out.println("privateKey = " + privateKey);
String encryptBcd = sm2.encryptBcd(data, KeyType.PublicKey);
System.out.println("encryptBcd = " + encryptBcd);

@changhr2013
Copy link
Contributor

changhr2013 commented May 20, 2022

使用 hutool 做 SM2 加解密:

@Test
public void Sm2EncryptAndDecrypt() {
  SM2 sm2 = SmUtil.sm2();
  String publicKey = Hex.toHexString(sm2.getQ(false));
  String privateKey = Hex.toHexString(sm2.getD());;
  System.out.println("publicKey = " + publicKey);
  System.out.println("privateKey = " + privateKey);
  
  // 待加密的原文
  String data = "hello world";
  
  // 加密为 hex 格式
  String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
  System.out.println("encryptHex = " + encryptHex);
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(encryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

publicKey = 04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc
privateKey = 15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6
encryptHex = 04818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf
hello world

使用 js sm-crypto 解密 java 加密的密文:

const Sm2Crypto = require("sm-crypto").sm2;

// 密文,需要移除前面的 04 标识
let cipherText = "818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf";
// 私钥
let privateKeyHex = "15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6";
// 解密
let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);
console.log(decryptText);

// 公钥
let publicKeyHex = "04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc"
// 加密
let encryptText = Sm2Crypto.doEncrypt("hello world", publicKeyHex, 1);
console.log(encryptText);

输出示例:

// js 解密 java 的密文
hello world
// js 加密的密文
f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac

使用 hutool 解密 js 的密文:

@Test
public void Sm2DecryptTest() {
  // 还原 SM2 实例
  byte[] privateKey = Hex.decode("15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6");
  byte[] publicKey = Hex.decode("04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc");
  SM2 sm2 = new SM2(privateKey, publicKey);
  
  // js 密文前需要添加 04 标识
  String jsEncryptHex = "04f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac";
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(jsEncryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

hello world

@Montaro2017

@Montaro2017
Copy link

使用 hutool 做 SM2 加解密:

@Test
public void Sm2EncryptAndDecrypt() {
  SM2 sm2 = SmUtil.sm2();
  String publicKey = Hex.toHexString(sm2.getQ(false));
  String privateKey = Hex.toHexString(sm2.getD());;
  System.out.println("publicKey = " + publicKey);
  System.out.println("privateKey = " + privateKey);
  
  // 待加密的原文
  String data = "hello world";
  
  // 加密为 hex 格式
  String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
  System.out.println("encryptHex = " + encryptHex);
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(encryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

publicKey = 04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc
privateKey = 15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6
encryptHex = 04818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf
hello world

使用 js sm-crypto 解密 java 加密的密文:

const Sm2Crypto = require("sm-crypto").sm2;

// 密文,需要移除前面的 04 标识
let cipherText = "818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf";
// 私钥
let privateKeyHex = "15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6";
// 解密
let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);
console.log(decryptText);

// 公钥
let publicKeyHex = "04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc"
// 加密
let encryptText = Sm2Crypto.doEncrypt("hello world", publicKeyHex, 1);
console.log(encryptText);

输出示例:

// js 解密 java 的密文
hello world
// js 加密的密文
f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac

使用 hutool 解密 js 的密文:

@Test
public void Sm2DecryptTest() {
  // 还原 SM2 实例
  byte[] privateKey = Hex.decode("15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6	");
  byte[] publicKey = Hex.decode("04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc");
  SM2 sm2 = new SM2(privateKey, publicKey);
  
  // js 密文前需要添加 04 标识
  String jsEncryptHex = "04f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac";
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(jsEncryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

hello world

@Montaro2017

哇 感谢 我去试试

@dragon1102
Copy link

感觉有必要写个跨语言说明了,要不然这个 issue 无解 😂。

Golang 使用 gmsm 加/解密:

func TestGmsm2Encrypt(t *testing.T) {

	// 原文
	dataBytes := []byte("hello world")

	// 生成密钥对
	sm2PriKey, err := sm2.GenerateKey(nil)
	if err != nil {
		return
	}

	// 私钥 hex
	s := hex.EncodeToString(sm2PriKey.D.Bytes())
	fmt.Println(s)

	// 公钥 hex
	sm2PubKey := sm2PriKey.PublicKey
	s2 := "04" + hex.EncodeToString(sm2PubKey.X.Bytes()) + hex.EncodeToString(sm2PubKey.Y.Bytes())
	fmt.Println(s2)

	// 公钥加密
	encrypt, err := sm2.Encrypt(&sm2PubKey, dataBytes, nil, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(hex.EncodeToString(encrypt))

	// 私钥解密
	decrypt, err := sm2.Decrypt(sm2PriKey, encrypt, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(string(decrypt))
}

输出示例:

// 私钥
edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc
// 公钥
0416c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd04631791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07
// golang 加密的密文
04f6f663b4ca467c1b0053ae6d22079ef3a9612253829eee84f32846067630aeeb95f19119a52b816ecf71ffdd9cdb68ca6fa2ab748a50acde6d9c93249288558645636ef1d77bbd32b419b87a7cd2337bb6362b6122746c0b313f461b373f7c61ea5a27b2dc2846ccd88f43
// golang 解密的明文
hello world

js 使用 sm-crypto 解密 golang 密文:

const Sm2Crypto = require("sm-crypto").sm2;

// golang 加密的密文,需要移除前面的 04 标识
let cipherText = "f6f663b4ca467c1b0053ae6d22079ef3a9612253829eee84f32846067630aeeb95f19119a52b816ecf71ffdd9cdb68ca6fa2ab748a50acde6d9c93249288558645636ef1d77bbd32b419b87a7cd2337bb6362b6122746c0b313f461b373f7c61ea5a27b2dc2846ccd88f43";
// 私钥
let privateKeyHex = "edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc";
// 解密
let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);
console.log(decryptText);

// 公钥
let publicKeyHex = "0416c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd04631791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07"
// 加密
let encryptText = Sm2Crypto.doEncrypt("hello world", publicKeyHex, 1);
console.log(encryptText);

输出示例:

// js 解密的明文
hello world
// js 加密的密文
17b8d0bde323c8b9bb593e0916e8f60ba2ac765fcc10ef1a88985c8a89e13bd9be256b8948761fbb8dbf531c99d62148f391f90cf1037e6d4cfb6934faed4172a37e5e4466b3a139330c9f9c5927216d2460733eedf6c2cd1ef54cf5d5e1f5b88e17c6e77a503c342d3cb6

使用 golang 解密 js 密文:

func TestGmsm2Decrypt(t *testing.T) {

	// js 加密的密文,需要添加 04 标识
	cipherBytes, _ := hex.DecodeString("0417b8d0bde323c8b9bb593e0916e8f60ba2ac765fcc10ef1a88985c8a89e13bd9be256b8948761fbb8dbf531c99d62148f391f90cf1037e6d4cfb6934faed4172a37e5e4466b3a139330c9f9c5927216d2460733eedf6c2cd1ef54cf5d5e1f5b88e17c6e77a503c342d3cb6")

	// 还原私钥结构体
	X, _ := hex.DecodeString("16c9fdfabe11ca0efe5aae03cdb46b96e39e14095e926e5f021889f79abd0463")
	Y, _ := hex.DecodeString("1791d2dc1672bad412596d5f32a6977332f5c815bf6ca948c9f3f24999551b07")
	D, _ := hex.DecodeString("edec1100fbc83411ed1112bab763df275f91ad3140aa19ae5cc717012454fefc")
	pubX := new(big.Int).SetBytes(X)
	pubY := new(big.Int).SetBytes(Y)
	priD := new(big.Int).SetBytes(D)

	sm2PriKey := &sm2.PrivateKey{
		PublicKey: sm2.PublicKey{
			Curve: sm2.P256Sm2(),
			X:     pubX,
			Y:     pubY,
		},
		D: priD,
	}

	// 私钥解密
	decrypt, err := sm2.Decrypt(sm2PriKey, cipherBytes, sm2.C1C3C2)
	if err != nil {
		return
	}
	fmt.Println(string(decrypt))
}

输出示例:

hello world

@daLingZhong

实测加密通过,感谢

@kiss-keray
Copy link

加密解密倒是可以了,签名和验签怎么不行啊

使用 hutool 做 SM2 加解密:

@Test
public void Sm2EncryptAndDecrypt() {
  SM2 sm2 = SmUtil.sm2();
  String publicKey = Hex.toHexString(sm2.getQ(false));
  String privateKey = Hex.toHexString(sm2.getD());;
  System.out.println("publicKey = " + publicKey);
  System.out.println("privateKey = " + privateKey);
  
  // 待加密的原文
  String data = "hello world";
  
  // 加密为 hex 格式
  String encryptHex = sm2.encryptHex(data, KeyType.PublicKey);
  System.out.println("encryptHex = " + encryptHex);
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(encryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

publicKey = 04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc
privateKey = 15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6
encryptHex = 04818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf
hello world

使用 js sm-crypto 解密 java 加密的密文:

const Sm2Crypto = require("sm-crypto").sm2;

// 密文,需要移除前面的 04 标识
let cipherText = "818a2a97c74c5a94d9ddf33707b215a0a0f86ec9e72b5ad1ee179cdb4527d5d4f853df4db3b2cf1904aa824c233bb5d53c388796801fc58f7542e8539c12b58cdada16e665ac796a0a047baddcfcd4fa22b083be334054179d2bca7d95ea6a0b3ef5fa32af3b01e9ebe4cf";
// 私钥
let privateKeyHex = "15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6";
// 解密
let decryptText = Sm2Crypto.doDecrypt(cipherText, privateKeyHex, 1);
console.log(decryptText);

// 公钥
let publicKeyHex = "04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc"
// 加密
let encryptText = Sm2Crypto.doEncrypt("hello world", publicKeyHex, 1);
console.log(encryptText);

输出示例:

// js 解密 java 的密文
hello world
// js 加密的密文
f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac

使用 hutool 解密 js 的密文:

@Test
public void Sm2DecryptTest() {
  // 还原 SM2 实例
  byte[] privateKey = Hex.decode("15ccdbb178f7f41c4acc7a74d1d35e6dbb3883d2e39559bf7bf74c0daac4d4d6");
  byte[] publicKey = Hex.decode("04d7124943876ff89a4bbe3d6f52f446a23868c760475c7993f30dc2fe3281b9a7866dc3644a175e435711ddde50918b46f4d2293a49aa9ffc77005b32f8bdb8dc");
  SM2 sm2 = new SM2(privateKey, publicKey);
  
  // js 密文前需要添加 04 标识
  String jsEncryptHex = "04f32bb02ba91d9eb67b4f36ef567ca9beb461ab69058ed072a4046bf132046828c6589e170235ec708e004619a37c2aeaccafbc549b584232df2449d7221a4bc772662330a7d0564f4b1940f496f561e9d9eccba5f17e147154242edbb5d6c76e7044d531e7d19279715cac";
  
  // 解密
  byte[] decrypt = sm2.decrypt(Hex.decode(jsEncryptHex));
  System.out.println(new String(decrypt, StandardCharsets.UTF_8));
}

输出示例:

hello world

@Montaro2017

加密倒是可以了,但是前面和验签不对,这个应该怎么处理?

@rookiezh
Copy link

rookiezh commented Aug 1, 2024

@changhr2013 您好,如果java后台生成的公私钥不是获取的Q值,js中有解决办法吗?java那边不乐意改代码;

004ff69d-9a18-4997-835c-9f3b50784947

@changhr2013
Copy link
Contributor

@changhr2013 您好,如果java后台生成的公私钥不是获取的Q值,js中有解决办法吗?java那边不乐意改代码;

004ff69d-9a18-4997-835c-9f3b50784947

一般 Java 默认生成私钥是 PKCS#8 格式的,公钥是 X.509 格式的,需要解编码。

可以参考 #109 的思路解编码即可。

最好还是沟通让后端改一下,后端现成的解析库,多一行代码的事。

裸公私钥存储和传输的成本也会低很多。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests