签名与验签

签名与验签

一、注意事项

1. 平台使用RSA2(SHA256WithRSA)签名方式,密钥为 pkcs#8格式
2. 接口中的所有参数的编码都是UTF-8,所以跟平台交互或者计算签名的时候字符编码都是UTF-8;
3. 如果参数的值为空不参与签名;
4. 参数名区分大小写;
5. 参数名为sign参数不参与签名

二、密钥说明

测试环境公钥、私钥,联调时百度配置
线上环境公钥、私钥重新配置

1)测试环境交互方私钥

----- BEGIN PRIVATE KEY -----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC+/7DBc/a/M8Ks
CK6bbIWsznkLJo6msytb5YITvRGl5jJQJKx6+injGn0eMJnbKAXp4JDGOk542utQ
c27wIRaLBzOPEsI3bz3X9xAr6kWv+HCk3V7YRfPiGZwsonc4d4tBYQ/S5ez0pfm/
C76biG/jDHJkuE7ep3/OV1MDQYaNZsmofzIdbYz0Y6s4isqOQjOfrHo/Xm4wIUZE
8W5nx8oy/u7ZjtteEe7QSPa7e0XX/0nKkAhT4uLbob/RZhgBNBXFQTfq4BRJvv/q
WdW3GsjMdmVy0g803bMpuFGRmC48Ap49ezdUDynDt9jijOmASpqucs4AJZBAuZa3
8P0sE78PAgMBAAECggEBAKxaSSNYxKmnSJ07PG0OUdYtf3b7dTCib5b+lRHAxGMj
RfWiEm7qvc+swIj0rHHiPOvedGYciWFvk3bGo65W/WO5vOH3uICThogS72nhfr8b
fnKXPJYHEdvO5mo0tUakQWpe7wuN5fSiraqjK35ocSa+vZ973df36FHH5SYEBCol
rmzXzmTrKBWQ5KMc+uGXz/Y2m8iZsO3zF1uT/7RwKsIjFm4Xz0j7K0d5oBBmiyHU
paOs230a8AMvA9BZQ7EzK//5G9+f2YcUJ8jQ5tOilQHZ9Pdz/+pYKdi9ALtbp+ya
kIiEwwVPBV55yIj7uifp4/pwhxJLaoea2ifJNInHSFkCgYEA6K2bVksYCGFlaZpy
reJl9LjneJ3k8QwxVj31DyhsH92dvtYvoOG0A8nskfI3KzVFP5Cw7sUSqimAHmDs
acU90PTSIdxd0jqPc5W06njIWmYQchJZzsFpg/0Fba5Ale/K9dKJUgVJwqWL2AoZ
gbb5dbY/MHuKEsGJn/ykasOvZyMCgYEA0iSdPf/o0VeRVsFGn/PFfKSN0rJ52aOw
MeZbtT7deeJqm95O0htSfA/0M6v0BOu8qUFsTdhtRO7KXvk/TFCqyo9eFDsoN/pW
9UB5MyIyNHqeP1CfSGbHYNxexFIsuK4kHOIi/8clUSxQOOH0p1a+FCjQ5kSw1hKE
EZxSHZznvSUCgYEA2dEDdOHxio7ap+Rs7LjfJxwdnH17P+hhG1H+4gS+S4pkTK0n
gx0nLtDNxMaRvMIupFXoPG2CjocxJ78mMdHyj3VQbWBk+BPYDKUd6s3NCUs4pADn
z7Sh+WxYs5eHYJVNU5iW8pB+v1fINSMnQ5Ytq3NDbjiIRgylsH6K0GOGBS8CgYBy
jf8h4zV5mTDjiC0F2Q+ZKOMfLqf6Gp9cNGVd4k09uk2/dmqlYruOYewPlvdJD3Ej
G0T9ErROVZYAPANjiL3x+kGg6ba8/T0WKPVpCIUx0n4dqceK4mxhwv/uKZWzf0q0
bBoEMP2IyI1a1OJXrpOjzbRDMsLrj+Bq8TpaZkR1+QKBgQDJoaNX5Jgi1ifR+3rR
ghEruB2qze/PNFTHD7LmTHlqfgTzIvccZ0bd41kC0CSKbkrTYQ/ihQnKp3YRLUiC
n700qN+EjrIPVqH1O7wJcAvBuw43wtoC1dushs4FkArHQYEGomA4zlgk1Q9kE9l/
X0YZriBvP/2YtUt2kyyo+n0fJw==
-----END PRIVATE KEY-----

