RSA加密算法的应用

<?php
/**
 * 生成RSA密钥对(公钥和私钥)的命令:
 * [root@localhost ~]# openssl genrsa -out rsa_private_key.pem 1024
 * [root@localhost ~]# openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem
 * [root@localhost ~]# openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
 * 命令①:生成原始RSA私钥文件
 * 命令②:将原始RSA私钥文件转换为pkcs8格式
 * 命令③:根据原始RSA私钥文件生成RSA公钥文件
 * 备 注:公钥发放给客户端用于加密数据,私钥则要留在服务端用于解密客户端发送过来的加密数据。
 */
class RSA
{
    private static $publicKeyPath = __DIR__ . '/rsa_public_key.pem';
    private static $privateKeyPath = __DIR__ . '/rsa_private_key.pem';
    private static $signatureAlgo = OPENSSL_ALGO_SHA512; // 签名算法

    /**
     * (使用公钥)加密
     * 备注:相同的数据每次加密产生的密文都是不同的。
     *
     * @param string $data 要加密的数据
     * @return string 数据加密后的密文
     */
    public static function encrypt($data)
    {
        $encrypt = ''; // 用于保存密文的变量

        // 获取公钥的内容(即字符串)
        $publicKeyContents = file_get_contents(self::$publicKeyPath);

        // 获取公钥的资源(获取失败返回FALSE)
        $publicKeyResource = openssl_pkey_get_public($publicKeyContents);

        $dataSplit = str_split($data, 117);
        foreach ($dataSplit as $split) {
            $splitEncrypt = '';
            openssl_public_encrypt($split, $splitEncrypt, $publicKeyResource); // 加密
            $encrypt .= $splitEncrypt;
        }

        return base64_encode($encrypt);
    }

    /**
     * (使用私钥)解密
     *
     * @param string $data 要解密的数据
     * @return string 数据解密后的明文
     */
    public static function decrypt($data)
    {
        $decrypt = ''; // 用于保存明文的变量

        // 获取私钥的内容(即字符串)
        $privateKeyContents = file_get_contents(self::$privateKeyPath);

        // 获取私钥的资源(获取失败返回FALSE)
        $privateKeyResource = openssl_pkey_get_private($privateKeyContents);

        $data = base64_decode($data);

        $dataSplit = str_split($data, 128);

        foreach ($dataSplit as $split) {
            $splitDecrypt = '';
            openssl_private_decrypt($split, $splitDecrypt, $privateKeyResource); // 解密
            $decrypt .= $splitDecrypt;
        }

        return $decrypt;
    }

    /**
     * (使用私钥)生成签名
     * 备注:相同的数据每次生成的签名都是相同的。
     *
     * @param string $data 要生成签名的数据
     * @return string 签名字符串
     */
    public static function signature($data)
    {
        // 获取私钥的内容(即字符串)
        $privateKeyContents = file_get_contents(self::$privateKeyPath);

        // 获取私钥的资源(获取失败返回FALSE)
        $privateKeyResource = openssl_pkey_get_private($privateKeyContents);

        $signature = '';

        openssl_sign($data, $signature, $privateKeyResource, self::$signatureAlgo);

        openssl_free_key($privateKeyResource);

        return base64_encode($signature);
    }

    /**
     * (使用公钥)验证签名
     *
     * @param string $data 数据(必须要和生成签名时的数据一样。PS;签名就是防止伪造数据)
     * @param string $signature 签名字符串
     * @return int 验证结果:1=签名正确,0=签名错误,-1=内部发生错误
     */
    public static function signatureVerify($data, $signature)
    {
        // 获取公钥的内容(即字符串)
        $publicKeyContents = file_get_contents(self::$publicKeyPath);

        // 获取公钥的资源(获取失败返回FALSE)
        $publicKeyResource = openssl_pkey_get_public($publicKeyContents);

        $signature = base64_decode($signature);

        $verify = openssl_verify($data, $signature, $publicKeyResource, self::$signatureAlgo);

        openssl_free_key($publicKeyResource);

        return $verify;
    }
}

$data = 'hello, world';
echo "数据:{$data}" . PHP_EOL; // 数据:hello, world
// 备注:由于这里只是简单演示如何使用RSA加解密,故原始数据简单粗暴地使用一串字符串,实际使用中原始数据都是数组格式,开发者需
//       要按照规则将数组转成字符串,通常规则都是将数组进行参数名ASCII字典序排序,然后转成“参数1=参数值1&参数2=参数值2&参数
//       3=参数值3”格式的字符串,然后将字符串进行base64_encode()处理,最后将base64_encode()得出的字符串加密即可,代码如下:
//       ksort($data);
//       $dataStr = urldecode(http_build_query($data));
//       $dataEncode = base64_encode($dataStr);
//       $encrypt = RSA::encrypt($dataEncode);

// 对原始数据进行加密(备注:相同的数据每次加密产生的密文都是不同的。)
$encrypt = RSA::encrypt($data);
echo "密文:{$encrypt}" . PHP_EOL; // 密文:A+V00pX0e0kzstPtY+dtgQqJPOx88LUfAdlH66......

// 对密文进行解密
$decrypt = RSA::decrypt($encrypt);
echo "明文:{$decrypt}" . PHP_EOL; // 明文:hello, world

// 生成签名(备注:相同的数据每次生成的签名都是相同的。)
$signature = RSA::signature($data);
echo "签名:{$signature}" . PHP_EOL; // 签名:SGYEPOmPVKwBNsbvYwMFbuL0Ybn3G5ipw4wW......

// 验证签名,验证结果:1=签名正确,0=签名错误,-1=内部发生错误
$verify = RSA::signatureVerify($data, $signature);
echo "验签:{$verify}" . PHP_EOL; // 验签:1

Copyright © 2023 码农人生. All Rights Reserved