RSA 解密问题

2017-06-20 13:09:55 +08:00
 imherer

最近在做酷派支付验证,遇到签名验证问题,卡好久了。

源数据如下:

var trans_data = '{"exorderno":"10004200000001100042","transid":"02113013118562300203","waresid":1,"appid":"20004600000001200046","feetype":0,"money":3000,"count":1,"result":0,"transtype":0,"transtime":"2013-01-31 18:57:27","cpprivate":"123456"}';
var key = 'MjhERTEwQkFBRDJBRTRERDhDM0FBNkZBMzNFQ0RFMTFCQTBCQzE3QU1UUTRPRFV6TkRjeU16UTVNRFUyTnpnek9ETXJNVE15T1RRME9EZzROVGsyTVRreU1ETXdNRE0zTnpjd01EazNNekV5T1RJek1qUXlNemN4';
var sign = '28adee792782d2f723e17ee1ef877e7 166bc3119507f43b06977786376c0434 633cabdb9ee80044bc8108d2e9b3c86e';

这个 key 经过 2 次 base64_decode 拿到的有效数据为:14885347234905678383+132944888596192030037770097312923242371 其中+的前面为 privatekey,后面为 modkey

文档里是这样写的: key = base64(${private_key}+${mod_key}),即 key 是由 private_key 和 mod_key 中间用加号“+”连接后做了 base64 编码得到的字符串。商户需要先把 private_key 和 mod_key 从 key 中解析出来。然后用 RSA 算法和解析出来的 keys 对签名 sign 进行解密得到的字符串,与将 transdata 进行 MD5 加密后的字符串进行比较。如果两者相等,则签名验证通过。

我在网上有找到这个:RSAKeyPair,但是都只有说加密,没说解密,我找到了它的解密函数,但是解出来是乱码 我自己先用简单的数据加密,然后再调它的解密,出来还是乱码。

对加解密这块的知识实在是匮乏,求各位大神帮忙,解决了红包感谢!

我现在临时的解决方案是:解密算法用 php 实现的,然后用 node 执行 php 脚本

4118 次点击
所在节点    Node.js
26 条回复
3pointer
2017-06-20 13:48:21 +08:00
RSA 加解密原理是一样,加密用公钥,解密用私钥,你找到了加密方法,就找到了解密方法,另外你暴露出 privatekey 是不是不太好
3pointer
2017-06-20 14:02:21 +08:00
还有那个 sign 就是 transdata 进行 MD5 之后,用发送方的 私钥 进行 RSA 加密后的数据,
你要做的就是 把 sign 用 发送方的公钥 进行 RSA 加密,比对一下 transdata 的 MD5 值 ,如果相等则证明消息来源于发送方,因为只有发送方才具有私钥。其中 RSA 加密都是一样的方法,只是传递的密钥不同
jiangzhuo
2017-06-20 14:09:01 +08:00
既然有 php 代码了,那照着翻译一遍就好了,楼主要懒得写可以出钱雇人翻译,这种有代码你给几个输入输出的值,这么需求明确的活很多人愿意接的
ChristopherWu
2017-06-20 14:19:30 +08:00
Google 搜索一下就有了: https://github.com/rzcoder/node-rsa
ytpfxnj
2017-06-20 14:34:30 +08:00
既然暴露了私钥就重新生成一对密钥吧。

我把 14885347234905678383+132944888596192030037770097312923242371 使用 base64 加密两次结果跟楼主不一样,大概问题在这里吧。

TVRRNE9EVXpORGN5TXpRNU1EVTJOemd6T0RNck1UTXlPVFEwT0RnNE5UazJNVGt5
TURNd01ETTNOemN3TURrMwpNekV5T1RJek1qUXlNemN4Q2c9PQo=
imherer
2017-06-20 14:39:54 +08:00
@3pointer 谢谢你的恢复
没事的,这个数据是 demo 里的

我知道的 RSA-MD5 是用发送方用私钥加密,然后接收方拿到签名后,用公钥进行验签。

但是酷派给的这个 是解密,而且它给过来的 key 也不是 RSA 格式的。它的 php demo 里确实是解密了。
imherer
2017-06-20 14:42:33 +08:00
@ytpfxnj 这是 demo 里的,没事的。
它这个很奇葩,key 第一次 base64_decode 之后,会把结果里前面 40 个长度的字符串去掉,即取 41 到结尾的字符串再 base64_decode 一次 结果就正常了,demo 里就是这样写的。
imherer
2017-06-20 14:46:53 +08:00
@ChristopherWu 这个我看过了。不是的。
ChristopherWu
2017-06-20 14:50:57 +08:00
@imherer 你发 PHP 解密来看看?
imherer
2017-06-20 14:56:20 +08:00
@ChristopherWu
````
CoolpayDecryptDemo.php
<?php

require 'CoolpayDecrypt.php';

