PHP生成RSA密钥及加解密的实现
学习记录 留作参考
祝君好运
使用PHP在win下生成私钥有诸多问题,需谨慎使用。
RSA算法单次加密的明文长度 <= 私钥长度。以1024bit私钥长度举例,其单次最多可加密的数据 1024/8=128byte
当明文长度小于私钥长度时,就需要使用padding。PHP默认使用的是 PKCS1Padding,长度占用11字节。
若使用padding,1024bit私钥单次最多可加密数据 128-11=117byte。2048bit私钥单次最多可加密数据 2048/8-11=245byte
加密生成的密文长度等于私钥长度。
如果明文长度超过了单次最大可加密数据的长度,就需要分段加密,拼接后再输出。同时也需要分段解密,拼接后即为明文。
加密时的单次最多可加密数据的长度可由私钥长度及padding类型计算得出。而解密的分段长度则是 密钥长度/8 byte
参考内容:
知乎:RSA算法明文长度介绍
PHP官方文档
CSDN:Windows 下 PHP openssl_pkey_new 方法始终返回 false 解决方法
<?php
new_key();
function new_key()
{
/**
* 在生成公钥私钥时,需要给一些基础参数,比如加密摘要算法、私钥长度位数。
* 生成参数可参考:https://www.php.net/manual/zh/function.openssl-csr-new.php
*/
$config = [
"digest_alg" => "sha256",
"private_key_bits" => 1024, // 私钥位数 1024 2048 4096 等
];
/**
* windows环境还需要补充自定义openssl.conf文件的路径。
* PHP版本不同,配置起来也不同。
*
* 具体的openssl安装,PHP官方文档给了解答:https://www.php.net/manual/zh/openssl.installation.php
* 文档指出,从PHP7.4.0开始,openssl默认配置文件的路径已经发生改变。
* 实测 PHP7.3 PHP5.4 即使配置了openssl.conf的路径也无法生成私钥。
* 实测 PHP8.0 将 php安装路径\extras\ssl\openssl.cnf 复制到 C:\Program Files\Common Files\SSL之后可正常使用,并且不用配置config变量
*
* 总之,在win下生成私钥可能问题比较多,谨慎使用。
*/
if (PHP_OS === 'WINNT') {
$config['config'] = 'C:\phpstudy_pro\Extensions\php\php7.3.4nts\extras\ssl\openssl.cnf';
}
$res = openssl_pkey_new($config); // 生成一个新的私钥
if (false === $res) {
exit("generate key fail.");
}
// 获取私钥
openssl_pkey_export($res, $private_key);
if (!$private_key) {
exit("private key is null.");
}
// 获取公钥
$public_key = openssl_pkey_get_details($res)['key'];
var_dump($public_key);
// 待加密数据
$string = "使用PHP生成公钥和私钥分别实现加解密。传输数据时,使用公钥加密私钥解密。验证证书时,使用私钥加密公钥解密。";
$encrypt_block_size = 117; // 加密时,单次(最大)加密数据字节数
$decrypt_block_size = 128; // 解密时,单次解密数据字节数
// 加密 --START
$encrypted = '';
$primary_data_arr = str_split($string, $encrypt_block_size); // 将待加密字符串按照长度分割成数组
foreach ($primary_data_arr as $item) {
// 使用私钥逐段加密,然后拼接。加密后的数据长度为密钥长度
$bool = openssl_private_encrypt($item, $part_encrypted, $private_key, OPENSSL_PKCS1_PADDING);
if (false === $bool) {
exit('encrypt is error.');
}
$encrypted .= $part_encrypted;
}
// 加密后的内容通常含有特殊字符,需要base64编码转换下
$encrypted = base64_encode($encrypted);
echo "私钥加密后的数据:{$encrypted}\r\n";
// 加密 --END
// 解密 --START
// 因为一般待解密的数据是经过base64转码的,所以需要再base64转回去来获取原始加密数据
// 因为加密数据可能是多次加密结果拼接而成,所以也需要分段解密再拼接成原始数据
// 解密时每段数据的长度(即单次加密的密文长度)等于密钥长度。如:密钥长度1024位,单次解密128字节
$encrypted_data_arr = str_split(base64_decode($encrypted), $decrypt_block_size);
$decrypted = '';
foreach ($encrypted_data_arr as $val) {
//公钥解密
openssl_public_decrypt($val, $part_decrypted, $public_key);
$decrypted .= $part_decrypted;
}
echo "公钥解密后的数据:{$decrypted}\r\n";
// 解密 --END
//// 公钥加密,私钥解密 方法一致:加密的时候分段,解密的时候也分段
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通