【问题标题】:Bouncy Castle (Java): How to correctly generate a PGP keyring with EC keys?Bouncy Castle (Java):如何使用 EC 密钥正确生成 PGP 密钥环?
【发布时间】:2021-09-20 10:37:00
【问题描述】:

因此,以下代码似乎可以正确生成带有 EC 密钥的 PGP 密钥环(如:可以使用 Bouncycastle 解析)。然而,Thunderbird 和 GnuPG 都存在问题。下面是代码,它基于我已经工作的基于 RSA/Elgamal 的实现(为了便于阅读,我只包括了相关的方法):

    private static final Provider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
    private static final ASN1ObjectIdentifier CURVE_OID = CryptlibObjectIdentifiers.curvey25519;

    private static final int MASTER_KEY_ALGORITHM = ECDSA;
    private static final int SUB_KEY_ALGORITHM = ECDH;

    private static final int MASTER_KEY_FLAGS = AUTHENTICATION | CERTIFY_OTHER | SIGN_DATA | ENCRYPT_STORAGE | ENCRYPT_COMMS;
    private static final int SUB_KEY_FLAGS = ENCRYPT_COMMS | ENCRYPT_STORAGE;

    private static final int[] PREFERRED_HASH_ALGORITHMS = {SHA256, SHA1, SHA384, SHA512, SHA224};
    private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = {AES_256, AES_192, AES_128};

    public PGPKeyRingGenerator createPGPKeyRingGenerator(String identity, String passphrase, int keySize)
        throws PGPException, InvalidAlgorithmParameterException {

        PGPKeyPair masterKeyPair = generateEcPgpKeyPair(MASTER_KEY_ALGORITHM);
        PGPKeyPair subKeyPair = generateEcPgpKeyPair(SUB_KEY_ALGORITHM);

        PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(SHA1);
        PGPDigestCalculator sha256Calc = new BcPGPDigestCalculatorProvider().get(SHA256);

        PGPSignatureSubpacketVector masterKeySubPacket = generateMasterkeySubpacket(MASTER_KEY_FLAGS);
        PGPSignatureSubpacketVector subKeySubPacket = generateSubkeySubpacket(SUB_KEY_FLAGS);

        PGPKeyRingGenerator keyRingGenerator =
            new PGPKeyRingGenerator(POSITIVE_CERTIFICATION, masterKeyPair, identity, sha1Calc, masterKeySubPacket,
                                    null,
                                    new JcaPGPContentSignerBuilder(masterKeyPair.getPublicKey().getAlgorithm(), SHA256)
                                        .setProvider(BOUNCY_CASTLE_PROVIDER),
                                    new JcePBESecretKeyEncryptorBuilder(AES_256, sha256Calc)
                                        .setProvider(BOUNCY_CASTLE_PROVIDER)
                                        .build(passphrase.toCharArray()));

        keyRingGenerator.addSubKey(subKeyPair, subKeySubPacket, null);

        return keyRingGenerator;
    }

    private AsymmetricCipherKeyPair generateEcKeyPair(ASN1ObjectIdentifier curveOid) {

        X9ECParameters curve = CustomNamedCurves.getByOID(curveOid);
        ECNamedDomainParameters ecDomainParameters = new ECNamedDomainParameters(curveOid, curve.getCurve(), curve.getG(), curve.getN(), curve.getH(), curve.getSeed());

        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
        keyPairGenerator.init(new ECKeyGenerationParameters(ecDomainParameters, new SecureRandom()));

        return keyPairGenerator.generateKeyPair();
    }

    private BcPGPKeyPair generateEcPgpKeyPair(int algorithm)
        throws InvalidAlgorithmParameterException, PGPException {

        return new BcPGPKeyPair(algorithm, generateEcKeyPair(CURVE_OID), new Date());
    }

    private PGPSignatureSubpacketVector generateMasterkeySubpacket(int keyFlags) {

        PGPSignatureSubpacketGenerator subpacketGen = new PGPSignatureSubpacketGenerator();

        subpacketGen.setKeyFlags(false, keyFlags);
        subpacketGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
        subpacketGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);

        return subpacketGen.generate();
    }

    private PGPSignatureSubpacketVector generateSubkeySubpacket(int keyFlags) {

        PGPSignatureSubpacketGenerator subpacketGen = new PGPSignatureSubpacketGenerator();
        subpacketGen.setKeyFlags(false, keyFlags);

        return subpacketGen.generate();
    }

那么,我在这里得到什么以及有什么问题:

  1. 当我选择 SHA1 作为哈希算法时。对于主密钥,GnuPG 实际上确实读取了密钥环,但子密钥缺少“E”(加密)密钥功能(而且我们得到了关于 SHA1 不足的完全正确的警告):
 >> gpg ./ec-sha1.asc
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: ECDSA key 8DF084241E957FFF requires a 256 bit or larger hash (hash is SHA1)
gpg: ECDSA key 8DF084241E957FFF requires a 256 bit or larger hash (hash is SHA1)
pub   cv25519 2021-09-17 [SCA]
      553E322AB50692F67E23FE7B8DF084241E957FFF
uid           Foo Bar <foo@bar.loc>
sub   cv25519 2021-09-17 []
  1. 使用 SHA256,根本无法解析密钥环:
 >> gpg ./ec.asc
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: Fatal: _gcry_mpi_ec_add_points: Montgomery not yet supported

谁能发现代码的问题,以便 GnuPG 和 Thunderbird 都能正确解析密钥环?

【问题讨论】:

    标签: java bouncycastle pgp elliptic-curve curve-25519


    【解决方案1】:

    事实证明,密钥环生成本身没问题——问题在于选择的曲线。主密钥生成。修复密钥生成:

    1. 既可以保持代码不变,又可以选择一条适用于 ECDSA 方案的不同曲线,例如secp256r1 而不是 Curve25519
        private static final ASN1ObjectIdentifier CURVE_OID = SECObjectIdentifiers.secp256r1;
    
    1. 但是,如果要使用 Curve25519(注意,这是一个不错的选择,有关详细信息,请参阅 https://safecurves.cr.yp.to/),那么我们必须使用 EdDSA 和 Ed25519(参见:RFC 8032)作为主密钥。但是,子密钥/加密密钥的生成保持不变。 Ed25519 是一种 EdDSA(与 ECDSA 相对)签名方案,曲线为 edwards25519。两个以正确的方式实现它,上面的代码有两点需要改变:
        private static final int MASTER_KEY_ALGORITHM = PublicKeyAlgorithmTags.EDDSA;
        private static final ASN1ObjectIdentifier MASTER_CURVE_OID = EdECObjectIdentifiers.id_Ed25519;
        private static final ASN1ObjectIdentifier SUB_CURVE_OID = CryptlibObjectIdentifiers.curvey25519;
    

    以及主密钥对的生成,如下:

        public AsymmetricCipherKeyPair generateEd25519KeyPair() {
    
            Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
            keyPairGenerator.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
    
            return keyPairGenerator.generateKeyPair();
        }
    

    【讨论】:

      猜你喜欢
      • 2018-02-20
      • 1970-01-01
      • 1970-01-01
      • 2016-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多