PHP实现RSA/OAEP-SHA256加密后Java后端解密失败求助
RSA OAEP加密PHP与Java兼容问题解决
问题描述
使用phpseclib3编写的RSA OAEP加密代码,生成的密文在Java后端解密时抛出javax.crypto.BadPaddingException: Decryption error,但Java自身实现的加密代码可正常解密。已确认Base64URL编解码环节无问题。
代码示例
PHP加密代码(原问题代码)
function encryptSecretKeyWithCertificate($plainKey, $certFilePath) { $certContent = file_get_contents($certFilePath); if (!$certContent) { throw new Exception("can't load certificate: $certFilePath"); } $rsa = PublicKeyLoader::load($certContent) ->withHash('sha256') ->withMGFHash('sha256') ->withPadding(RSA::ENCRYPTION_OAEP); return rtrim(strtr(base64_encode($rsa->encrypt($plainKey)), '+/', '-_'), '='); }
Java解密代码
private static byte[] decryptSecretKey(PrivateKey privKey, byte[] encKey) { try { Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING"); OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT); cipher.init(Cipher.DECRYPT_MODE, privKey, oaepParams); return cipher.doFinal(encKey, 0, encKey.length); }catch (Exception ex){ ex.printStackTrace(); return null; } }
Java正常加密代码
public static String encryptSecretKeyAsymmetric(byte[] key, PublicKey publicKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING"); final OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT); cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams); byte[] cipherText = cipher.doFinal(key); String encodedCipher = doEncode(cipherText); return encodedCipher; }
异常信息
javax.crypto.BadPaddingException: Decryption error at java.base/sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:488) at java.base/sun.security.rsa.RSAPadding.unpad(RSAPadding.java:284) at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:366) at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:400) at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2303) at org.example.CryptoUtil.decryptSecretKey(CryptoUtil.java:264) at org.example.CryptoUtil.symmetricDecryptResponse(CryptoUtil.java:224) at org.example.Main.main(Main.java:40)
问题根源
phpseclib3中,withMGFHash()方法仅用于PSS签名的MGF哈希配置,对OAEP加密的MGF哈希不生效。原代码虽设置了withHash('sha256')和withMGFHash('sha256'),但OAEP加密的MGF哈希仍使用默认的SHA-1,与Java端配置的SHA-256不一致,导致解密时填充校验失败。
解决方案
修改PHP代码,通过withPadding()参数或withOAEPParameters()方法,显式指定OAEP加密的哈希算法和MGF哈希算法:
方案一:使用withPadding()传递参数
function encryptSecretKeyWithCertificate($plainKey, $certFilePath) { $certContent = file_get_contents($certFilePath); if (!$certContent) { throw new Exception("can't load certificate: $certFilePath"); } // 依次指定OAEP哈希、MGF哈希、空标签(对应Java的PSource.PSpecified.DEFAULT) $rsa = PublicKeyLoader::load($certContent) ->withPadding(RSA::ENCRYPTION_OAEP, 'sha256', 'sha256', ''); return rtrim(strtr(base64_encode($rsa->encrypt($plainKey)), '+/', '-_'), '='); }
方案二:使用withOAEPParameters()方法
function encryptSecretKeyWithCertificate($plainKey, $certFilePath) { $certContent = file_get_contents($certFilePath); if (!$certContent) { throw new Exception("can't load certificate: $certFilePath"); } $rsa = PublicKeyLoader::load($certContent) ->withOAEPParameters('', RSA::ENCRYPTION_OAEP, 'sha256', 'sha256'); return rtrim(strtr(base64_encode($rsa->encrypt($plainKey)), '+/', '-_'), '='); }
结论
PHP通过phpseclib3完全可以实现符合RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING规范的加密,关键是要正确配置OAEP加密的哈希和MGF哈希参数,避免混淆签名与加密的配置方法。
内容的提问来源于stack exchange,提问作者Morpheus.47
相关产品推荐
相关产品推荐

