PHP在做api开发中,RSA加密算法的使用

RSA 加密是什么
RSA(Rivest-Shamir-Adleman)是最早的公钥密码系统之一,广泛用于安全数据传输。3 位数学家 Rivest、Shamir 和 Adleman 的名字来命名的。
是非对称加密的一种 这种算法非常可靠,密钥越长,它就越难破解。.
在这样的密码系统中,加密密钥是公共的,并且它与保密(私有)的解密密钥不同
 
加密
RSA 密码体制是一种公钥密码体制,加密算法公开,以分配的密钥作为加密解密的关键
一般来说,在一对公私钥中,公钥和私钥都可以用来加密和解密,即公钥加密能且只能被对应的私钥进行解密
私钥加密能且只能被对应的公钥进行解密。
一般我们都是把公钥公开出去 如果我们是服务方 一般我们是拿私钥加密 接收方来拿公钥进行验签等
为了保证加密安全 建议 RSA 密钥的长度为 2048
 
签名
签名就是在这份资料后面增加一段强而有力的证明,以此证明这段信息的发布者和这段信息的有效性完整性。
简单来说,签名主要包含两个过程:摘要 和 非对称加密,首先对需要签名的数据做摘要(类似于常见的 MD5)后得到摘要结果。
然后通过签名者的私钥对摘要结果进行非对称加密即可得到签名结果
 
支付中签名的套路
来说下对接的支付中的签名,拿 支付宝 举例
 
生成签名
1. 生成参数并进行 url_encode 然后按照字典排序,组成字符串 等到待签名字符串。
2. 获取私钥 然后使用各自语音的加密方法 对待签名字符串进行加密
3. 得到签名 (sign) 后并进行 base64 转码
4. 把加密字符串的的数组 和 签名 一并发给接受方
 
验证签名
1. 在通知返回参数列表中,除去 签名参数 (sign ) 以及空的参数 其他的全部都是待签名的参数。
2. 将剩下参数进行 url_decode, 然后进行字典排序, 使用 & = 组成字符串,得到待签名字符串。
 
3. 将签名参数(sign)使用 base64 解码为字节码串。
4. 使用 RSA 的验签方法,通过签名字符串、签名参数(经过 base64 解码)及支付宝公钥验证签名,根据返回结果判定是否验签通过
 
PHP 中 OpenSSL 的使用
php 的扩展中有 OpenSSL 库 可以用来操作 对称/非对称的加密 算法
 
下面贴段大概的 PHP 示例代码
生成待 签名 / 验签 的字符串
/**
 * 生成待签名的字符串
 * @param $data 参与签名的参数数组
 * @return string 待签名的字符串
 */
function getSignStr($data)
{
    //排序
    ksort($data);
    //剔除sign 如果对方的签名叫sign 或者可以在调用方法的时候剔除
    //unset($data['sign']);

    $stringToBeSigned = '';

    $i = 0;

    foreach ($data as $k => $v) {
        if ($i == 0) {
            $stringToBeSigned .= "$k" . "=" . "$v";
        } else {
            $stringToBeSigned .= "&" . "$k" . "=" . "$v";
        }

        $i++;
    }

    return $stringToBeSigned;
}
生成签名,返回签名字符串sign
/**
 * 生成签名
 * @param array $params 待签名的所有参数
 * @return string 生成的签名
 */
function getSignGenerator($params)
{
    //生成待验签的字符串
    $data = $this->getSignStr($params);
    //私钥的内容 一行的格式
    $privateKey = 'xxx';

    $pem = "-----BEGIN RSA PRIVATE KEY-----\n" .
        wordwrap($privateKey, 64, "\n", true) .
        "\n-----END RSA PRIVATE KEY-----";

    //openssl_private_encrypt($data, $crypted, $pem);
    openssl_sign($data, $sign, $pem, OPENSSL_ALGO_SHA256);

    $sign = base64_encode($sign);

    return $sign;
}
验证签名
/**
 * 验证签名
 * @param array $params 待签名的所有参数
 * @param string $sign 生成的签名
 * @return boolean 校验的结果
 */
function signCheck($params, $sign)
{
    //生成待验签的字符串
    $data = $this->getSignStr($params);
    //对方的公钥内容 一行的形式
    $publicKey = 'xxx';

    $pem = "-----BEGIN PUBLIC KEY-----\n" .
        wordwrap($publicKey, 64, "\n", true) .
        "\n-----END PUBLIC KEY-----";

    $checkResult = (bool)openssl_verify($data, base64_decode($sign), $pem, OPENSSL_ALGO_SHA256);

    return $checkResult;
}
自己的加密方法
/**
 * 我们自己的加密
 * @param $str 待加密的字段
 * @return string
 */
function encrypt($string)
{
    //公钥内容 一行的形式
    $pubKey = 'xxxx';

    $res = "-----BEGIN PUBLIC KEY-----\n" .
        wordwrap($pubKey, 64, "\n", true) .
        "\n-----END PUBLIC KEY-----";

    openssl_public_encrypt($string, $encrypt, $res);

    return base64_encode($encrypt);
}
自己的解密方法
/**
 * 我们自己的解密
 * @param $secret 加密后的base64字段
 * @return string
 */
function decrypt($secret)
{
    //私钥内容 一行的形式
    $privateKey = 'xx';

    $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
        wordwrap($privateKey, 64, "\n", true) .
        "\n-----END RSA PRIVATE KEY-----";

    openssl_private_decrypt(base64_decode($secret), $oldData, $res);

    return $oldData;
}
注意:主要使用到下面几个函数 分别都是对应的
私钥加密 openssl_private_encrypt => 公钥解密 openssl_public_decrypt
公钥加密 openssl_public_encrypt => 私钥解密 openssl_private_decrypt
 
使用的时候分别使用对应的公钥 / 私钥的文件内容即可,我是把公钥和私钥都配置了一行形式 (也就是没有换行符等 方便配置吧)。