PHP利用OpenSSL实现RSA密钥读写、加解密与签名验签完整指南

Elena
Elena
发布时间:2026-10-13| 5人浏览过
原创

本文详解在PHP中如何使用OpenSSL扩展进行RSA非对称加密。涵盖从生成密钥开始,到使用OpenSSL函数实现多种格式的密钥资源获取,再到数据加解密和数字签名的完整实现流程。

第一步:理解密钥格式与安全要求

RSA加密的安全性很大程度上取决于密钥长度。一般推荐使用 2048位 或更长的密钥,以平衡安全性与计算性能。在PHP开发中,最常见的密钥存储格式是 PEM(Privacy-Enhanced Mail),即以 -----BEGIN PRIVATE KEY----- 开头并以 -----END PRIVATE KEY----- 结尾的Base64编码文本。

虽然也存在PKCS#12 (.p12/.pfx) 等包含证书链的格式,但在Web应用接口通信中,通常直接使用 PEM 格式的明文密钥串更为灵活和常见。

第二步:三种常见的密钥读取方式

在使用 PHP 的 OpenSSL 函数前,需要将密钥字符串转换为 OpenSSL 内部的密钥资源句柄(Resource)。根据实际业务场景,主要有以下三种输入形式:

第1步:处理单行 Base64 字符串

如果通过 API 或配置文件获取的是没有换行的长 Base64 字符串(如 MwwDQYJ...),需要手动拼装成标准的 PEM 格式,再进行解析。

$publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKZ1mKTymRoGKnHiP1xAy4aiyt5r0BscCZnDAonCrMFZ4kBGriPNHxEaLr5lfBnMKw7k6i+2dsFPSEZooTvqtPUCAwEAAQ==';
$privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApnWYpPKZGgYqceI/XEDLhqLK3mvQGxwJmcMCicKswVniQEauI80fERouvmV8GcwrDuTqL7Z2wU9IRmihO+q09QIDAQABAkBunx3nGHXYjppsfn++7iyTd+I7+Agfy/0xWyB3rpEiGGgfemjcRFaeq5SC2vUNXsrEOY5gbUSQmFxH//Cym18NAiEA1z1cZx/Q9cbIjFPwp1a+K5CVFDXDcfbi/AQgAkVs0/cCIQDF+2fr23AoBslcOC4S0yAx94AbgxCntYuRqztxybsrcwIgMW86ZcT87TX2oaQ1xXk6vC68zqN6fBZEE7Wu1Fa1pAkCIElmOJP3qfAc/AAlj+dIwLHlqWgJwl3674CU9Bfui2bDAiEA0CKJpF8x7KANCcopEQC93PsbIztuML322LOfDV1Lw/k=';

// 转换逻辑
$pemPriv = "-----BEGIN PRIVATE KEY-----\n" . chunk_split($privateKey, 64, "\n") . "-----END PRIVATE KEY-----\n";
$pemPub  = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($publicKey, 64, "\n") . "-----END PUBLIC KEY-----\n";

第2步:直接复制标准 PEM 字符串

若开发者直接从生成工具复制了带头和尾的完整 Key 字符串,可直接调用 openssl_pkey_get_public 和 openssl_pkey_get_private 进行转换。

$publicKeyString = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1ffL+kUEedBLTzQrKU+
...
-----END PUBLIC KEY-----";
 
$privateKeyString = "-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiGQABAEEAAKBgQCnV98v6RQR50Et...
-----END PRIVATE KEY-----";
 
$publicKeyResource = openssl_pkey_get_public($publicKeyString);
$privateKeyResource = openssl_pkey_get_private($privateKeyString);

注意: 此方法需确保字符串中包含正确的头尾标识符,且内容未发生截断。

第3步:从 .pem 本地文件加载

在生产环境中,将密钥文件存储在服务器指定目录是最安全的方式之一。使用 file_get_contents 读取后再转换。

$publicKeyString = file_get_contents('public.pem');
$privateKeyString = file_get_contents('private.pem');
 
$publicKeyResource = openssl_pkey_get_public($publicKeyString);
$privateKeyResource = openssl_pkey_get_private($privateKeyString);

第三步:执行 RSA 加解密操作

RSA 是非对称加密算法,遵循以下核心规则:

  1. 公钥加密,私钥解密:用于数据传输保密性。发送方用接收方的公钥加密,只有持有对应私钥的接收方才能解开。
  2. 私钥加密,公钥解密:常用于数字签名。发送方用自己的私钥加密摘要,接收方用发送方的公钥验证身份。

基本代码如下,演示这两种模式:

 私钥解密
openssl_public_encrypt($data, $encryptedWithPublicKey, $publicKeyResource);
openssl_private_decrypt($encryptedWithPublicKey, $decryptedWithPrivateKey, $privateKeyResource);

// 2. 私钥加密 -> 公钥解密
openssl_private_encrypt($data, $encryptedWithPrivateKey, $privateKeyResource);
openssl_public_decrypt($encryptedWithPrivateKey, $decryptedWithPublicKey, $publicKeyResource);

echo "原始数据: " . $data . "\n";
echo "公钥加密私钥解密结果: " . $decryptedWithPrivateKey . "\n";
echo "私钥加密公钥解密结果: " . $decryptedWithPublicKey . "\n";
?>

第四步:实现签名与验签

