PHP 实现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)。

加密(公钥加密,私钥解密)
<?php
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_public( file_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();
        }
    }
}
签名(私钥签名,公钥验签)
<?php
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的区别

签名算法标准签名算法描述
RSA2SHA256WithRSA强制要求 RSA 密钥的长度至少为 2048。
RSASHA1WithRSA对 RSA 密钥的长度不限制,推荐使用 2048 位以上。
结论

以上就是使用 RSA 进行加解密以及签名验签的全部实现了,并不是很复杂,代码稍作修改即可应用在你自己的业务中了。

另外建议在使用 RSA 做签名验证的时候建议使用 RSA2 的方式,相对而言 RSA2 的安全能力是高于 RSA 的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吹落的树叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值