核心要点
- 非对称加密(如 RSA)通过使用公钥加密、私钥解密解决密钥分发问题,公钥可安全传输,只有私钥持有者能解密消息
- RSA 加密的局限在于只能处理有限长度的明文(例如 2048 位密钥最多加密 256 字节)
- 安全加密大型消息的解决方案:使用对称加密消息,再用接收方公钥加密对称密钥并附加到消息中
- PHP 安全通信库 (phpseclib) 提供纯 PHP 实现的多种加密算法,可在缺乏快速加密函数的环境中可靠工作
引言
大多数人都理解传输敏感数据前需要加密。加密是将明文(普通数据)转化为密文(加密数据)的过程。该过程通过密钥和算法实现。读取数据时,密文必须使用密钥和算法解密恢复为明文。
加密算法是对密钥数值和明文字符串数值进行数学运算的过程,运算结果即为密文。密钥越大,密文越安全。
核心问题:任何加密算法都需解决密钥分发问题——如何安全地将密钥传输给需要建立安全通信的接收方?
解决方案取决于密钥和算法的性质。
加密算法与密钥
加密算法有两种基本类型:
- 对称算法:加密和解密使用相同密钥
- 非对称算法:加密和解密使用不同密钥
选择对称算法时,收发双方必须拥有相同密钥。若双方位于不同位置,密钥传输过程本身存在安全风险。
选择非对称算法时,存在两个密钥:公钥和私钥。公钥加密的数据只能用对应私钥解密。接收方先将公钥发送给发送方,发送方用该公钥加密消息后传输,接收方再用私钥解密。
非对称算法解决了密钥分发问题,接收方可自由分发公钥,因为只有私钥持有者能解密消息。
非对称算法解决了密钥分发问题。
强加密算法基于高级数学和数论。通常,对于给定密钥长度,若攻击者无密钥时解密耗时不可行,则该算法被视为"强加密"算法1。
由于加密算法复杂性,非数学专业人士选择算法可能困惑。因此许多商业和政府机构依赖美国国家标准与技术研究院(NIST)的加密建议。
RSA 密钥的问题
虽然非对称算法解决密钥分发问题,但存在另一问题:RSA 算法特性决定其只能加密有限长度的明文。例如 2048 位密钥最多只能加密 256 字节明文数据4。
问题总结:
- 非对称密钥易于安全交换,但消息大小受限
- 对称密钥支持无限消息大小,但难以安全交换
解决方案
解决方案是:用对称密钥加密消息,再用非对称方式加密该密钥并附加到消息中。
接收方收到消息后,提取加密的对称密钥,用私钥非对称解密,再用对称密钥解密消息。
加密步骤
- 接收方生成密钥对,将公钥发送给发送方
- 发送方生成随机对称密钥加密大消息
- 用对称密钥加密消息
- 用接收方公钥加密对称密钥
- 拼接加密后的对称密钥和加密消息
- 将加密消息发送给接收方
解密步骤
接收方获取消息后反向操作:
- 从消息中提取加密的对称密钥
- 用私钥解密对称密钥
- 用对称密钥解密消息
PHP 实现方案
我们将使用 PHP 安全通信库 (phpseclib) 演示该技术。这个开源库提供纯 PHP 实现的任意精度整数、RSA、DES、3DES、RC4、Rijndael、AES、SSH-1、SSH-2 和 SFTP 等功能。
该库优先使用快速加密函数(如 open_ssl 和 mcrypt)提升性能,但在共享主机等环境不可用时仍能可靠运行(速度较慢)。
可通过 Composer 安装,文档参见 phpseclib 文档。
示例代码
生成密钥对
$rsa = new Crypt_RSA();
$keys = $rsa->createKey(2048);
file_put_contents('key.pri',$keys['privatekey']);
file_put_contents('key.pub',$keys['publickey']);
加密函数
function encrypt_message($plaintext,$asym_key,$key_length=150)
{
$rsa = new Crypt_RSA();
$rij = new Crypt_Rijndael();
// 生成随机对称密钥
$sym_key = crypt_random_string($key_length);
// 用对称密钥加密消息
$rij->setKey($sym_key);
$ciphertext = $rij->encrypt($plaintext);
$ciphertext = base64_encode($ciphertext);
// 用非对称密钥加密对称密钥
$rsa->loadKey($asym_key);
$sym_key = $rsa->encrypt($sym_key);
// Base64编码对称密钥便于传输
$sym_key = base64_encode($sym_key);
$len = strlen($sym_key); // 获取长度
$len = dechex($len); // 消息前3字节存储密钥长度
$len = str_pad($len,3,'0',STR_PAD_LEFT); // 零填充确保长度
// 拼接长度、加密的对称密钥和消息
$message = $len.$sym_key.$ciphertext;
return $message;
}
解密函数
function decrypt_message($message,$asym_key)
{
$rsa = new Crypt_RSA();
$rij = new Crypt_Rijndael();
// 提取对称密钥
$len = substr($message,0,3);
$len = hexdec($len);
$sym_key = substr($message,0,$len);
// 提取加密消息
$message = substr($message,3);
$ciphertext = substr($message,$len);
$ciphertext = base64_decode($ciphertext);
// 解密加密的对称密钥
$rsa->loadKey($asym_key);
$sym_key = base64_decode($sym_key);
$sym_key = $rsa->decrypt($sym_key);
// 解密消息
$rij->setKey($sym_key);
$plaintext = $rij->decrypt($ciphertext);
return $plaintext;
}
结论
随着程序和网络服务互联性增强,远程进程间的安全数据传输应成为常态。非对称密钥可安全传输更受青睐,但消息大小受限。混合使用对称和非对称算法可解决这两大局限。
PHP 安全通信库提供便捷的纯 PHP 实现方案,支持行业标准对称和非对称算法,使我们能在 PHP 中创建混合解决方案。
本文源代码位于 GitHub:https://github.com/sitepoint-editors/sitepointcrypto
常见问题解答
对称加密和非对称加密的主要区别?
- 对称加密:使用相同密钥加密解密,需共享密钥存在安全风险
- 非对称加密:使用公钥加密、私钥解密,公钥可自由分发,私钥保密
phpseclib 如何处理大消息?
使用 “RSA with OAEP padding” 技术:将大消息分块加密后组合,支持任意大小消息
phpseclib 支持哪些算法?
支持 RSA、DES、AES 等对称/非对称算法,以及 SSH、X.509 证书等加密操作
如何选择非对称密钥长度?
2048 位密钥通常平衡安全性与性能,安全要求更高可选更长密钥
如何保证私钥安全?
私钥需存储于安全位置,使用强密码保护并严格限制访问权限
技术参考:
[1] 密钥长度 - 维基百科
[2] RSA vs DSA 对比
[3] NIST 加密标准演进
[4] RSA 明文长度限制原理