請注意,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
加密方法。
8.0 之前 PHP 版本的預設值是 ('RC4'
),這被認為是不安全的。強烈建議明確指定安全的加密方法。
iv
用於解密 data
的初始化向量。如果加密方法需要 IV,則此參數為必需。可以透過使用 cipher_algo
呼叫 openssl_cipher_iv_length() 來確認是否需要 IV。
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 的擁有者才能使用金鑰 $ekeys[0] 和 $ekeys[1] 分別解密 $sealed。
if (openssl_seal($data, $sealed, $ekeys, array($pk1, $pk2), 'AES256', $iv) > 0) {
// 可以儲存 $sealed 和 $iv 值,並稍後在 openssl_open 中使用
echo "success\n";
}
?>
雖然預設使用 RC4,但可以使用其他更安全的演算法。這些演算法指定為第五個參數。此外,需要新增一個初始化向量(隨機位元組)。例如:
<?php
$data = "這是最高機密。";
// 取得收件者的公鑰,並準備好
$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