核心要点

  • 非对称加密(如 RSA)通过使用公钥加密、私钥解密解决密钥分发问题,公钥可安全传输,只有私钥持有者能解密消息
  • RSA 加密的局限在于只能处理有限长度的明文(例如 2048 位密钥最多加密 256 字节)
  • 安全加密大型消息的解决方案:使用对称加密消息,再用接收方公钥加密对称密钥并附加到消息中
  • PHP 安全通信库 (phpseclib) 提供纯 PHP 实现的多种加密算法,可在缺乏快速加密函数的环境中可靠工作

引言

大多数人都理解传输敏感数据前需要加密。加密是将明文(普通数据)转化为密文(加密数据)的过程。该过程通过密钥算法实现。读取数据时,密文必须使用密钥和算法解密恢复为明文。

加密算法是对密钥数值和明文字符串数值进行数学运算的过程,运算结果即为密文。密钥越大,密文越安全。

核心问题:任何加密算法都需解决密钥分发问题——如何安全地将密钥传输给需要建立安全通信的接收方?

解决方案取决于密钥和算法的性质。

加密算法与密钥

加密算法有两种基本类型:

  1. 对称算法:加密和解密使用相同密钥
  2. 非对称算法:加密和解密使用不同密钥

选择对称算法时,收发双方必须拥有相同密钥。若双方位于不同位置,密钥传输过程本身存在安全风险。

选择非对称算法时,存在两个密钥:公钥和私钥。公钥加密的数据只能用对应私钥解密。接收方先将公钥发送给发送方,发送方用该公钥加密消息后传输,接收方再用私钥解密。

非对称算法解决了密钥分发问题,接收方可自由分发公钥,因为只有私钥持有者能解密消息。

非对称算法解决了密钥分发问题。

强加密算法基于高级数学和数论。通常,对于给定密钥长度,若攻击者无密钥时解密耗时不可行,则该算法被视为"强加密"算法1

由于加密算法复杂性,非数学专业人士选择算法可能困惑。因此许多商业和政府机构依赖美国国家标准与技术研究院(NIST)的加密建议。

RSA 密钥的问题

虽然非对称算法解决密钥分发问题,但存在另一问题:RSA 算法特性决定其只能加密有限长度的明文。例如 2048 位密钥最多只能加密 256 字节明文数据4

问题总结:

  1. 非对称密钥易于安全交换,但消息大小受限
  2. 对称密钥支持无限消息大小,但难以安全交换

解决方案

解决方案是:用对称密钥加密消息,再用非对称方式加密该密钥并附加到消息中。

接收方收到消息后,提取加密的对称密钥,用私钥非对称解密,再用对称密钥解密消息。

加密步骤

  1. 接收方生成密钥对,将公钥发送给发送方
  2. 发送方生成随机对称密钥加密大消息
  3. 用对称密钥加密消息
  4. 用接收方公钥加密对称密钥
  5. 拼接加密后的对称密钥和加密消息
  6. 将加密消息发送给接收方

解密步骤

接收方获取消息后反向操作:

  1. 从消息中提取加密的对称密钥
  2. 用私钥解密对称密钥
  3. 用对称密钥解密消息

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 明文长度限制原理