我正在使用 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);
我得到类似 cryptoBased64:publicKeyBased64 的信息。
它似乎可以工作,如果我尝试使用与 PHP 相同的方法进行解密,它也可以工作。但尝试用java解密,给我java.security.InvalidKeyException:无效的 key 格式。 这是代码:
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 用于加密/解密和签名/验证两种环境。对于加密/解密,发送者使用接收者的公钥进行加密,接收者使用他自己的私钥进行解密。对于签名/验证,发送者使用自己的私钥进行签名,接收者使用发送者的公钥进行验证。这个有详细解释here和 here .
此时很明显,两个上下文在发布的代码中都被混淆了:对于加密/解密,发送者使用自己的私钥对消息进行加密,而接收者使用发送者的公钥对其进行解密。这是错误的。如果按照上述加密/解密的方式在两个代码中对此进行修改,则 Java 中的解密也将起作用!
要理解为什么解密在 PHP 中有效,而在 Java 中无效,必须考虑填充。出于安全原因,实际上 RSA 必须始终与填充结合使用,例如PKCS1-v1_5-填充或 OAEP。但是,填充会根据上下文而有所不同。使用 RSAES-PKCS1-v1_5-padding 进行加密/解密,并使用 RSASSA-PKCS1-v1_5-padding 进行签名/验证。这同样适用于 OAEP(分别为 RSAES-OAEP 和 RSASSA-PSS)。这在RFC8017中有详细解释。和 here 。重要的是解密的填充与加密的填充相同。否则解密会失败。这同样适用于签名/验证。
下面,假设使用 PKCS1-v1_5-padding,如发布的代码中所示。
在 PHP/phpseclib 中,
encrypt
/decrypt
-方法使用RSAES-PKCS1-v1_5-padding,无论使用公钥还是私钥来加密(或解密)。这意味着加密
/解密
方法始终对应于加密/解密上下文。对于签名/验证有相应的方法sign
/verify
。这意味着在发布的代码中,PHP 端使用 RSAES-PKCS1-v1_5-padding 进行加密和解密,这就是解密在 PHP 端起作用的原因。在 Java 中,
Cipher
-class 根据模式和键类型的组合确定上下文或填充。 加密模式/公钥和解密模式/私钥组合使用 RSAES-PKCS1-v1_5-padding 定义加密/解密上下文,其他组合定义带有 RSASSA-PKCS1-v1_5-padding 的符号/验证上下文。这意味着在发布的代码中,RSASSA-PKCS1-v1_5-padding 用于 Java 端进行解密。因此,由于填充不同,在 PHP 中加密的消息在 Java 中解密失败。注意:通常在 Java 中,
Signature
-class 用于签名/验证(Cipher
-class 用于加密/解密)。由于发布的代码没有透露要加密的内容,因此还应该提到的是,只有相对较短的消息可以使用 RSA 进行加密,而对于较长的消息,将使用对称算法(例如 AES) ,或两个进程的组合 ( hyprid cryptosystem )。
关于java - 为什么我尝试用 java 解密 RSA 但用 php 加密时会出现此异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58468087/