It appears that while the PGPPublicKey
class offers an isEncryptionKey()
method to determine if a public key's algorithm is acceptable for encryption purposes (RSA_GENERAL
, RSA_ENCRYPT
, ELGAMAL_GENERAL
, ELGAMAL_ENCRYPT
) this alone is not sufficient for selecting a valid encryption subkey.
There is information regarding the intended usage of the public key stored within the packet, as seen in GnuPG packet.h:
41 /* Usage flags */
42 #define PUBKEY_USAGE_SIG GCRY_PK_USAGE_SIGN /* Good for signatures. */
43 #define PUBKEY_USAGE_ENC GCRY_PK_USAGE_ENCR /* Good for encryption. */
44 #define PUBKEY_USAGE_CERT GCRY_PK_USAGE_CERT /* Also good to certify keys.*/
45 #define PUBKEY_USAGE_AUTH GCRY_PK_USAGE_AUTH /* Good for authentication. */
46 #define PUBKEY_USAGE_UNKNOWN GCRY_PK_USAGE_UNKN /* Unknown usage flag. */
47 #define PUBKEY_USAGE_NONE 256 /* No usage given. */
My question is, given that Bouncy Castle does not expose these flags, what is the suggested way to extract this key usage information from a PublicKeyPacket in Java?
I figured it out. For posterity, here's the solution:
// If Key Usage flags are present, we must respect them:
int keyFlagsEncountered = 0;
boolean keyUsageAllowsEncryption = false;
Iterator<PGPSignature> i = key.getSignatures();
while(i.hasNext()) {
PGPSignature signature = i.next();
int keyFlags = signature.getHashedSubPackets().getKeyFlags();
keyFlagsEncountered += keyFlags;
boolean isEncryptComms = (keyFlags & KeyFlags.ENCRYPT_COMMS) > 0;
boolean isEncryptStorage = (keyFlags & KeyFlags.ENCRYPT_STORAGE) > 0;
// Other KeyFlags available here (AUTHENTICATION, SIGN_DATA, CERTIFY_OTHER).
if (isEncryptComms || isEncryptStorage) {
keyUsageAllowsEncryption = true;
}
}
// However, if Key Usage flags are not present (older key, or key generation process simply did not include the flags)
// then we still attempt to use an encryption key using the existing methods:
keyUsageAllowsEncryption = keyFlagsEncountered == 0 || keyUsageAllowsEncryption;