You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题解答

  1. 不需要刻意格式化PGP消息:PGP消息本身是二进制或ASCII-armored格式,换行符不属于PGP消息的必要组成部分,出现这种差异是Java版本迁移后的编码/字节处理逻辑变化导致,而非PGP消息本身需要格式化。

  2. 核心原因分析:

    • 代码中直接使用input.getBytes()转换字符串为字节流,未指定编码。Java 8和Java 11的默认编码可能存在差异,导致转换后的字节流与原始PGP消息不匹配,解密时无法正确解析会话密钥。
    • 消息结尾的^M$可能刚好弥补了编码转换导致的字节缺失,让解密逻辑能正常识别消息边界。
  3. 修复建议:

    • 明确指定字符串转字节的编码(根据PGP消息的实际编码选择,通常为UTF-8或ASCII):
      InputStream in = PGPUtil.getDecoderStream(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)));
      
    • 检查PGP消息的传输/存储环节,确保消息未被意外修改(比如丢失字节、替换换行符)。
    • 确认BouncyCastle版本与Java 11的兼容性,当前1.70版本兼容,但可尝试升级至最新稳定版排除潜在版本问题。

内容的提问来源于stack exchange,提问作者Chennai Cheetah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:39:55