Search code examples
javabouncycastlepgpgnupg

Extracting GPG Key Usage Flags via Bouncy Castle


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?


Solution

  • 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;