PHP中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, 然后进行字典排序, 使用 & = 组成字符串,得到待签名字符串

body=大乐透2.1&buyer_id=2088102116773037&charset=utf-8&gmt_close=2016-07-19 14:10:46&gmt_payment=2016-07-19 14:10:47&notify_time=2016-07-19 14:10:49&notify_type=trade_status_sync&out_trade_no=0719141034-6418&refund_fee=0.00&subject=大乐透2.1&total_amount=2.00&trade_no=2016071921001003030200089909&trade_status=TRADE_SUCCESS&version=1.0

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;
}

生成签名

/**
 * 生成签名
 * @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

使用的时候分别使用对应的公钥/私钥的文件内容即可

我是把公钥和私钥都配置了一行形式(也就是没有换行符等 方便配置吧)

可是使用支付宝的签名工具来生成

下面贴2个测试使用

private_pem=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCCozdqfMkO/Rz9sPe1Peg6WVAGike13FNqgtdSKrekAFw7dwVIXrCUY82C+ojUrVCswtNAjpLP9h830V6GAYwLhb8qbDIUDWKb3uXHw5a2RXa9zuZ5eo7Sue1boJffBQt5fG/BZ5aB8if6BGln92WucVNqNMB7Q68i5O7zDtQ1ulxC8VXSAqYHtiqMkRH+krnB0N8KQHay0SpGtnTMKYn7J+kkaqn0Q+xiwvwkR709cvXPzTDsgu74GqNrk1/EJfRWx1/7ViHOqRoWlLG8pRe74RldXobonrff0fZHh/+64XmPGick8DGoVdXy0VDh9JCZd/sb1dsc7ZupcuAWo2hpAgMBAAECggEAcbxsaNfPrK8N6ImEgFUvtG1B0Q/fMyRps5fKo4zxaWWu507OWJ2oGulxaZF6Q7/JN0A/8Dqw80ITF+O+6ei65uVA9ExSpUaqDY7Yez18H6EGqmZSHn1nj2u0jUuaLd8UiGagXcA0N1p/F5tzscKg0mlz+EmJM0hQWI78wrysYUFd50gDZUp8MkDQF1LoUaRuS5j7kvC2rcjGcG0d8DyrOhC1Y+hP135cP+lAuJt2aE2RqZo1gg3BkEG80ERZjthmlzmMAoYo7MD0oFhKqP7BeQRvK7y8SKr4PpsloIwkAXLzX+JFaoYh4tvRXyjV9OjC/KlsPuOOIGECAqCYTVkEiQKBgQDy9MrXdtZBPeV9b5HxIUUBcXrmjFWZLTbVqpldEZQc3LwaDCBz0mf5Zy4K3cFuwjQl9SBSjBFH9W/hN4bVgkoDZkfljjipAwM5cBC2krHFkkXiNhDhv5ewxqTEhPlg2mLMDOYufNlXXknev0ZHaWf9tNpCshwHIlhz2u9sI44cKwKBgQCJprVfb+uXNMXMi9SkKy4CLp4HuppN9JZfTPZBJxGHnR/xIwzcqPzR0OzC9vv4H4m/kkLYBb6wqmiVof5xmigndop3CwABhJkDOAXbRFNg2AoJAMEa1tEzne4+1eL+ixv+kePVbHtDKj8haFB2jUGevYtdp8b/D2CyGKOwnlL/uwKBgGdIEQLPjf1mJqzQRSZQklkuuKRRfAIF6o2iXFfoJSKv43bfONyD0ZeAeV49LwG6pRiNwBPXX3FLsSf4xuT04inXzTCU6RlQIorETRaP/eqTaAnmGf8dx4pqp5izPqAhtmYwBZo1kN9HQffjMCkjcgW0YCSCOs/9lcSKicVayArJAoGBAIGc9j/NVRrl2DKIoEuNVR884hUJAm7RFh7aiy5CKWF29DLM/Dly7cxrW+UhR5UVrVEz2bkrf6k+bIgpJzu1GoY+OpdpRjIntySuxeT3a0NPafOcQDiTeh2OYl3MbJeeSVfl65B1O4O87gD5Bnr6+4ULmYUwjFJzGmvYg5m1GxYtAoGADGU0fr7BjQCeb6/VfPlgAotlrCp0hcfhCgSaaOc3D6qXdt6xqlK0CoKFDoW3Nd1krz6JDz9V0m2b30oBwjL9yLR5df2OpJG19KPIaI8EqqJ6onu48dZ9Mo7epE5D90LICbyJ9iChnCOadb0moU1f6O1J3CZapjEuWUDay2zOP1s=
public_pem=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgqM3anzJDv0c/bD3tT3oOllQBopHtdxTaoLXUiq3pABcO3cFSF6wlGPNgvqI1K1QrMLTQI6Sz/YfN9FehgGMC4W/KmwyFA1im97lx8OWtkV2vc7meXqO0rntW6CX3wULeXxvwWeWgfIn+gRpZ/dlrnFTajTAe0OvIuTu8w7UNbpcQvFV0gKmB7YqjJER/pK5wdDfCkB2stEqRrZ0zCmJ+yfpJGqp9EPsYsL8JEe9PXL1z80w7ILu+Bqja5NfxCX0Vsdf+1YhzqkaFpSxvKUXu+EZXV6G6J6339H2R4f/uuF5jxonJPAxqFXV8tFQ4fSQmXf7G9XbHO2bqXLgFqNoaQIDAQAB

也可以直接存储文件(需要读取资源)

php
本作品采用《CC 协议》,转载必须注明作者和本文链接
微信公众号:码咚没 ( ID: codingdongmei )
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5

请问一下,php可以实现rsa只要私钥加密解密吗,好像java可以,不知道php行不行

2年前 评论
deatil 2年前
$i = 0;

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

    $i++;
}

可以用 http_build_query ($data)实现更简洁:bowtie:

2年前 评论

推荐个 spatie 的包: github.com/spatie/crypto

2年前 评论
月光 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!