PHP中RSA加密演算法的使用

reggie發表於2021-08-06

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

也可以直接儲存檔案(需要讀取資源)

本作品採用《CC 協議》,轉載必須註明作者和本文連結
微信公眾號:碼咚沒 ( ID: codingdongmei )

相關文章