2)测试环境交互方公钥

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvv+wwXP2vzPCrAium2yF
rM55CyaOprMrW+WCE70RpeYyUCSsevop4xp9HjCZ2ygF6eCQxjpOeNrrUHNu8CEW
iwczjxLCN2891/cQK+pFr/hwpN1e2EXz4hmcLKJ3OHeLQWEP0uXs9KX5vwu+m4hv
4wxyZLhO3qd/zldTA0GGjWbJqH8yHW2M9GOrOIrKjkIzn6x6P15uMCFGRPFuZ8fK
Mv7u2Y7bXhHu0Ej2u3tF1/9JypAIU+Li26G/0WYYATQVxUE36uAUSb7/6lnVtxrI
zHZlctIPNN2zKbhRkZguPAKePXs3VA8pw7fY4ozpgEqarnLOACWQQLmWt/D9LBO/
DwIDAQAB
-----END PUBLIC KEY-----

3)测试环境平台私钥

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDO/j5+5S+rjtqS
HdO+aUi+Lv73Hfvw+qNz6oz6UcNm8D1viFXkA64JLMlVF2FcL0oKImS1lRZP9Fww
uO9i3BV6hAeMiSEXEJWx8xnF7A9JH6Wjun7kwWrCDzVkjljjvUJjFv1tGehJJZHi
15IBKASoVdU7LtVijtU5hXMaNssAtclMPbE6vyiQGzjjgqIvFIYQ1TKw7ZcO9eDP
iuw7uJBJ0eyqaugwj1YoN9YRsvXzdOy8VVyJErn9+1Vkyx2wll7BPssKn/qXYPBf
5HN267EZ0ouWH45DqQD29nt0s8DeNech+ocBYawkt8iXaFAtGzfFptT3ygslz0HL
5wEJ/UHNAgMBAAECggEBAI3OfD4vASOFMJRdvsx6EI3KYH4nmoyTCRvGmmJ7VWTW
PSCFxGH7a2V5u0yCQf8Hnq+mtFv6EVkhTW9LoySVJlnSzeEnqPXGNOe+Ze/EMwyj
k7TBdWF+aIceKDQFmTwcUrjT7TarQwmppXJEwQ/tvJMeioCLK0DkHYvUsvBIjXaj
193T0ET/x9GpWBF881mHBNZWj2g74Mhj8M6AsHcbbLdHpLXeUB9oPI8L+aFihxiN
3f77DPsEs9eHY3RMattK5rUks2+IzH/kA3+a/8uItwi/wX9Dn4M1MwaSbVH4C3XT
4h09eB3X49Pv6c0xabxl9I8tW/PQNVGF4+AZQVy7tPkCgYEA8Bk6KksJkmWn5FJL
Oj27ZOyyiRKtqRWdYAmuOgIbaL+dZ+JUK9Gqn3yzwHUwDuOufB+238LE4xNhFrWU
GJT9b6liiueTtpiGmwdh/POM3ObYECkooQUEdLD35sTqKBhREfq69wwwE8GMyye1
GOidoi+C95CN6Ubo7cPoMuHkoIcCgYEA3LO6c71LKczjdrdj1Gw4WnWeJHnu6ZUD
MnNEvMNezOlGbw97J/0ArOmd939/5pOVjKYCfGu0PTp6V+NjIk2ARJX/zcHXkbro
OfqtgO7brCzrKmiRWad70BeSy1p4HBYlGpJ6nS2y23P9YIE1h/6w06ZkBtCGDjlK
FEYMI6NlxAsCgYEAlVk74x71/0iYnN/Lx5iqvma1z6n8uBan5BthctfosMmwYfRZ
I0Cyf66UKX07vadG3BLmMF35Fr7xP60HSBs1YoXctEbEjWpnIHwBKdVdo/M8JyFT
EoYKdYO3UKbgeLxXgXWTYyUDquZUa/JxJeNcWoxvTQtwGtFvBqc8ApqdGnUCgYBZ
OlCnemBpBezwkjRKLXabG/JTzPhKd3RTcUbZCiPJtIZFYHzqLd+YCNtUtLeBASn1
MyjOJL06A+pynfv2Yl75W6uQBE6jHG3HAxIijm9BrOdmveAF3zCvcNhoXqswr8NQ
eugXo+Ir1zkZSyPZV58l8p9+IdAQ8BiDGc7OmcELAQKBgBydPl1MohoJY+L5Hcsk
CD67/uG92D0tOC/CiKxvGfOBJ2x/fjwRmX9ia3LBC+VdOSZOCppEwCyvlT2gN1kf
jAVR/bsxG+DBbYgulZJv3vpIXsG/egwpBxpPlTN+xHISZu4rCxFQL7Sf58pUBO8F
UoVgx4mnsEJHAETn3/RDrgW6
-----END PRIVATE KEY-----

