[英]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.