V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
imherer
V2EX  ›  Node.js

RSA 解密问题

  •  
  •   imherer · 2017-06-20 13:09:55 +08:00 · 4259 次点击
    这是一个创建于 2712 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    源数据如下:

    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 脚本

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

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

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

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

    但是酷派给的这个 是解密,而且它给过来的 key 也不是 RSA 格式的。它的 php demo 里确实是解密了。
    imherer
        7
    imherer  
    OP
       2017-06-20 14:42:33 +08:00
    @ytpfxnj 这是 demo 里的,没事的。
    它这个很奇葩,key 第一次 base64_decode 之后,会把结果里前面 40 个长度的字符串去掉,即取 41 到结尾的字符串再 base64_decode 一次 结果就正常了,demo 里就是这样写的。
    imherer
        8
    imherer  
    OP
       2017-06-20 14:46:53 +08:00
    @ChristopherWu 这个我看过了。不是的。
    ChristopherWu
        9
    ChristopherWu  
       2017-06-20 14:50:57 +08:00
    @imherer 你发 PHP 解密来看看?
    imherer
        10
    imherer  
    OP
       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
        11
    imherer  
    OP
       2017-06-20 14:56:44 +08:00
    @ChristopherWu 不好意思,这个格式太乱了,不知道怎么格式化
    popok
        12
    popok  
       2017-06-20 15:00:11 +08:00
    试着 base64_decode 了一下 Key,确实前 40 位是其他字符串
    ChristopherWu
        13
    ChristopherWu  
       2017-06-20 15:03:26 +08:00
    @imherer 看了下代码,不难啊。64510824,可以跟你交流一下
    ChristopherWu
        14
    ChristopherWu  
       2017-06-20 15:03:47 +08:00
    6453 10824,qq 号
    imherer
        15
    imherer  
    OP
       2017-06-20 15:06:25 +08:00
    @ChristopherWu 加您了
    popok
        16
    popok  
       2017-06-20 15:36:31 +08:00
    楼主这个问题好像困扰了 4 个月了啊
    popok
        17
    popok  
       2017-06-20 15:55:42 +08:00

    这个解密函数好像是自己写的吧,我没学过 php,不过查了下几个函数,都是很基本的。你自己用 node 一点点照着写就行了。
    好像和 RSA 没关系,就是用 2 个 key,把 sign 还原成 MD5 值。然后和原始数据的 MD5 值比较
    darkbread
        18
    darkbread  
       2017-06-20 16:06:50 +08:00
    楼主你看下 Math::hex2dec(), Math::int2byte()是怎么实现的
    imherer
        19
    imherer  
    OP
       2017-06-20 17:00:15 +08:00
    @popok 是啊,之前没解决就放那了,最近又要接,没法了。只能再来搞了。
    它这里面牵涉到大量的 BigInt 以及位与运算,实在是难搞定
    imherer
        20
    imherer  
    OP
       2017-06-20 17:01:10 +08:00
    @darkbread 都是一些 BigInt 以及位与运算什么的
    darkbread
        21
    darkbread  
       2017-06-20 17:21:35 +08:00
    @imherer 我的意思是如果你想自己写的话就要参考那两个函数了, 因为 RSA 算法大家应该用的都是同一个, 但输入和输出似乎是用那两个函数自定义了。
    imherer
        22
    imherer  
    OP
       2017-06-20 17:23:03 +08:00
    @darkbread 好的,谢谢!
    popok
        23
    popok  
       2017-06-20 17:24:27 +08:00 via iPhone
    @darkbread 那 2 个好像是普通的进制转换吧
    darkbread
        24
    darkbread  
       2017-06-20 17:34:01 +08:00
    @popok 不懂 PHP,不过看名字好像是的。因为刚刚拿私钥验证了一下发现对不上就怀疑那两个函数是不是有进行其他的格式化。随手试的,说错了请见谅。
    jiangzhuo
        25
    jiangzhuo  
       2017-06-20 17:49:20 +08:00
    imherer
        26
    imherer  
    OP
       2017-06-20 17:54:58 +08:00
    @jiangzhuo 非常感谢,稍等 我先看看,如果没问题一定红包感谢你!😀
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3503 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 10:25 · PVG 18:25 · LAX 02:25 · JFK 05:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.