PGP加密代码生成的密文无法解密,报错unknown object in stream:20
PGP加密解密失败:
unknown object in stream: 20 问题排查 错误根源分析
错误提示中的unknown object in stream: 20,指PGP加密流中出现了类型20的数据包(Symmetric-Key Encrypted Session Key Packet),但你的解密逻辑是按公钥加密流程处理(期望类型1的公钥加密会话密钥包),或者加密代码本身的PGP包构造逻辑存在错误。
既然在线工具能正常用同一密钥对加解密,说明密钥本身无问题,问题完全出在Groovy加密代码的实现上。
常见代码错误点及修复建议
1. 误使用对称密钥加密逻辑
如果你的代码错误调用了对称密钥加密生成器而非公钥加密生成器,就会生成类型20的数据包,导致私钥解密时无法识别:
- 检查是否使用了
PGPPublicKeyEncryptedDataGenerator(公钥加密),而非PGPSymmetricKeyEncryptedDataGenerator(对称密钥加密) - 确保加密时是用目标公钥加密会话密钥,而非直接用对称密钥加密消息内容
2. PGP数据包顺序错误
PGP加密流的正确构造顺序必须是:
- 公钥加密的会话密钥包(类型1)
- 对称加密的数据包(类型18)
如果代码颠倒了包顺序或遗漏了必要包,解密迭代器会无法识别错误的包类型,触发该异常。
3. ASCII Armor格式不规范
在线工具输出的密文是标准ASCII Armor格式(带-----BEGIN PGP MESSAGE-----头尾标记),如果你的代码直接输出二进制流,或生成Armor时格式错误(比如缺少头尾、存在多余空白字符),也会导致解密失败:
- 确认是否用
ArmoredOutputStream包裹输出流,生成标准PGP Armor格式密文 - 检查输出密文的头尾标记是否完整,无额外字符
4. 加密算法不匹配
检查代码中指定的对称加密算法(如AES-256)是否与密钥支持的算法一致,同时解密逻辑是否使用了相同的算法配置。若密钥仅支持CAST5但代码用了AES-256,也可能导致包解析错误。
正确的Groovy公钥加密代码示例
import org.bouncycastle.openpgp.* import org.bouncycastle.jce.provider.BouncyCastleProvider import java.security.Security import java.security.SecureRandom Security.addProvider(new BouncyCastleProvider()) void encryptPGPMessage(PGPPublicKey publicKey, String plainText, OutputStream outputStream) { // 初始化ASCII Armor输出流 ArmoredOutputStream armoredOut = new ArmoredOutputStream(outputStream) // 初始化公钥加密会话密钥生成器 PGPPublicKeyEncryptedDataGenerator encryptor = new PGPPublicKeyEncryptedDataGenerator() encryptor.addMethod(publicKey) // 生成随机对称会话密钥(AES-256) byte[] sessionKey = new byte[32] new SecureRandom().nextBytes(sessionKey) // 打开加密输出流 OutputStream encryptedOut = encryptor.open(armoredOut, sessionKey) // 初始化压缩数据生成器 PGPCompressedDataGenerator compressor = new PGPCompressedDataGenerator(PGPCompressedData.ZIP) OutputStream compressedOut = compressor.open(encryptedOut) // 初始化明文数据生成器 PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator() OutputStream literalOut = literalGen.open(compressedOut, PGPLiteralData.TEXT, "", new Date(), new byte[1 << 16]) // 写入明文内容 literalOut.write(plainText.getBytes("UTF-8")) // 按顺序关闭所有流(必须严格顺序) literalOut.close() literalGen.close() compressedOut.close() compressor.close() encryptedOut.close() encryptor.close() armoredOut.close() }
验证步骤
- 用修正后的代码重新生成密文,确认输出是标准ASCII Armor格式
- 用在线工具解密新密文,验证是否成功
- 对比原代码与示例代码的差异,重点排查加密器类型、数据包顺序、流关闭逻辑
内容的提问来源于stack exchange,提问作者kanha
相关产品推荐
相关产品推荐

