Java 1.8迁移至Java 11后PGP解密报InvalidCipherTextException错误
PGP消息解密异常排查(Java 8迁移至Java 11后)
问题背景
应用从Java 1.8迁移至Java 11后,某业务模块出现PGP消息解密失败问题。
报错详情
- 核心异常:
org.bouncycastle.crypto.InvalidCipherTextException: block incorrect - 异常触发代码行:
InputStream clear = pbe.getDataStream(b);
测试规律
当PGP消息输入结尾带有^M$(Windows换行符)时,解密可正常完成;无该标识时,解密直接抛出上述异常。
疑问
是否需要在解密前对PGP消息进行格式化处理?
相关代码片段
public class TestDecrypt { private static final Logger LOGGER = LogManager.getLogger(TestDecrypt.class); public TestDecrypt() { } public static String decryptContent(String input, String publicKey, String keystoreFile, String keystoreCred, String fileName) { LOGGER.info("[Starting Decryption for {} ] ", fileName); String output = ""; try { InputStream in = PGPUtil.getDecoderStream(new ByteArrayInputStream(input.getBytes())); Throwable var7 = null; try { File publicKeyObject = new File(publicKey); File privateKeyObject = new File(keystoreFile); PGPBean pgpBean = new PGPBean(publicKeyObject, privateKeyObject, keystoreCred); Security.addProvider(new BouncyCastleProvider()); PGPObjectFactory pgpF = new JcaPGPObjectFactory(in); Object o = pgpF.nextObject(); PGPEncryptedDataList enc; if (o instanceof PGPEncryptedDataList) { enc = (PGPEncryptedDataList)o; } else { enc = (PGPEncryptedDataList)pgpF.nextObject(); } Iterator<PGPEncryptedData> it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe; for(pbe = null; sKey == null && it.hasNext(); sKey = pgpBean.getPgpPrivateKey()) { pbe = (PGPPublicKeyEncryptedData)it.next(); } if (sKey == null) { throw new IllegalArgumentException("Secret key for message not found."); } PublicKeyDataDecryptorFactory b = (new JcePublicKeyDataDecryptorFactoryBuilder()).setProvider("BC").setContentProvider("BC").build(sKey); InputStream clear = pbe.getDataStream(b); PGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); Object message = plainFact.nextObject(); if (message instanceof PGPCompressedData) { PGPCompressedData cData = (PGPCompressedData)message; PGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream()); message = pgpFact.nextObject(); } if (!(message instanceof PGPLiteralData)) { if (message instanceof PGPOnePassSignatureList) { throw new PGPException("Encrypted message contains a signed message - not literal data."); } throw new PGPException("Message is not a simple encrypted file - type unknown."); } PGPLiteralData ld = (PGPLiteralData)message; InputStream unc = ld.getInputStream(); output = (String)((Stream)(new BufferedReader(new InputStreamReader(unc))).lines().parallel()).collect(Collectors.joining("\n")); if (pbe.isIntegrityProtected() && !pbe.verify()) { throw new PGPException("Message failed integrity check"); } LOGGER.info("[Completed Decryption for {} ] ", fileName); } catch (Throwable var31) { var7 = var31; throw var31; } finally { if (in != null) { if (var7 != null) { try { in.close(); } catch (Throwable var30) { var7.addSuppressed(var30); } } else { in.close(); } } } } catch (Exception var33) { LOGGER.error("[Exception occurred while decrypting {} ] ", fileName); LOGGER.error("Exception has been caught : ", var33); output = "FAILURE"; } return output; } }
堆栈跟踪
11:54:09,394[main] ERROR(TestDecrypt.java:118) - Exception has been caught : org.bouncycastle.openpgp.PGPException: exception decrypting session data at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.decryptSessionData(Unknown Source) ~[bcpg-jdk15on-1.70.jar:1.70.00.0] at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.access$100(Unknown Source) ~[bcpg-jdk15on-1.70.jar:1.70.00.0] at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder$2.recoverSessionData(Unknown Source) ~[bcpg-jdk15on-1.70.jar:1.70.00.0] at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getSessionKey(Unknown Source) ~[bcpg-jdk15on-1.70.jar:1.70.00.0] at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source) ~[bcpg-jdk15on-1.70.jar:1.70.00.0] at com.company.test.util.service.TestDecrypt.decryptContent(TestDecrypt.java:74) ~[test-utility/:?] at com.company.test.util.service.TestDecrypt.main(TestDecrypt.java:235) ~[test-utility/:?] Caused by: org.bouncycastle.jcajce.provider.util.BadBlockException: unable to decrypt block at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.getOutput(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at javax.crypto.Cipher.doFinal(Cipher.java:2083) ~[?:?] ... 7 more Caused by: org.bouncycastle.crypto.InvalidCipherTextException: block incorrect at org.bouncycastle.crypto.encodings.PKCS1Encoding.decodeBlock(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at org.bouncycastle.crypto.encodings.PKCS1Encoding.processBlock(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.getOutput(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source) ~[bcprov-jdk15on-1.70.jar:1.70.0] at javax.crypto.Cipher.doFinal(Cipher.java:2083) ~[?:?] ... 7 more
问题解答
不需要刻意格式化PGP消息:PGP消息本身是二进制或ASCII-armored格式,换行符不属于PGP消息的必要组成部分,出现这种差异是Java版本迁移后的编码/字节处理逻辑变化导致,而非PGP消息本身需要格式化。
核心原因分析:
- 代码中直接使用
input.getBytes()转换字符串为字节流,未指定编码。Java 8和Java 11的默认编码可能存在差异,导致转换后的字节流与原始PGP消息不匹配,解密时无法正确解析会话密钥。 - 消息结尾的
^M$可能刚好弥补了编码转换导致的字节缺失,让解密逻辑能正常识别消息边界。
- 代码中直接使用
修复建议:
- 明确指定字符串转字节的编码(根据PGP消息的实际编码选择,通常为UTF-8或ASCII):
InputStream in = PGPUtil.getDecoderStream(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))); - 检查PGP消息的传输/存储环节,确保消息未被意外修改(比如丢失字节、替换换行符)。
- 确认BouncyCastle版本与Java 11的兼容性,当前1.70版本兼容,但可尝试升级至最新稳定版排除潜在版本问题。
- 明确指定字符串转字节的编码(根据PGP消息的实际编码选择,通常为UTF-8或ASCII):
内容的提问来源于stack exchange,提问作者Chennai Cheetah
相关产品推荐
相关产品推荐

