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

PGP签名验证遭遇invalid header异常,寻求解决方案

PGP签名验证触发"invalid header encountered"异常解决方案

需求

  • 签名哈希算法:SHA256
  • 发送方使用自身私钥对消息签名
  • 接收方使用发送方公钥验证签名

问题现象

验证签名时抛出如下异常:

Exception in thread "main" java.io.IOException: invalid header encountered
at org.bouncycastle.bcpg.BCPGInputStream.readPacket(Unknown Source)
at org.bouncycastle.openpgp.PGPLiteralData.(Unknown Source)
at org.bouncycastle.openpgp.PGPObjectFactory.nextObject(Unknown Source)
at com.groupId.auth.PGPExample.verify(PGPExample.java:253)
at com.groupId.auth.PGPExample.main(PGPExample.java:52)

异常出现在代码行while ((obj = pgpObjectFactory.nextObject()) != null) {...}的pgpObjectFactory.nextObject()调用处。

问题根源分析

  1. 签名编码与解码不匹配:签名方法使用ArmoredOutputStream生成ASCII装甲格式的签名,但验证方法直接将装甲格式的字节流传入PGPObjectFactory,未进行装甲解码,导致PGP解析器无法识别格式头。
  2. 公钥使用错误:验证时误用了接收方公钥,实际应该使用发送方的公钥来验证签名。
  3. 消息处理冗余:签名时对普通字符串消息调用PGPUtil.getDecoderStream,这会错误地将消息当作PGP装甲格式解码,破坏原始消息内容。

修改后的代码实现

修正后的签名方法

private static byte[] sign(String message) throws IOException, PGPException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    // 生成ASCII装甲格式的签名输出流
    ArmoredOutputStream signedOut = new ArmoredOutputStream(out);

    PGPSecretKey senderPrivateKey = PGPUtils.getSenderPrivateKey();
    PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
            new JcaPGPContentSignerBuilder(senderPrivateKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA256)
                    .setProvider("BC")
    );

    String password = "password";
    // 初始化签名生成器,使用BINARY_DOCUMENT类型
    signatureGenerator.init(PGPSignature.BINARY_DOCUMENT,
            senderPrivateKey.extractPrivateKey(
                    new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(password.toCharArray())));

    // 添加签名者用户ID(可选)
    Iterator<?> it = senderPrivateKey.getPublicKey().getUserIDs();
    if (it.hasNext()) {
        PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
        spGen.addSignerUserID(false, (String) it.next());
        signatureGenerator.setHashedSubpackets(spGen.generate());
    }

    // 直接使用原始消息字节流,无需装甲解码
    InputStream in = new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8));
    byte[] buf = new byte[8192];
    int len;
    while ((len = in.read(buf)) > 0) {
        signatureGenerator.update(buf, 0, len);
    }
    in.close();

    // 生成并编码签名
    signatureGenerator.generate().encode(new BCPGOutputStream(signedOut));
    
    // 关闭流
    signedOut.close();
    out.close();

    return out.toByteArray();
}

修正后的验证方法

private static boolean verify(String message, byte[] signature) throws IOException, PGPException {
    // 先对ASCII装甲格式的签名进行解码
    InputStream decodedIn = PGPUtil.getDecoderStream(new ByteArrayInputStream(signature));
    PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(decodedIn, new JcaKeyFingerprintCalculator());

    Object obj;
    while ((obj = pgpObjectFactory.nextObject()) != null) {
        if (obj instanceof PGPSignatureList) {
            PGPSignatureList signatureList = (PGPSignatureList) obj;
            if (signatureList.isEmpty()) {
                throw new IllegalArgumentException("No signature found in the data");
            }
            PGPSignature pgpSignature = signatureList.get(0);
            // 使用发送方公钥初始化验证器,而非接收方公钥
            pgpSignature.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), PGPUtils.getSenderPublicKey());
            // 更新原始消息内容
            pgpSignature.update(message.getBytes(StandardCharsets.UTF_8));
            return pgpSignature.verify();
        }
    }
    throw new IllegalArgumentException("No signature found in the data");
}

主方法(补充message定义)

public static void main(String[] args) throws Exception {
    Security.addProvider(new BouncyCastleProvider());
    String message = "待签名的测试消息"; // 补充定义message变量

    byte[] signature = sign(message);
    System.out.println("Signature: ");
    System.out.println(new String(signature));

    boolean verified = verify(message, signature);
    System.out.println("Signature Verified: " + verified);
}

关键修改点说明

  1. 签名解码:验证时通过PGPUtil.getDecoderStream处理签名输入流,将ASCII装甲格式转换为PGP可解析的二进制格式。
  2. 公钥修正:将验证时的公钥替换为发送方公钥,这是签名验证的核心逻辑——必须用签名者的公钥验证其私钥生成的签名。
  3. 消息处理优化:签名时直接使用原始消息字节流,避免不必要的装甲解码操作,保证消息内容的完整性。

内容的提问来源于stack exchange,提问作者MM Ary

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:55:10