[英]Why I get this exception by trying to decrypt RSA with java but encrypted with php?
我正在使用 PHPSecLib 使用 RSA 加密我的文本:
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
extract($rsa->createKey());
$rsa->loadKey($privatekey);
$ciphertext = $rsa->encrypt($plaintext);
$rsa->loadKey($publickey);
return base64_encode($ciphertext) . ":" . base64_encode($publickey);
我得到類似 encryptedBased64:publicKeyBased64 的東西。
它似乎有效,如果我嘗試使用與 PHP 相同的方法解密它也可以。 但是嘗試用 java 解密,給了我 java.security.InvalidKeyException:無效的密鑰格式。 這是代碼:
public static String decrypt(byte[] msg, byte[] key)
throws Exception{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
PublicKey keys = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding","BC");
cipher.init(Cipher.DECRYPT_MODE, keys);
return new String(cipher.doFinal(msg));
}
public static void main(String[] args) throws Exception{
String res = "encryptedBased64:publicKeyBased64";
decrypt(Base64.getDecoder().decode(res.split(":")[0]),Base64.getDecoder().decode(res.split(":")[1]));
}
無法理解為什么。
RSA 用於兩個上下文en-/decrypting和sign-/verifying 。 對於加密/解密,發送者使用接收者的公鑰加密,接收者使用自己的私鑰解密。 對於簽名/驗證,發件人使用自己的私鑰進行簽名,收件人使用發件人的公鑰進行驗證。 這在此處和此處進行了詳細說明。
在這一點上,很明顯,在發布的代碼中這兩種上下文都被混淆了:對於加密/解密,發送者用他自己的私鑰加密消息,而接收者用發送者的公鑰解密它。 這是錯誤的。 如果在上述兩個代碼中都進行了修改以進行加密/解密,則 Java 中的解密也將起作用!
要理解為什么解密在 PHP 的情況下有效,而在 Java 的情況下無效,必須考慮填充。 出於安全原因,實際上 RSA 必須始終與填充一起使用,例如 PKCS1-v1_5-padding 或 OAEP。 但是,填充因上下文而異。 用於加密/解密 RSAES-PKCS1-v1_5-padding 並用於簽名/驗證 RSASSA-PKCS1-v1_5-padding。 這同樣適用於 OAEP(分別為 RSAES-OAEP 和 RSASSA-PSS)。 這在RFC8017和這里有詳細解釋。 重要的是,用於解密的填充與用於加密的填充相同。 否則解密將失敗。 這同樣適用於簽名/驗證。
在下文中,假設 PKCS1-v1_5-padding 與發布的代碼一樣使用。
在 PHP/phpseclib 中,encrypt
/ decrypt
方法使用 RSAES-PKCS1-v1_5-padding,無論是使用公鑰還是私鑰進行加密(或解密)。 這意味着encrypt
/ decrypt
方法始終對應於加密/解密上下文。 對於簽名/驗證,有相應的方法sign
/ verify
。 這意味着在發布的代碼中,RSAES-PKCS1-v1_5-padding 在 PHP 端用於加密和解密,這就是解密在 PHP 端起作用的原因。
在 Java 中, Cipher
類根據模式和密鑰類型的組合確定上下文或填充。 加密模式/公鑰- 和解密模式/私鑰- 組合使用 RSAES-PKCS1-v1_5-padding 定義加密/解密上下文,其他組合使用 RSASSA-PKCS1-v1_5- 定義簽名/驗證上下文填充。 這意味着在發布的代碼中,在 Java 端使用 RSASSA-PKCS1-v1_5-padding 進行解密。 因此,由於填充不同,在 PHP 中加密的消息的解密在 Java 中失敗。
注意:通常在 Java 中, Signature
類用於簽名/驗證(而Cipher
類用於加密/解密)。
由於發布的代碼沒有透露要加密的內容,因此還應該提及的是,只有相對較短的消息可以使用 RSA 加密,對於較長的消息,將使用對稱算法(如 AES),或兩者結合使用(混合密碼系統)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.