代码之家  ›  专栏  ›  技术社区  ›  wallenborn

Bouncycastle openpgpg:如何创建具有两个用户ID的PGP密钥对?

  •  3
  • wallenborn  · 技术社区  · 11 年前

    使用命令行gnupg i可以为“John Doe<john@doe.com>“。我还可以添加另一个具有uid”Jane Doe<jane@doe.com>“使用 --edit-key adduid 生成的secring.jpg如下所示:

    $ gpg -vv secring.gpg
    :secret key packet:
            version 4, algo 1, created 1380898817, expires 0
            skey[0]: [2048 bits]
            skey[1]: [17 bits]
            iter+salt S2K, algo: 9, SHA1 protection, hash: 2, salt: 3825c4409323d7b0
            protect count: 65536 (96)
            protect IV:  ac f9 8d 4a b7 3a 5d 7c b2 3c 28 ff 82 6d 4c ef
            encrypted stuff follows
    :user ID packet: "John Doe <john@doe.com>"
    :signature packet: algo 1, keyid 9991E2282A9906C7
            version 4, created 1380898818, md5len 0, sigclass 0x13
            digest algo 2, begin of digest bd 01
            hashed subpkt 2 len 4 (sig created 2013-10-04)
            hashed subpkt 27 len 1 (key flags: 03)
            hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
            hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
            hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
            hashed subpkt 30 len 1 (features: 01)
            subpkt 16 len 8 (issuer key ID 9991E2282A9906C7)
            data: [2045 bits]
    :trust packet: flag=00 sigcache=00
    :user ID packet: "Jane Doe <jane@doe.com>"
    :signature packet: algo 1, keyid 9991E2282A9906C7
            version 4, created 1380899570, md5len 0, sigclass 0x13
            digest algo 2, begin of digest 2f 50
            hashed subpkt 2 len 4 (sig created 2013-10-04)
            hashed subpkt 27 len 1 (key flags: 03)
            hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
            hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
            hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
            hashed subpkt 30 len 1 (features: 01)
            hashed subpkt 23 len 1 (key server preferences: 80)
            subpkt 16 len 8 (issuer key ID 9991E2282A9906C7)
            data: [2047 bits]
    :trust packet: flag=00 sigcache=00
    sec  2048R/2A9906C7 2013-10-04 John Doe <john@doe.com>
    sig        2A9906C7 2013-10-04   [selfsig]
    uid                            Jane Doe <jane@doe.com>
    sig        2A9906C7 2013-10-04   [selfsig]
    

    我想在Java中使用Bouncycastle创建同样的东西,从Java.security.KeyPair开始,即使用这样的签名:

    public void createKeyRing(KeyPair keyPair, char[] pass, String[] ids)
    

    其中pass是密码短语 ids = new String[] {"John Doe <john@doe.com>", "Jane Doe <jane@doe.com>"} 。我遵循 this tutorial ,其中代码的关键部分是:

    PGPKeyRingGenerator keyRingGen =
        new PGPKeyRingGenerator(
            PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign,
            id, sha1Calc, signhashgen.generate(), null,
            new BcPGPContentSignerBuilder(
                rsakp_sign.getPublicKey().getAlgorithm(),
                HashAlgorithmTags.SHA1),
            pske);
    
    // Add our encryption subkey, together with its signature.
    keyRingGen.addSubKey(rsakp_enc, enchashgen.generate(), null);
    

    其中,rsakp_sign和rsakp_enc分别是签名密钥和加密密钥的RSA密钥对,signhashgen和enchashgen是包含算法偏好等的子分组的生成器。

    只要我只为约翰创造一把钥匙,一切都很好。但当我想添加Jane时,我遇到了一个问题:显然我不能使用addSubKey为Jane添加一个signhashgen化的密钥对。第一次天真的尝试是这样的:

    PGPSignatureGenerator sGen = new PGPSignatureGenerator(
      new BcPGPContentSignerBuilder(PGPPublicKey.RSA_SIGN, PGPUtil.SHA1));
    sGen.init(PGPSignature.POSITIVE_CERTIFICATION, pgpPrivateKey);
    PGPSignature certification = sGen.generateCertification("Jane Doe <jane@doe.com>", pgpPublicKey);
    
    PGPPublicKey newKey = PGPPublicKey.addCertification(pgpPublicKey, "Jane Doe <jane@doe.com>", certification);
    
    keyRingGen.addSubKey(new PGPKeyPair(newKey, pgpPrivateKey), enchashgen.generate(), null);
    

    这没有任何作用,没有其他条目显示在 gpg --vv meeththedoes.asc .

    或者,我可以拿走约翰的钥匙圈,提取密钥,然后添加Jane:

    PGPSecretKeyRing keyRing = keyRingGen.generateSecretKeyRing();
    
    PGPSecretKey pgpSecretKey = keyRing.getSecretKey();
    BcPGPDigestCalculatorProvider calculatorProvider = new BcPGPDigestCalculatorProvider();
    BcPBESecretKeyDecryptorBuilder decryptor = new BcPBESecretKeyDecryptorBuilder(calculatorProvider);
    PGPPrivateKey pgpPrivateKey = pgpSecretKey.extractPrivateKey(decryptor.build(pass));    
    PGPPublicKey pgpPublicKey = pgpSecretKey.getPublicKey();
    
    PGPSignatureGenerator    sGen = new PGPSignatureGenerator(
      new BcPGPContentSignerBuilder(PGPPublicKey.RSA_SIGN, PGPUtil.SHA1));
    sGen.init(PGPSignature.POSITIVE_CERTIFICATION, pgpPrivateKey);
    PGPSignature certification = sGen.generateCertification("Jane Doe <jane@doe.com>", pgpPublicKey);
    
    PGPPublicKey newKey = PGPPublicKey.addCertification(pgpPublicKey, "Jane Doe <jane@doe.com>", certification);
    
    pgpSecretKey = PGPSecretKey.replacePublicKey(pgpSecretKey, newKey);
    

    这有点奏效,但在最终的钥匙圈中,John和Jane有不同的子包:

    $ gpg -vv meetthedoes.asc
    gpg: ASCII-Hülle: BEGIN PGP PRIVATE KEY BLOCK
    gpg: ASCII-Hülle: Version: BCPG v1.48
    :secret key packet:
            version 4, algo 3, created 1380908986, expires 0
            skey[0]: [2048 bits]
            skey[1]: [17 bits]
            iter+salt S2K, algo: 9, SHA1 protection, hash: 2, salt: 696d2d42bd6b727c
            protect count: 65536 (96)
            protect IV:  87 31 81 df 17 fa 74 c4 c3 35 39 26 98 c1 15 27
            encrypted stuff follows
    :user ID packet: "John Doe <john@doe.com>"
    :signature packet: algo 3, keyid 3A80073198CF2010
            version 4, created 1380908986, md5len 0, sigclass 0x13
            digest algo 2, begin of digest a5 c8
            hashed subpkt 2 len 4 (sig created 2013-10-04)
            hashed subpkt 27 len 1 (key flags: 03)
            hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
            hashed subpkt 21 len 5 (pref-hash-algos: 8 2 9 10 11)
            hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
            hashed subpkt 30 len 1 (features: 01)
            subpkt 16 len 8 (issuer key ID 3A80073198CF2010)
            data: [2047 bits]
    :user ID packet: "Jane Doe <jane@doe.com>"
    :signature packet: algo 3, keyid 3A80073198CF2010
            version 4, created 1380908986, md5len 0, sigclass 0x13
            digest algo 2, begin of digest f6 e8
            hashed subpkt 2 len 4 (sig created 2013-10-04)
            subpkt 16 len 8 (issuer key ID 3A80073198CF2010)
            data: [2039 bits]
    sec  2048s/98CF2010 2013-10-04 John Doe <john@doe.com>
    sig        98CF2010 2013-10-04   [selfsig]
    uid                            Jane Doe <jane@doe.com>
    sig        98CF2010 2013-10-04   [selfsig]
    

    我可能不想要,对吧?无论如何,这种方法似乎也是错误的。难道我不应该先完全安装生成器,然后让Bouncycastle一下子生成密钥环文件吗?我在这里做错了什么?

    1 回复  |  直到 11 年前
        1
  •  3
  •   wallenborn    11 年前

    API在添加第一个uid和随后的uid之间显然有所不同。对于约翰,我可以说

    PGPKeyRingGenerator generator = new PGPKeyRingGenerator(
      PGPSignature.POSITIVE_CERTIFICATION, 
      signKeyPair, john, sha1Calc,
      signhashgen.generate(), null,
      new BcPGPContentSignerBuilder(
        signKeyPair.getPublicKey().getAlgorithm(),
        HashAlgorithmTags.SHA1),
        new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256).build(pass)
    );    
    generator.addSubKey(encKeyPair, enchashgen.generate(), null);    
    PGPSecretKeyRing ring = generator.generateSecretKeyRing();
    

    这创建了一个带有签名密钥和加密子密钥的密钥环,就像gnuppp那样。但对Jane来说,这不起作用,因为携带子密钥信息的signhashgen不是addSubKey的参数。相反,我可以从戒指中提取John的密钥,从中创建一个PGPPrivateKey,并用它来签名Jane的uid。

    PGPSignatureGenerator generator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(PGPPublicKey.RSA_GENERAL,
      PGPUtil.SHA1)); 
    generator.init(PGPSignature.POSITIVE_CERTIFICATION, johnsPrivateKey);
    PGPSignatureSubpacketGenerator signhashgen = copyJohnsSignhashgen();      
    generator.setHashedSubpackets(signhashgen.generate());    
    PGPSignature certification = generator.generateCertification(jane, getEncryptionKey(secretKeyRing));
    PGPPublicKey janesKey = PGPPublicKey.addCertification(getEncryptionKey(secretKeyRing), jane, certification);
    

    剩下要做的就是将新钥匙添加到钥匙圈中。