PHP利用OpenSSL实现RSA密钥读写、加解密与签名验签完整指南
本文详解在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 是非对称加密算法,遵循以下核心规则:
- 公钥加密,私钥解密:用于数据传输保密性。发送方用接收方的公钥加密,只有持有对应私钥的接收方才能解开。
- 私钥加密,公钥解密:常用于数字签名。发送方用自己的私钥加密摘要,接收方用发送方的公钥验证身份。
基本代码如下,演示这两种模式:
私钥解密 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加密解密技术的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