为了保证数据的完整性及不可抵赖性,通常对数据的 Hash 值进行签名。在 PHP 中可通过 openssl_sign 和 openssl_verify 实现。

    /**
     * 构造签名
     * @param string $dataString 被签名数据
     * @return string base64后的签名
     */
    public function sign($dataString)
    {
        $signature = false;
        // 使用私钥签名
        openssl_sign($dataString, $signature, $privateKeyResource);
        return base64_encode($signature);
    }
 
    /**
     * 验证签名
     * @param string $dataString 原数据
     * @param string $signString base64后的签名串
     * @return int 1正确 0错误 -1失败
     */
    public function verify($dataString, $signString)
    {
        $signature = base64_decode($signString);
        // 使用公钥验签
        $flg = openssl_verify($dataString, $signature, $publicKeyResource);
        return $flg;
     }

第五步:完整 RSA 类封装示例

为了方便在项目中使用,建议将上述功能封装为 Class。该类支持三种构造密钥的方式(单行串、完整Key串、文件路径)。

setKey($publicKey, $privateKey, $type);
    }

    public function setKey($publicKey = null, $privateKey = null, $type=1)
    {
        if (!is_null($publicKey)) {
            $this->setupPubKey($publicKey, $type);
        }
        if (!is_null($privateKey)) {
            $this->setupPrivKey($privateKey, $type);
        }
    }

    private function setupPrivKey($privateKey, $type)
    {
        if (is_resource($this->privateKey)) {
            return true;
        }

        if ($type == 1) {
            // 无换行的字符串转PEM
            $pem = "-----BEGIN PRIVATE KEY-----\n" . chunk_split($privateKey, 64, "\n") . "-----END PRIVATE KEY-----\n";
            $this->privateKey = openssl_pkey_get_private($pem);
        } elseif ($type == 2) {
            // 完整PEM字符串
            $this->privateKey = openssl_pkey_get_private($privateKey);
        } elseif ($type == 3) {
            // 文件路径
            $privateKeyString = file_get_contents($privateKey);
            $this->privateKey= openssl_pkey_get_private($privateKeyString);
        }
        return true;
    }

    private function setupPubKey($publicKey, $type=1)
    {
        if (is_resource($this->publicKey)) {
            return true;
        }
        if ($type == 1) {
            // 无换行的字符串转PEM
            $pem = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($publicKey, 64, "\n") . "-----END PUBLIC KEY-----\n";
            $this->publicKey = openssl_pkey_get_public($pem);
        } elseif ($type == 2) {
            $this->publicKey= openssl_pkey_get_public($publicKey);
        } elseif ($type == 3) {
            $pubString = file_get_contents($publicKey);
            $this->publicKey= openssl_pkey_get_public($pubString);
        }
        return true;
    }

    public function privEncrypt($data)
    {
        if (!is_string($data) || empty($this->privateKey)) {
            return null;
        }
        $r = openssl_private_encrypt($data, $encrypted, $this->privateKey);
        return $r === true ? base64_encode($encrypted) : null;
    }

    public function privDecrypt($encrypted)
    {
        if (!is_string($encrypted) || empty($this->privateKey)) {
            return null;
        }
        $encrypted = base64_decode($encrypted);
        $r = openssl_private_decrypt($encrypted, $decrypted, $this->privateKey);
        return $r === true ? $decrypted : null;
    }

    public function pubEncrypt($data)
    {
        if (!is_string($data) || empty($this->publicKey)) {
            return null;
        }
        $r = openssl_public_encrypt($data, $encrypted, $this->publicKey);
        return $r === true ? base64_encode($encrypted) : null;
    }

    public function pubDecrypt($crypted)
    {
        if (!is_string($crypted) || empty($this->publicKey)) {
            return null;
        }
        $crypted = base64_decode($crypted);
        $r = openssl_public_decrypt($crypted, $decrypted, $this->publicKey);
        return $r === true ? $decrypted : null;
    }

    /**
     * 构造签名
     * @param string $dataString 被签名数据
     * @return string base64签名
     */
    public function sign($dataString)
    {
        $signature = false;
        openssl_sign($dataString, $signature, $this->privateKey);
        return base64_encode($signature);
    }

    /**
     * 验证签名
     * @param string $dataString 原数据
     * @param string $signString 已经签名的字符串
     * @return number 1签名正确 0签名错误
     */
    public function verify($dataString, $signString)
    {
        $signature = base64_decode($signString);
        $flg = openssl_verify($dataString, $signature, $this->publicKey);
        return $flg;
    }

    public function __destruct()
    {
        is_resource($this->privateKey) && @openssl_free_key($this->privateKey);
        is_resource($this->publicKey) && @openssl_free_key($this->publicKey);
    }
}
/*
* 测试用例
$rsa = new RSA("public.pem", "private.pem", 3);
// $rsa = new RSA($pubStr, $privStr, 1); // type 1
// $rsa = new RSA($pubStr, $privStr, 2); // type 2
*/
$rsa = new RSA("public.pem", "private.pem", 3);

echo "私钥加密 公钥解密:"; echo $rsa->pubDecrypt($rsa->privEncrypt("abc")); echo "
"; echo "公钥加密 私钥解密:"; echo $rsa->privDecrypt($rsa->pubEncrypt("dfg")); echo "
"; echo "签名: "; $sign=$rsa->sign("a=1&b=2"); echo $sign; $res6=$rsa->verify("a=1&b=2", $sign); echo "
验证结果: " . $res6; ?>

以上就是PHP三种方式读取RSA密钥加解密、签名验签完整教程的详细内容,更多关于PHP RSA加密解密技术的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。