请注意,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
中。这意味着可以将密封数据发送给多个接收者(前提是已获得他们的公钥)。每个接收者必须同时接收密封数据和用接收者公钥加密的信封密钥。
data
要密封的数据。
sealed_data
密封后的数据。
encrypted_keys
加密密钥数组。
public_key
OpenSSLAsymmetricKey 实例的数组,包含公钥。
cipher_algo
密码方法。
默认值 ('RC4'
) 被认为是不安全的。强烈建议显式指定安全密码方法。
iv
初始化向量。
成功时返回密封数据的长度,出错时返回 false
。如果成功,密封后的数据将返回到 sealed_data
中,信封密钥将返回到 encrypted_keys
中。
版本 | 说明 |
---|---|
8.0.0 |
public_key 现在接受 array 的 OpenSSLAsymmetricKey 实例;以前,接受 array 的 resource 类型为 OpenSSL key 。 |
8.0.0 |
cipher_algo 不再是可选参数。 |
8.0.0 |
iv 现在可以为空。 |
范例 #1 openssl_seal() 范例
<?php
// 假设 $data 包含要密封的数据
// 获取接收者的公钥并准备它们
$fp = fopen("/src/openssl-0.9.6/demos/maurice/cert.pem", "r");
$cert = fread($fp, 8192);
fclose($fp);
$pk1 = openssl_get_publickey($cert);
// 对第二个接收者重复
$fp = fopen("/src/openssl-0.9.6/demos/sign/cert.pem", "r");
$cert = fread($fp, 8192);
fclose($fp);
$pk2 = openssl_get_publickey($cert);
// 密封消息,只有 $pk1 和 $pk2 的所有者才能分别使用密钥 $ekeys[0] 和 $ekeys[1] 解密 $sealed。
openssl_seal($data, $sealed, $ekeys, array($pk1, $pk2));
// 从内存中释放密钥
openssl_free_key($pk1);
openssl_free_key($pk2);
?>
虽然默认使用 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