请注意,openssl_seal() 无法用于 EC 加密。
我花了整整两个小时才发现这一点,因为 OpenSSL 文档太糟糕了。
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
openssl_seal — 密封(加密)数据
$data
,&$sealed_data
,&$encrypted_keys
,$public_key
,$cipher_algo
,&$iv
= null
openssl_seal() 使用指定的 cipher_algo
和随机生成的密钥来密封(加密)data
。然后使用 public_key
数组中的每个公钥对密钥进行加密,并将每个加密的信封密钥返回到 encrypted_keys
中。这允许将密封数据发送给多个接收者(前提是他们的公钥可用)。每个接收者都必须同时接收密封数据和使用接收者的公钥加密的信封密钥。生成 IV(初始化向量),其值将返回到 iv
中。
data
要密封的数据。
sealed_data
密封后的数据。
encrypted_keys
加密密钥数组。
public_key
包含公钥的 OpenSSLAsymmetricKey 实例数组。
cipher_algo
密码方法。
PHP 8.0 之前的版本的默认值为('RC4'
),这被认为是不安全的。强烈建议显式指定安全密码方法。
iv
用于解密 data
的初始化向量。如果密码方法需要 IV,则需要它。这可以通过使用 cipher_algo
调用 openssl_cipher_iv_length() 来查明。
无法显式设置 IV。其中的任何值都将被随机生成的值覆盖。
成功时返回密封数据的长度,错误时返回 false
。如果成功,则密封数据将返回到 sealed_data
中,信封密钥将返回到 encrypted_keys
中。
版本 | 描述 |
---|---|
8.0.0 |
public_key 现在接受 OpenSSLAsymmetricKey 实例的 数组;之前,接受类型为 OpenSSL key 的 资源 的 数组。 |
8.0.0 |
cipher_algo 不再是可选参数。 |
8.0.0 |
iv 现在可以为 null。 |
示例 #1 openssl_seal() 示例
<?php
// 假设 $data 包含要密封的数据
$data = "test";
// 获取公钥
$pk1 = openssl_get_publickey("file://cert1.pem");
$pk2 = openssl_get_publickey("file://cert2.pem");
// 密封消息,只有 $pk1 和 $pk2 的所有者才能使用密钥解密 $sealed
// 分别为 $ekeys[0] 和 $ekeys[1]。
if (openssl_seal($data, $sealed, $ekeys, array($pk1, $pk2), 'AES256', $iv) > 0) {
// 可能存储 $sealed 和 $iv 值并在以后在 openssl_open 中使用
echo "success\n";
}
?>
虽然默认使用 RC4,但可以使用其他更安全的算法。这些算法指定为第五个参数。此外,还需要添加初始化向量(随机字节)。例如:
<?php
$data = "This is top secret.";
// 获取接收者的公钥并准备好
$cert = file_get_contents('./cert.pem');
$pk1 = openssl_get_publickey($cert);
$iv = openssl_random_pseudo_bytes(32);
openssl_seal($data, $sealed, $ekeys, array($pk1), "AES256", $iv);
// 释放内存中的密钥
openssl_free_key($pk1);
echo base64_encode($sealed);
?>
文档中没有,其他地方也没有广泛地写到的一些关键细节。
- 信封密钥是随机生成的 128 位 RSA 密钥。
- 使用信封密钥通过 (A)RC4 加密数据。
- 信封密钥使用 PKCS1 v1.5 加密以进行传输。它不是 OAEP 填充变体。PKCS1 v1.5 甚至更旧,并且不再得到广泛支持。
至少在我们使用的 PHP 7.2 中,openssl_seal 就是这样。
(注意:在 Python 中,您可以使用 Cryptography 包和 padding.PKCS1v15() 解密此信封密钥)
在我看来,RC4 和 PKCS1 v1.5 的组合实际上使此函数对于安全用途来说有点过时了。
"使用 RC4 和随机生成的密钥来密封(加密)数据"
需要注意的是,随机生成的密钥长度为 128 位(openssl: EVP_rc4(void): RC4 流密码。这是一种可变密钥长度的密码,默认密钥长度为 128 位)。
根据多个来源(例如 crypto101.io 或维基百科),RC4 不安全,不再建议使用。
因此,openssl_seal 不应该使用其他流密码来代替 RC4 吗?
当您需要安全地将数据传递到其他平台/语言时,openssl_seal() 可以很好地工作。openssl_seal() 的作用是:
1. 生成一个随机密钥
2. 使用随机密钥,使用 RC4 对数据进行对称加密
3. 使用公钥/证书,使用 RSA 对随机密钥本身进行加密
4. 返回加密后的数据和加密后的密钥
因此,解密的步骤很简单:
1. 使用 RSA 和您的私钥解密密钥
2. 使用 RC4 和解密后的密钥解密数据
最棘手的部分可能是弄清楚如何处理私钥 - BouncyCastle(http://www.bouncycastle.org/)为 Java 和 C# 提供了 PEMReader,而 Not Yet commons-ssl(http://juliusdavies.ca/commons-ssl/)具有 KeyStoreBuilder 以从 PEM 证书构建 Java 密钥库。
Java 中的完整示例在http://blog.local.ch/archive/2007/10/29/openssl-php-to-java.html中进行了描述