4)测试环境平台公钥

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzv4+fuUvq47akh3TvmlI
vi7+9x378Pqjc+qM+lHDZvA9b4hV5AOuCSzJVRdhXC9KCiJktZUWT/RcMLjvYtwV
eoQHjIkhFxCVsfMZxewPSR+lo7p+5MFqwg81ZI5Y471CYxb9bRnoSSWR4teSASgE
qFXVOy7VYo7VOYVzGjbLALXJTD2xOr8okBs444KiLxSGENUysO2XDvXgz4rsO7iQ
SdHsqmroMI9WKDfWEbL183TsvFVciRK5/ftVZMsdsJZewT7LCp/6l2DwX+Rzduux
GdKLlh+OQ6kA9vZ7dLPA3jXnIfqHAWGsJLfIl2hQLRs3xabU98oLJc9By+cBCf1B
zQIDAQAB
-----END PUBLIC KEY-----

三、签名步骤

1.筛选并排序
  获取所有请求参数,不包括字节类型参数,如文件、字节流,剔除sign字段,剔除值为空的参数,并按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。
2.拼接
将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。
3.调用签名函数
使用各自语言对应的SHA256WithRSA签名函数利用商户私钥对待签名字符串进行签名,并进行Base64编码。
4.把生成的签名赋值给sign参数,拼接到请求参数中。

四、生成签名和验证签名方法示例(PHP)

    /**
     * @desc 私钥生成签名字符串
     * @param array $assocArr 参与签名参数数组
     * @param string $rsaPriKeyStr  用户私钥
     * @return string   密钥串   返回不为空字符串表示生成签名成功
     * @throws Exception
     */
    public  function genSignWithRsa(array $assocArr, $rsaPriKeyStr)
    {
        $sign = '';
        if (empty($assocArr) || !is_array($assocArr) || empty($rsaPriKeyStr)) {
            return $sign;
        }

        //需要参与sign计算的参数
        $arrNeedSignParams = [];
        foreach ($assocArr as $key => $val) {
            if ($val === null || $val === ""
                || (is_array($val) && empty($val))
            ) {
                continue;
            } else {
                $arrNeedSignParams[$key] = $val;
            }
        }


        if (!function_exists('openssl_pkey_get_private') || !function_exists('openssl_sign')) {
            throw new Exception("openssl扩展不存在");
        }

        $priKey = openssl_pkey_get_private($rsaPriKeyStr);

        if (isset($arrNeedSignParams['sign'])) {
            unset($arrNeedSignParams['sign']);
        }

        ksort($arrNeedSignParams); //按字母升序排序
        if (empty($arrNeedSignParams)) {
            return $sign;
        }

        $parts = array();
        foreach ($arrNeedSignParams as $k => $v) {
            $parts[] = $k . '=' . $v;
        }
        $str = implode('&', $parts);
        openssl_sign($str, $sign, $priKey,'SHA256');
        openssl_free_key($priKey);

        return base64_encode($sign);
    }


    /**
     * @desc 签名校验
     * @param array $assocArr  参与签名参数数组
     * @param string $rsaPubKeyStr  平台公钥
     * @return bool  $result    验签结果  true 验签成功 false 验签失败
     * @throws Exception
     */
    private function checkSignWithRsa(array $assocArr, $rsaPubKeyStr)
    {

        $arrNeedSignParams = [];

        if (empty($assocArr) || !is_array($assocArr) || empty($rsaPubKeyStr)) {
            return false;
        }

        foreach ($assocArr as $key => $val) {
            if ($val === NULL || $val === ""
                || (is_array($val) && empty($val))
            ) {
                continue;
            } else {
                $arrNeedSignParams[$key] = $val;
            }
        }

        if (!isset($arrNeedSignParams['sign']) || empty($arrNeedSignParams)) {
            return false;
        }

        if (!function_exists('openssl_pkey_get_public') || !function_exists('openssl_verify')) {
            throw new Exception("openssl扩展不存在");
        }

        $sign = $arrNeedSignParams['sign'];
        unset($arrNeedSignParams['sign']);

        if (empty($arrNeedSignParams)) {
            return false;
        }
        ksort($arrNeedSignParams); //按字母升序排序
        $parts = array();
        foreach ($arrNeedSignParams as $k => $v) {
            $parts[] = $k . '=' . $v;
        }
        $str = implode('&', $parts);

        $sign = base64_decode($sign);
        $pubKey = openssl_pkey_get_public($rsaPubKeyStr);
        $result = (bool)openssl_verify($str, $sign, $pubKey, 'SHA256');
        openssl_free_key($pubKey);

        return $result;
    }
开发者订单数据传输接入文档申请退款后退款审核接口