Ausgabe
Daher scheint der folgende Code einen PGP-Schlüsselbund mit EC-Schlüsseln korrekt zu generieren (wie in: er kann mit Bouncycastle geparst werden). Sowohl Thunderbird als auch GnuPG haben jedoch Probleme damit. Hier ist der Code, der auf einer bereits funktionierenden RSA/Elgamal-basierten Implementierung von mir basiert (ich habe der Lesbarkeit halber nur die relevanten Methoden eingefügt):
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();
}
Also, was bekomme ich hier und was sind die Probleme:
- Wenn ich SHA1 als Hash-Alg auswähle. Für den Hauptschlüssel liest GnuPG tatsächlich den Schlüsselbund, aber dem Unterschlüssel fehlt die Schlüsselfunktion “E” (Verschlüsselung) (und wir erhalten auch die vollkommen korrekte Warnung, dass SHA1 nicht ausreicht):
>> 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 <[email protected]>
sub cv25519 2021-09-17 []
- Mit SHA256 kann der Schlüsselbund überhaupt nicht geparst werden:
>> 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
Kann jemand das Problem mit dem Code erkennen, sodass sowohl GnuPG als auch Thunderbird den Schlüsselbund korrekt parsen können?
Lösung
Turns out, the keyring generation as such is alright — the issue was with the chosen curve resp. the master key generation. To fix the key generation:
- One either can keep the code as is and pick a different curve which works with the ECDSA scheme e.g.
secp256r1
instead ofCurve25519
:
private static final ASN1ObjectIdentifier CURVE_OID = SECObjectIdentifiers.secp256r1;
- However, if Curve25519 is to be used (N.B. which is a good choice, for further information please refer to https://safecurves.cr.yp.to/), then we have to use EdDSA with Ed25519 (see: RFC 8032) for the master key. The generation of the subkey/encryption key, however, stays the same. Ed25519 is an EdDSA (as opposed to ECDSA) signature scheme with edwards25519 as the curve. To implement it the correct way, two things about the code above have to be changed:
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;
And the master key pair generation, which is as follows:
public AsymmetricCipherKeyPair generateEd25519KeyPair() {
Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
keyPairGenerator.init(new Ed25519KeyGenerationParameters(new SecureRandom()));
return keyPairGenerator.generateKeyPair();
}
Beantwortet von – Pawel Os.
Antwort geprüft von – Mary Flores (FixError Volunteer)