I need help on how to correctly generate PGPKeyring that contains 3 keys each for signing, encryption, and authentication. These keys to be uploaded to a smartcard with SmartPGP applet. I have successfully put the keys into the card, and tested it with the OpenKeyChain. But it only works when I am using RSA type of keys.
My code to generate the keys is as follows:
private PgpKeyRingGenerator CreatePgpKeyRingGenerator(string email, string signatureKeyAlg, string encryptionKeyAlg, string authenticationKeyAlg)
{
PgpKeyPair signatureKeyPair = CreatePgpKeyPair(signatureKeyAlg, false);
int certificationLevel = PgpSignature.DefaultCertification;
string id = email;
SymmetricKeyAlgorithmTag encAlgorithm = SymmetricKeyAlgorithmTag.Null;
HashAlgorithmTag hashAlgorithm = HashAlgorithmTag.Sha256;
char[] rawPassPhrase = null;
bool useSha1 = true;
PgpSignatureSubpacketGenerator masterHashGen = new PgpSignatureSubpacketGenerator();
masterHashGen.SetKeyFlags(false, PgpKeyFlags.CanSign | PgpKeyFlags.CanCertify);
PgpSignatureSubpacketVector masterHashedPackets = masterHashGen.Generate();
PgpSignatureSubpacketVector unhashedPackets = null;
PgpKeyRingGenerator pgpKeyRingGenerator = new PgpKeyRingGenerator(certificationLevel, signatureKeyPair, id, encAlgorithm, hashAlgorithm, rawPassPhrase, useSha1, masterHashedPackets, unhashedPackets, new Org.BouncyCastle.Security.SecureRandom());
PgpKeyPair encSubKeyPair = CreatePgpKeyPair(encryptionKeyAlg, true);
PgpSignatureSubpacketGenerator encHashGen = new PgpSignatureSubpacketGenerator();
encHashGen.SetKeyFlags(false, PgpKeyFlags.CanEncryptStorage | PgpKeyFlags.CanEncryptStorage);
PgpSignatureSubpacketVector encHashedPackets = encHashGen.Generate();
pgpKeyRingGenerator.AddSubKey(encSubKeyPair, encHashedPackets, null);
PgpKeyPair authSubKeyPair = CreatePgpKeyPair(authenticationKeyAlg, true);
PgpSignatureSubpacketGenerator authHashGen = new PgpSignatureSubpacketGenerator();
authHashGen.SetKeyFlags(false, 0x20); // CanAuth
PgpSignatureSubpacketVector authHashedPackets = authHashGen.Generate();
pgpKeyRingGenerator.AddSubKey(authSubKeyPair, authHashedPackets, null);
return pgpKeyRingGenerator;
}
private PgpKeyPair CreatePgpKeyPair(string algorithm, bool subKey)
{
switch (algorithm)
{
case "RSA-2048":
return CreateRsaKeyPair(2048, PublicKeyAlgorithmTag.RsaGeneral);
case "RSA-3072":
return CreateRsaKeyPair(3072, PublicKeyAlgorithmTag.RsaGeneral);
case "RSA-4096":
return CreateRsaKeyPair(4096, PublicKeyAlgorithmTag.RsaGeneral);
case "EC-256 (nistP256r1)":
return CreateEcKeyPair(EcObjectIdentifiers.NIST_P_256, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
case "EC-256 (brainpoolP256r1)":
return CreateEcKeyPair(EcObjectIdentifiers.BRAINPOOL_P256_R1, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
case "EC-384 (nistP384r1)":
return CreateEcKeyPair(EcObjectIdentifiers.NIST_P_384, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
case "EC-384 (brainpoolP384r1)":
return CreateEcKeyPair(EcObjectIdentifiers.BRAINPOOL_P384_R1, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
case "EC-512 (nistP521r1)":
return CreateEcKeyPair(EcObjectIdentifiers.NIST_P_521, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
case "EC-512 (brainpoolP512r1)":
return CreateEcKeyPair(EcObjectIdentifiers.BRAINPOOL_P512_R1, (subKey ? PublicKeyAlgorithmTag.ECDH : PublicKeyAlgorithmTag.ECDsa));
default:
throw new Exception("invalid algorithm: " + algorithm);
}
}
private PgpKeyPair CreateRsaKeyPair(int length, PublicKeyAlgorithmTag tag)
{
DateTime currentUtcDt = DateTime.UtcNow;
var rsaKeyGen = GeneratorUtilities.GetKeyPairGenerator("RSA");
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(new SecureRandom(), length);
rsaKeyGen.Init(keyGenerationParameters);
PgpKeyPair pgpKeyPair = new PgpKeyPair(tag, rsaKeyGen.GenerateKeyPair(), currentUtcDt);
return pgpKeyPair;
}
private PgpKeyPair CreateEcKeyPair(DerObjectIdentifier oid, PublicKeyAlgorithmTag tag)
{
DateTime currentUtcDt = DateTime.UtcNow;
ECDomainParameters eCDomainParameters = new ECDomainParameters(ECNamedCurveTable.GetByOid(oid));
var ecdhKeyGen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
ecdhKeyGen.Init(new ECKeyGenerationParameters(eCDomainParameters, new SecureRandom()));
var masterKeyPair = ecdhKeyGen.GenerateKeyPair();
ECPublicKeyParameters masterPk = (ECPublicKeyParameters)masterKeyPair.Public;
masterPk = new ECPublicKeyParameters("ECDH", masterPk.Q, oid); // need to be remake, so we can add the curveOid parameter, otherwise it will cause null error during public key packet encoding
PgpKeyPair masterPgpKeyPair = new PgpKeyPair(tag, masterPk, masterKeyPair.Private, currentUtcDt); // need to use ECDsa otherwise if using ECDH it will cause Signature generation error at PgpKeyRingGenerator
return masterPgpKeyPair;
}
Example calling the program:
// rsa
CreatePgpKeyRingGenerator("linrsa@mail.com", "RSA-2048", "RSA-2048", "RSA-2048");
// ec
CreatePgpKeyRingGenerator("lin@mail.com", "EC-256 (brainpoolP256r1)", "EC-256 (brainpoolP256r1)", "EC-256 (brainpoolP256r1)");
With the code above when using RSA type of keys, it can be successfully imported in Kleopatra, see the screenshot below:
But when creating EC type of keys, e.g: using brainpoolP256r1 the import show hash warning, and i dont know which part to edit in the above code, also only the master key is imported in Kleopatra and the subkeys is not. See the screenshots below:
Any suggestion is appreciated.