PHP 实现RSA,RSA2 加密和签名
时间:2022-11-18 01:00:00
目录
-
-
-
- 前情提要
- 生成 RSA 私钥和公钥
- 加密(公钥加密,私钥解密)
- 签名(私钥签名,公钥验证)
- 使用 RSA2 签名及验签说明
- 结论
-
-
前情提要
在网站或应用程序的业务开发中,经常使用一些加密逻辑或与第三方对接 API 在当前复杂的加密/签名算法中,我相信签名逻辑 RSA 算法相对适用。
这里还有一个问题,很多人分不清楚 RSA 哪个公钥和私钥用于加密,哪个用于解密;或者哪个用于签名,哪个用于验证。事实上,这个问题也很容易理解。
如果是用来加密的,那么作为开发者,我肯定不希望别人知道我的消息,所以也就是说只有我才能解密,所以我可以得公钥负责加密,私钥负责解密
如果是用来签名的,作为开发者,我当然不希望有人冒充我的消息。我只能生成这个签名,我可以得到它私钥负责签名,公钥负责验证
那么,有了上述结论,我们将实现这种加密方法。
生成 RSA 私钥和公钥
RSA是非对称加密,对加密内容长度有限制,生成 1024 私钥最多只能加密 127 如果加密字符串太长,请生成位数据 2048 位的秘钥
# 目前默认生成的是2048 bit的秘钥 openssl genrsa -out private_key.pem 2048 # 如果是Java开发人员需要将私钥转换为PKCS8格式 openssl pkcs8 -topk8 -inform PEM -in private_key.pem -outform PEM -nocrypt -out private_key.pem # 生成相应的公钥 openssl rsa -in private_key.pem -pubout -out public_key.pem
通过上述操作命令,将生成的公钥和秘钥复制到自己的项目录中,可以继续进行以下开发工作(使用 Laravel8)。
加密(公钥加密,私钥解密)
namespace App\Http\Controllers; use Illuminate\Support\Str; class RsaController extends Controller {
// 这里应直接从配置文件中获取,方便大家查阅直接定义, // 公钥和私钥可以直接复制并以字符串的形式配置,也可以配置存储公钥密钥文件的目录路径 private $private_key = 'MIIEowIBAAKCAQEAyZGgkPRWyeGIlY'; private $public_key = storage_path('public_key.pem'); // 公钥加密 public function encrypt() {
// 待加密字符串 $str = 123456789; ///验证公钥 组装公钥可以阅读文件或按64位长度组装字符串 if (Str::endsWith($this->public_key, '.pem')) {
$public_key = openssl_pkey_get_publicfile_get_contents($this->public_key) );;
} else {
$public_key = "-----BEGIN PUBLIC KEY-----\n".
wordwrap($this->public_key, 64, "\n", true).
"\n-----END PUBLIC KEY-----";
}
// 加密
try {
openssl_public_encrypt($str,$encrypted, $public_key);
// base64_encode转码后的内容通常含有特殊字符,在浏览器通过url传输时要注意base64编码是否是url安全的,所以进行url转码
$encrypted = urlencode(base64_encode($encrypted));
!is_resource($public_key) ?: openssl_free_key($public_key);
return $encrypted;
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
// 私钥解密
public function decrypt()
{
// 公钥加密后的字符串
$str = 'lj73ktX7FJWb534rbiE...';
// 验证私钥 拼装私钥
if (Str::endsWith($this->private_key, '.pem')) {
$private_key = openssl_pkey_get_private($this->private_key);
} else {
$private_key = "-----BEGIN RSA PRIVATE KEY-----\n".
wordwrap($this->private_key, 64, "\n", true).
"\n-----END RSA PRIVATE KEY-----";
}
// 解密
try {
openssl_private_decrypt(base64_decode(urldecode($str)), $decrypted, $private_key);
!is_resource($private_key) ?: openssl_free_key($private_key);
return $decrypted;
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
}
签名(私钥签名,公钥验签)
namespace App\Http\Controllers;
use Illuminate\Support\Str;
class RsaController extends Controller
{
// 这里应直接从配置文件获取,为方便大家查阅直接定义在这里,
// 公钥和私钥可以直接复制出来以字符串形式配置,也可以配置存放公钥秘钥文件的目录路径
private $private_key = 'MIIEowIBAAKCAQEAyZGgkPRWyeGIlY';
private $public_key = storage_path('public_key.pem');
// 私钥签名
public function genSign()
{
// 待生成签名的字符串
$str = 'a=1&b=2&c=3&d=5';
// 验证私钥 拼装私钥
if (Str::endsWith($this->private_key, '.pem')) {
$private_key = openssl_pkey_get_private($this->private_key);
} else {
$private_key = "-----BEGIN RSA PRIVATE KEY-----\n".
wordwrap($this->private_key, 64, "\n", true).
"\n-----END RSA PRIVATE KEY-----";
}
try {
openssl_sign($str, $signature, $private_key);
$sign = base64_encode($signature);
!is_resource($private_key) ?: openssl_free_key($private_key);
return $sign;
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
// 公钥验签
public function verifySign()
{
// 获取到参与签名的字符串
$str = 'a=1&b=2&c=3&d=5';
// 私钥生成的签名
$sign = 'ZSMivQqMFZ1s36NFE9kcB83BcltwII...';
// 验证公钥 拼装公钥
if (Str::endsWith($this->public_key, '.pem')) {
$public_key = openssl_pkey_get_public($this->public_key);
} else {
$public_key = "-----BEGIN PUBLIC KEY-----\n".
wordwrap($this->public_key, 64, "\n", true).
"\n-----END PUBLIC KEY-----";
}
// 验签
try {
// 如果签名正确返回 1, 签名错误返回 0, 内部发生错误则返回-1
$result = openssl_verify($str, base64_decode($sign), $public_key );
!is_resource($public_key) ?: openssl_free_key($public_key);
return $result === 1
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
}
使用 RSA2 的签名和验签说明
RSA 默认签名方式为 OPENSSL_ALGO_SHA1 如果使用RSA2的话需要在签名和验签方法中增加参数 OPENSSL_ALGO_SHA256 ,示例如下:
// 签名
openssl_sign($str, $signature, $private_key, OPENSSL_ALGO_SHA256);
// 验签
openssl_verify($str, base64_decode($sign), $public_key, OPENSSL_ALGO_SHA256);
RSA 和 RSA2的区别
签名算法 | 标准签名算法 | 描述 |
---|---|---|
RSA2 | SHA256WithRSA | 强制要求 RSA 密钥的长度至少为 2048。 |
RSA | SHA1WithRSA | 对 RSA 密钥的长度不限制,推荐使用 2048 位以上。 |
结论
以上就是使用 RSA 进行加解密以及签名验签的全部实现了,并不是很复杂,代码稍作修改即可应用在你自己的业务中了。
另外建议在使用 RSA 做签名验证的时候建议使用 RSA2 的方式,相对而言 RSA2 的安全能力是高于 RSA 的。