Skip to content

Problem with pgp decryption and HSM based ECC key #1686

Open

Description

@cbr-zso

I am using bcpg-jdk18on-1.78.1.jar and a CryptoServer HSM JCE (Utimaco) to decrypt PGP data. The private ECC key is stored on HSM, and the JCE provider for HSM is registred in the Java vm. With software ECC keys everything works fine with pgp decryption. I tried to decrypt the PGP session key first and then let BC decrypt the actual payload of the pgp encrypted blob. My code looks like this (taken from BC FIPS 100 examples):

public static byte[] decryptPGPData(PGPPrivateKey encSubPrivateKey,
                                    byte[] pgpEncryptedData) throws Exception {

    PGPObjectFactory pgpFact = new JcaPGPObjectFactory(pgpEncryptedData);
    PGPEncryptedDataList encList = (PGPEncryptedDataList) pgpFact.nextObject();
    PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encList.get(0);

    PublicKeyDataDecryptorFactory dataDecryptorFactory =
            new JcePublicKeyDataDecryptorFactoryBuilder().build(encSubPrivateKey);
            
    // will fail with HSM based PGPPrivateKey
    PGPSessionKey decryptedSessionKey = encData.getSessionKey(dataDecryptorFactory);

    SessionKeyDataDecryptorFactory sessionKeyDataDecryptorFactor =
            new JceSessionKeyDataDecryptorFactoryBuilder().setProvider("BC").build(decryptedSessionKey);
    InputStream clear = encData.getDataStream(sessionKeyDataDecryptorFactor);

    byte[] literalData = Streams.readAll(clear);
    if (encData.verify()) {
        PGPObjectFactory litFact = new JcaPGPObjectFactory(literalData);
        PGPLiteralData litData = (PGPLiteralData) litFact.nextObject();
        byte[] data = Streams.readAll(litData.getInputStream());
        return data;
    }
    throw new IllegalStateException("modification check failed");
}

Trying the same with a PGPPrivateKey residing within a HSM gives me an exception when calling encData.getSessionKey(encSubPrivateKey) and when instantiating KeyAgreement of the private key associtated HSM JCE:

Exception in thread "main" org.bouncycastle.openpgp.PGPException: error decrypting session data: Unsupported parameter type
	at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.decryptSessionData(JcePublicKeyDataDecryptorFactoryBuilder.java:287)
	at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.access$500(JcePublicKeyDataDecryptorFactoryBuilder.java:50)
	at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder$2.recoverSessionData(JcePublicKeyDataDecryptorFactoryBuilder.java:183)
	at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getSessionKey(PGPPublicKeyEncryptedData.java:101)
	at Z1HsmBcPGPTestEC.decryptPGPData(Z1HsmBcPGPTestEC.java:415)
	at Z1HsmBcPGPTestEC.main(Z1HsmBcPGPTestEC.java:188)
Caused by: java.security.InvalidAlgorithmParameterException: Unsupported parameter type
	at CryptoServerJCE.CryptoServerKeyAgreement.engineInit(CryptoServerKeyAgreement.java:170)
	at java.base/javax.crypto.KeyAgreement.implInit(KeyAgreement.java:356)
	at java.base/javax.crypto.KeyAgreement.chooseProvider(KeyAgreement.java:386)
	at java.base/javax.crypto.KeyAgreement.init(KeyAgreement.java:547)
	at java.base/javax.crypto.KeyAgreement.init(KeyAgreement.java:517)
	at org.bouncycastle.openpgp.operator.jcajce.JcaJcePGPUtil.getSecret(JcaJcePGPUtil.java:102)
	at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.getSessionKey(JcePublicKeyDataDecryptorFactoryBuilder.java:323)
	at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder.decryptSessionData(JcePublicKeyDataDecryptorFactoryBuilder.java:281)

You can see that BC JcaJcePGPUtil.getSecret(JcaJcePGPUtil.java:102) tries to initialize the KeyAgreement with a BC specific KeyAgreement params (UserKeyingMaterialSpec from BC) which doesnt work. Because the KeyAgreement is instantiated on the HSM JCE provider. And th HSM JCE KeyAgreement doesnt know the UserKeyingMaterialSpec

https://github.com/bcgit/bc-java/blob/1.78.1/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaJcePGPUtil.java#L78

image

I tried to to create a workaround and supply a vendor specifc HSM KeyAgreement (CryptoServerSharedInfoParameterSpec) and then the KeyAgreement runs fine. But the next problem is that the resulting symmetric aes key is a HSM JCE key (AES symmetric key on HSM) and cannot be used by later to decrypt with a AES Cipher. Here the AES key must be constructed with SecretKeySpec using the bytes from the KeyAgreement. So the resulting AES key is a BC software key.

My changes to make it work with HSM looks like this:

static Key getSecret(OperatorHelper helper, PublicKey cryptoPublicKey,
                     String keyEncryptionOID, String agreementName,
			  AlgorithmParameterSpec ukmSpec, Key privKey)
    throws GeneralSecurityException
{

    ukmSpec = new CryptoServerSharedInfoParameterSpec(
            ((UserKeyingMaterialSpec) ukmSpec).getUserKeyingMaterial());

    KeyAgreement agreement = helper.createKeyAgreement(agreementName);
    agreement.init(privKey, ukmSpec);
    agreement.doPhase(cryptoPublicKey, true);

    byte[] keyBytes = agreement.generateSecret();
    SecretKey aesKey = new SecretKeySpec(Arrays.copyOfRange(keyBytes, 0, 32), "AES");

    return aesKey;

}

But my change assumes every generated secret key is always AES 256. So its just a quick hack, rather than a final solution.

So I am asking for help for 2 topics here:

  1. Could you extend JcePublicKeyDataDecryptorFactoryBuilder so I can supply the KeyAgreement params (from HSM) outside before decrypting the session key? So i could pass my CryptoServerSharedInfoParameterSpec to it?

  2. Could you change the way the secret session key is generated and explicitly dont generat the Key in HSM provider but as a BC softwarekey with SecretKeySpec classe for example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions