【PHP】RSA 类 和 使用

小破孩
2022-06-23 / 1 评论 / 429 阅读 / 正在检测是否收录...
最新优化的版本,不受长度限制
<?php

namespace app\common\lib\pay\tppay;

class Rsa
{
    private $privateKey;
    private $publicKey;

    public function __construct(?string $privateKeyPath = null, ?string $publicKeyPath = null, ?string $privateKeyContent = null, ?string $publicKeyContent = null)
    {
        if ($privateKeyContent !== null) {
            $this->privateKey = openssl_pkey_get_private($privateKeyContent);
        } else {
            $this->privateKey = openssl_pkey_get_private(file_get_contents($privateKeyPath ?? __DIR__ . '/rsa_private_key.pem'));
        }

        if ($publicKeyContent !== null) {
            $this->publicKey = openssl_pkey_get_public($publicKeyContent);
        } else {
            $this->publicKey = openssl_pkey_get_public(file_get_contents($publicKeyPath ?? __DIR__ . '/rsa_public_key.pem'));
        }
    }


    /**
     * 生成数据的SHA1withRSA签名
     * @param string $data 需要签名的数据
     * @return string 返回base64编码的签名
     */
    public function sign(string $data): string
    {
        if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA1)) {
            throw new \RuntimeException('签名失败');
        }
        return base64_encode($signature);
    }

    /**
     * 验证数据的SHA1withRSA签名
     * @param string $data 原始数据
     * @param string $signature 要验证的base64编码的签名
     * @return bool 返回签名是否有效
     */
    public function verify(string $data, string $signature): bool
    {
        $signature = base64_decode($signature);
        $result = openssl_verify($data, $signature, $this->publicKey, OPENSSL_ALGO_SHA1);
        if ($result === -1) {
            throw new \RuntimeException('验证签名失败: ' . openssl_error_string());
        }
        return $result === 1;
    }

    private function getBlockSize($type = 'private')
    {
        $keyDetail = openssl_pkey_get_details($type === 'private' ? $this->privateKey : $this->publicKey);
        $modulusSize = strlen($keyDetail['rsa']['n']) * 8; // 获取模数的位数
        return floor(($modulusSize / 8) - 11); // RSA PKCS#1 v1.5 padding
    }

    public function privEncrypt(string $data)
    {
        $blockSize = $this->getBlockSize('private');
        $encryptedData = '';
        while ($data) {
            $input = substr($data, 0, $blockSize);
            $data = substr($data, $blockSize);
            if (!openssl_private_encrypt($input, $encryptedPart, $this->privateKey)) {
                throw new \RuntimeException('私钥加密失败');
            }
            $encryptedData .= $encryptedPart;
        }
        return base64_encode($encryptedData);
    }

    public function publicEncrypt(string $data)
    {
        $blockSize = $this->getBlockSize('public');
        $encryptedData = '';
        while ($data) {
            $input = substr($data, 0, $blockSize);
            $data = substr($data, $blockSize);
            if (!openssl_public_encrypt($input, $encryptedPart, $this->publicKey)) {
                throw new \RuntimeException('公钥加密失败');
            }
            $encryptedData .= $encryptedPart;
        }
        return base64_encode($encryptedData);
    }

    public function privDecrypt(string $encrypted)
    {
        $blockSize = $this->getBlockSize('private');
        $decryptedData = '';
        $encrypted = base64_decode($encrypted);
        while ($encrypted) {
            $input = substr($encrypted, 0, $blockSize + 11); // 加上padding长度
            $encrypted = substr($encrypted, $blockSize + 11);
            if (!openssl_private_decrypt($input, $decryptedPart, $this->privateKey)) {
                throw new \RuntimeException('私钥解密失败');
            }
            $decryptedData .= $decryptedPart;
        }
        return $decryptedData;
    }

    public function publicDecrypt(string $encrypted)
    {
        $blockSize = $this->getBlockSize('public');
        $decryptedData = '';
        $encrypted = base64_decode($encrypted);
        while ($encrypted) {
            $input = substr($encrypted, 0, $blockSize + 11);
            $encrypted = substr($encrypted, $blockSize + 11);
            if (!openssl_public_decrypt($input, $decryptedPart, $this->publicKey)) {
                throw new \RuntimeException('公钥解密失败');
            }
            $decryptedData .= $decryptedPart;
        }
        return $decryptedData;
    }

    public function __destruct()
    {
        if ($this->privateKey !== null) {
            openssl_free_key($this->privateKey);
        }
        if ($this->publicKey !== null) {
            openssl_free_key($this->publicKey);
        }
    }

    /**
     * 创建RSA密钥对
     * @param int $bits 密钥位数,默认为2048位
     * @param bool $saveToFile 是否将密钥保存到文件,默认为true
     * @param string|null $privateKeyPath 私钥保存路径,如果$saveToFile为false,则忽略此参数
     * @param string|null $publicKeyPath 公钥保存路径,如果$saveToFile为false,则忽略此参数
     * @return array 返回包含私钥和公钥的数组
     */
    public static function createKeyPair($bits = 2048, $saveToFile = true, $privateKeyPath = null, $publicKeyPath = null)
    {
        // 创建一个新的私钥和公钥资源
        $res = openssl_pkey_new([
            "private_key_bits" => $bits,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ]);

        if ($res === false) {
            throw new \RuntimeException('无法创建RSA密钥对');
        }

        // 提取私钥和公钥
        openssl_pkey_export($res, $privateKey);
        $publicKey = openssl_pkey_get_details($res)['key'];

        // 如果需要保存到文件
        if ($saveToFile) {
            // 确保目录存在
            if ($privateKeyPath !== null && !is_dir(dirname($privateKeyPath))) {
                mkdir(dirname($privateKeyPath), 0755, true);
            }
            if ($publicKeyPath !== null && !is_dir(dirname($publicKeyPath))) {
                mkdir(dirname($publicKeyPath), 0755, true);
            }

            // 保存私钥和公钥到文件
            file_put_contents($privateKeyPath ?? __DIR__ . '/rsa_private_key.pem', $privateKey);
            file_put_contents($publicKeyPath ?? __DIR__ . '/rsa_public_key.pem', $publicKey);
        }

        return [
            'privateKey' => $privateKey,
            'publicKey' => $publicKey,
        ];
    }
}

// $rsa = new Rsa('/path/to/private_key.pem', '/path/to/public_key.pem');
// $rsa = new Rsa(null, null, '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----');

//直接获取密钥对(不保存)
//$keyPair = Rsa::createKeyPair(2048, false);
//echo "Private Key:\n" . $keyPair['privateKey'];
//echo "Public Key:\n" . $keyPair['publicKey'];

//将密钥对保存到指定位置
//$keyPair = Rsa::createKeyPair(2048, true, '/path/to/rsa_private_key.pem', '/path/to/rsa_public_key.pem');




下载开源RSA密钥生成工具openssl(通常Linux系统都自带该程序),解压缩至独立的文件夹,进入其中的bin目录,执行以下命令:
    a、openssl genrsa -out rsa_private_key.pem 1024
    b、openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem
    c、openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
第一条命令生成原始 RSA私钥文件 rsa_private_key.pem
第二条命令将原始 RSA私钥转换为 pkcs8格式
第三条生成RSA公钥 rsa_public_key.pem

上面几个就可以看出:通过私钥能生成对应的公钥
RSA类:
    <?php
    class Rsa {
    /**     
     * 获取私钥     
     * @return bool|resource     
     */    
    private static function getPrivateKey() 
    {        
        $abs_path = dirname(__FILE__) . '/rsa_private_key.pem';
        $content = file_get_contents($abs_path);    
        return openssl_pkey_get_private($content);    
    }    

    /**     
     * 获取公钥     
     * @return bool|resource     
     */    
    private static function getPublicKey()
    {   
        $abs_path = dirname(__FILE__) . '/rsa_public_key.pem';
        $content = file_get_contents($abs_path);    
        return openssl_pkey_get_public($content);     
    }

    /**     
     * 私钥加密     
     * @param string $data     
     * @return null|string     
     */    
    public static function privEncrypt($data = '')    
    {        
        if (!is_string($data)) {            
            return null;       
        }        
        return openssl_private_encrypt($data,$encrypted,self::getPrivateKey()) ? base64_encode($encrypted) : null;    
    }    

    /**     
     * 公钥加密     
     * @param string $data     
     * @return null|string     
     */    
    public static function publicEncrypt($data = '')   
    {        
        if (!is_string($data)) {            
            return null;        
        }        
        return openssl_public_encrypt($data,$encrypted,self::getPublicKey()) ? base64_encode($encrypted) : null;    
    }    

    /**     
     * 私钥解密     
     * @param string $encrypted     
     * @return null     
     */    
    public static function privDecrypt($encrypted = '')    
    {        
        if (!is_string($encrypted)) {            
            return null;        
        }        
        return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, self::getPrivateKey())) ? $decrypted : null;    
    }    

    /**     
     * 公钥解密     
     * @param string $encrypted     
     * @return null     
     */    
    public static function publicDecrypt($encrypted = '')    
    {        
        if (!is_string($encrypted)) {            
            return null;        
        }        
    return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, self::getPublicKey())) ? $decrypted : null;    
    }

    }

**demo:**

    <?php
    
    require_once "Rsa.php";
    $rsa = new Rsa();
    $data['name'] = 'Tom';
    $data['age']  = '20';
    $privEncrypt = $rsa->privEncrypt(json_encode($data));
    echo '私钥加密后:'.$privEncrypt.'<br>';
    
    $publicDecrypt = $rsa->publicDecrypt($privEncrypt);
    echo '公钥解密后:'.$publicDecrypt.'<br>';
    
    $publicEncrypt = $rsa->publicEncrypt(json_encode($data));
    echo '公钥加密后:'.$publicEncrypt.'<br>';
    
    $privDecrypt = $rsa->privDecrypt($publicEncrypt);
    echo '私钥解密后:'.$privDecrypt.'<br>';
也有一些网站提供生成rsa公钥私钥的服务:http://www.bm8.com.cn/webtool/rsa/

文章参考:https://www.cnblogs.com/xuweiqiang/p/9784584.html

0

评论

博主关闭了所有页面的评论