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

使用BouncyCastle解析EdDSA类型PGP公钥时指纹异常且UserID为空

问题与解决方案

问题描述

使用BouncyCastle解析PGP公钥时,RSA加密生成的公钥可正常读取信息,但EdDSA生成的公钥出现异常:获取到的指纹与Kleopatra显示的不一致,且UserIDs为空。

依赖与测试代码

依赖配置

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.72</version>
</dependency>

测试代码

@Test
public void test() throws NoSuchProviderException, IOException, PGPException {
    InputStream in = PGPUtil.getDecoderStream(new FileInputStream("public_EdDsa.asc"));

    PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, new JcaKeyFingerprintCalculator());

    List<PGPPublicKey> keys = new ArrayList<>();

    Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();

    while (CollectionUtils.isEmpty(keys) && rIt.hasNext()) {
        PGPPublicKeyRing kRing = rIt.next();
        Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
        while (CollectionUtils.isEmpty(keys) && kIt.hasNext()) {
            PGPPublicKey k = kIt.next();
            if (k.isEncryptionKey()) {
                keys.add(k);
            }
        }
    }

    for (PGPPublicKey pgpPublicKey : keys) {
        System.out.println(pgpPublicKey.getKeyID());

        byte[] fingerprint = pgpPublicKey.getFingerprint();
        for (byte b : fingerprint) {
            System.out.print(b);
        }
        System.out.println();
        System.out.println(byte2Hex(fingerprint));

        Iterator<String> userIDs = pgpPublicKey.getUserIDs();
        while (userIDs.hasNext()){
            String next = userIDs.next();
            System.out.println(next);
        }

        System.out.println("============end============");
    }
}

解决方案

1. 升级BouncyCastle版本

BouncyCastle 1.72对EdDSA的PGP支持存在缺陷,建议升级到1.73及以上稳定版本(如1.77),后续版本修复了EdDSA密钥的解析问题,能正确读取指纹和关联信息。

修改依赖:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.77</version>
</dependency>

2. 修正密钥筛选逻辑

EdDSA在PGP体系中默认作为签名密钥使用,而非加密密钥。原代码仅筛选isEncryptionKey()的密钥,会漏掉EdDSA主密钥,导致读取到的是无关子密钥(或无密钥),进而出现指纹不匹配、UserIDs为空的问题。

修改筛选条件,同时包含签名密钥:

// 替换原有的if判断
if (k.isEncryptionKey() || k.isSigningKey()) {
    keys.add(k);
}

另外,原代码中CollectionUtils.isEmpty(keys) && ...的逻辑会在找到第一个符合条件的密钥后停止遍历,建议去掉该限制,遍历所有密钥以确认主密钥信息。

3. 修复指纹显示逻辑

直接打印byte数组的原始值会因byte的有符号特性导致显示异常,需确保byte2Hex方法将byte作为无符号值转换为十六进制:

private static String byte2Hex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02X", b & 0xFF));
    }
    return sb.toString();
}

修改后的完整测试代码

@Test
public void test() throws NoSuchProviderException, IOException, PGPException {
    InputStream in = PGPUtil.getDecoderStream(new FileInputStream("public_EdDsa.asc"));

    PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in, new JcaKeyFingerprintCalculator());

    List<PGPPublicKey> keys = new ArrayList<>();

    Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();

    while (rIt.hasNext()) {
        PGPPublicKeyRing kRing = rIt.next();
        Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
        while (kIt.hasNext()) {
            PGPPublicKey k = kIt.next();
            if (k.isEncryptionKey() || k.isSigningKey()) {
                keys.add(k);
            }
        }
    }

    for (PGPPublicKey pgpPublicKey : keys) {
        System.out.println("密钥ID: " + Long.toHexString(pgpPublicKey.getKeyID()).toUpperCase());

        byte[] fingerprint = pgpPublicKey.getFingerprint();
        System.out.println("指纹十六进制: " + byte2Hex(fingerprint));

        Iterator<String> userIDs = pgpPublicKey.getUserIDs();
        while (userIDs.hasNext()){
            String next = userIDs.next();
            System.out.println("用户ID: " + next);
        }

        System.out.println("密钥用途: " + (pgpPublicKey.isSigningKey() ? "签名" : "") 
                + (pgpPublicKey.isEncryptionKey() ? "加密" : ""));
        System.out.println("============end============");
    }
}

private static String byte2Hex(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02X", b & 0xFF));
    }
    return sb.toString();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:03:11