//以下三个数据为演示数据 trans_data 和 sign 为报文中获取的字段,key 为从商户自服务获取的应用密钥。
$trans_data = '{"exorderno":"10004200000001100042","transid":"02113013118562300203","waresid":1,"appid":"20004600000001200046","feetype":0,"money":3000,"count":1,"result":0,"transtype":0,"transtime":"2013-01-31 18:57:27","cpprivate":"123456"}';
$key = 'MjhERTEwQkFBRDJBRTRERDhDM0FBNkZBMzNFQ0RFMTFCQTBCQzE3QU1UUTRPRFV6TkRjeU16UTVNRFUyTnpnek9ETXJNVE15T1RRME9EZzROVGsyTVRreU1ETXdNRE0zTnpjd01EazNNekV5T1RJek1qUXlNemN4';
$sign = '28adee792782d2f723e17ee1ef877e7 166bc3119507f43b06977786376c0434 633cabdb9ee80044bc8108d2e9b3c86e';
// $sign ='2b6efac86ef6b58448a8d13a7341a904 fc56eaddca4ad6b2b95c3762dd0cb9b 2dc35815c9a1da8128112501b809c6c1';

$tools = new CoolpayDecrypt();
$result = $tools->validsign($trans_data,$sign,$key);
// $result = $tools->gensign($trans_data,$key);
// echo $result;
if($result == 0)
//验签名成功,添加处理业务逻辑的代码;
echo 'SUCCESS';
else
echo 'FAILED';
?>
````

CoolpayDecrypt.php

<?php

require 'RSAUtil.php';

class CoolpayDecrypt{
public function validsign($trans_data,$sign,$key){
$rsa = new RSAUtil();

//解析 key 需要从商户自服务提供的 key 中解析出我们的真正的 key. 商户自服务提供的 key = mybase64(private_key+mod_key);
$key1 = base64_decode($key);
//echo "$key1";
$key2 = substr($key1,40,strlen($key1)-40);
// echo "$key2";
$key3 = base64_decode($key2);
// echo "$key3";
//php 5.3 环境用下面这个
if(phpversion () > "5.3"){
list($private_key, $mod_key) = explode("+", $key3);
}else{
list($private_key, $mod_key) = split("\\+", $key3);
}
//使用解析出来的 key,解密包体中传过来的 sign 签名值
$sign_md5 = $rsa->decrypt($sign, $private_key, $mod_key);
$msg_md5 = md5($trans_data);


return strcmp($msg_md5,$sign_md5);
}
}
?>

RSAUtil.php 这个里面基本就只用到了这个函数
require 'Math.php';

public function decrypt($string, $d, $n){
//解决某些机器验签时好时坏的 bug
//BCMath 里面的函数 有的机器 php.ini 设置不起作用
//要在 RSAUtil 的方法 decrypt 加 bcscale(0);这样一行代码才行
//要不有的机器计算的时候会有小数点 就会失败
bcscale(0);

$bln = $this->keylen * 2 - 1;
$bitlen = ceil($bln / 8);
$arr = explode(' ', $string);
$data = '';
foreach($arr as $v){
$v = Math::hex2dec($v);
$v = bcpowmod($v, $d, $n);
$data .= Math::int2byte($v);
}
return trim($data);
}

然后还有一个 Math.php 文件 就是在 RSAUtil.php 里会用到,不过代码有点多
imherer
2017-06-20 14:56:44 +08:00
@ChristopherWu 不好意思,这个格式太乱了,不知道怎么格式化
popok
2017-06-20 15:00:11 +08:00
试着 base64_decode 了一下 Key,确实前 40 位是其他字符串
ChristopherWu
2017-06-20 15:03:26 +08:00
@imherer 看了下代码,不难啊。64510824,可以跟你交流一下
ChristopherWu
2017-06-20 15:03:47 +08:00
6453 10824,qq 号
imherer
2017-06-20 15:06:25 +08:00
@ChristopherWu 加您了
popok
2017-06-20 15:36:31 +08:00
楼主这个问题好像困扰了 4 个月了啊
popok
2017-06-20 15:55:42 +08:00

这个解密函数好像是自己写的吧,我没学过 php,不过查了下几个函数,都是很基本的。你自己用 node 一点点照着写就行了。
好像和 RSA 没关系,就是用 2 个 key,把 sign 还原成 MD5 值。然后和原始数据的 MD5 值比较
darkbread
2017-06-20 16:06:50 +08:00
楼主你看下 Math::hex2dec(), Math::int2byte()是怎么实现的
imherer
2017-06-20 17:00:15 +08:00
@popok 是啊,之前没解决就放那了,最近又要接,没法了。只能再来搞了。
它这里面牵涉到大量的 BigInt 以及位与运算,实在是难搞定
imherer
2017-06-20 17:01:10 +08:00
@darkbread 都是一些 BigInt 以及位与运算什么的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/369776

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX