Is there a way to tell Jose4J not to generate and send an IV but use the one returned by the underlying cryptographic provider?

Issue #184 on hold
sai Lakshmi created an issue

We are planning to use Jose4J to generate and exchange JWEs with partners. Our keys and certs are secured within a HSM (Luna 7.4 Network HSM). We see that Jose4J generates a IV (initialization vector) and sends this to the underlying cryptographic provider. However in case of HSM's that are deployed in a FIPS mode, the HSM rejects any IV sent by the caller and fails with an invalid parameter error. In FIPS mode, the IV is generated within the HSM and is sent back as a response. See here for more information - https://thalesdocs.com/gphsm/luna/7.4/docs/network/Content/sdk/mechanisms/CKM_AES_GCM.htm

Comments (4)

  1. Brian Campbell repo owner

    In short, no - jose4j doesn’t provide anything to control that behaviour and the way it uses the JCA APIs is to always provide the IV via a parameter spec when initializing the cipher.

    Perhaps one of the AES-CBC+HMAC encryption algs would work? The luna doc makes no mention of IV treatment like it does for GCM https://thalesdocs.com/gphsm/luna/7.4/docs/network/Content/sdk/mechanisms/CKM_AES_CBC.htm

    Or maybe you could pass a ProviderContext https://www.javadoc.io/doc/org.bitbucket.b_c/jose4j/latest/org/jose4j/jca/ProviderContext.html to the JsonWebEncryption object to dictate that the asymmetric operations happen with the HSM provider but that the symmetric happen in software?

  2. sai Lakshmi reporter

    Thank you for your input. We tried by providing ProviderContext to JsonWebEncryption.

    We are using LunaProvider with HSM. We are trying to use JOSE4j library to perform JWE operations. We want KeyEncryption to happen inside HSM and ContentEncryption in software. So we set jwe.getSuppliedKeyProviderContext().setCipherProvider(“LunaProvider”)

    Please find the below code snippet.

    import com.safenetinc.luna.LunaSlotManager;
    import com.safenetinc.luna.provider.LunaProvider;
    import org.jose4j.jca.ProviderContext;
    import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
    import org.jose4j.jwe.JsonWebEncryption;
    import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.security.*;
    import java.security.cert.CertificateException;

    public class Jose4jTest {
    private static final int slot = 10;
    private static final String passwd = "example";

    public static void main(String[] args) throws Exception{
    
        //Connecting to Luna HSM
        LunaSlotManager slotManager = LunaSlotManager.getInstance();
        slotManager.setSecretKeysDerivable( false );
        slotManager.setSecretKeysExtractable(false);
        slotManager.login("label", passwd);
    
        KeyStore myStore = null;
        try {
            Security.addProvider(new LunaProvider());
            ByteArrayInputStream is1 = new ByteArrayInputStream(("slot:" + slot).getBytes());
            myStore = KeyStore.getInstance("Luna");
            myStore.load(is1, passwd.toCharArray());
        } catch (KeyStoreException kse) {
            System.out.println("Unable to create keystore object");
            kse.printStackTrace();
        } catch (NoSuchAlgorithmException nsae) {
            System.out.println("Unexpected NoSuchAlgorithmException while loading keystore");
            nsae.printStackTrace();
        } catch (CertificateException e) {
            System.out.println("Unexpected CertificateException while loading keystore");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("Unexpected IOException while loading keystore.");
            e.printStackTrace();
        }
    
        //retrieving public key from HSM (doesn't return actual key but returns only handle to key)
        Key publicKey = myStore.getCertificate("xyz").getPublicKey();
        //retrieving private key from HSM (doesn't return actual key but returns only handle to key)
        Key privateKey = myStore.getKey("xyz",passwd.toCharArray());
        testJWERSAEncryptANDDecrypt(publicKey,privateKey);
    
    }
    
    /**
     * To test JWE encryption and decryption with KeyManagement algorithm RSA_OAEP_256 and ContentEncryption algorithm AES_256_GCM
     * @param publicKey public key handle fetched from HSM
     * @param privateKey private key handle fetched from HSM (As HSM doesn't return actual keys outside, we set jwe.setDoKeyValidation to 'false' )
     * @throws Exception
     */
    
    
    public static void testJWERSAEncryptANDDecrypt(Key publicKey, Key privateKey) throws Exception{
    
        String message = "Hello JOSE4j";
    
        ProviderContext senderProviderContext = new ProviderContext();
        senderProviderContext.getSuppliedKeyProviderContext().setCipherProvider("LunaProvider");
    
        JsonWebEncryption senderJwe = new JsonWebEncryption();
        senderJwe.setProviderContext(senderProviderContext);
        senderJwe.setPlaintext(message);
        senderJwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP_256);
        senderJwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
        senderJwe.setDoKeyValidation(false);
        senderJwe.setKey(publicKey);
        String compactSerialization = senderJwe.getCompactSerialization();
        System.out.println("JWE compact serialization: " + compactSerialization);
    
        ProviderContext receiverProviderContext = new ProviderContext();
        receiverProviderContext.getSuppliedKeyProviderContext().setCipherProvider("LunaProvider");
    
        JsonWebEncryption receiverJwe = new JsonWebEncryption();
        receiverJwe.setCompactSerialization(compactSerialization);
        receiverJwe.setProviderContext(receiverProviderContext);
        receiverJwe.setKey(privateKey);
        receiverJwe.setDoKeyValidation(false);
        String plaintext = receiverJwe.getPlaintextString();
        System.out.println("plaintext: " + plaintext);
    
    }
    

    }

    Output of code snippet:
    JWE compact serialization: eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.gjAVFwLykPwaCDR26-FW3i941vsS_ApzQcysOpK83rOTRVCEVXvOXGqpTzw56ubyn2phxICF-rkP6vmhjx7-yNJlsNaQ8iFBkZxeTK3NYi93A-073n5oUXMaF6ryMiYYq2zzxlQtWhX1Y_tElhNu7bimp2WxwHNcGNQXiS2ui-T30L8sSR7lEVoaV7vM1X-dazUl0H2yplDb0xoI_vOIfHS7nphf73vQPh_Q7usGAp5vyu-uMCuhgfxysl9DbEaXDeLXdGXAuS2n11emG-6v7sNVydWHk3sE1HzHvSeghA6TYaA5mZIndySDM3BpOXlJDxuRY6XLVrs6aPAMomb1dQ.4I0nupL4vFQ0_kW5.cUk_SWkTyFqfrCQh.5MyYPJcjUXlx1PSMJUbYAA

    We are able to encrypt, but unable to decrypt. Getting below error.

    Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:109)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
    Caused by: org.jose4j.lang.JoseException: javax.crypto.AEADBadTagException: Tag mismatch!
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:165)
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:152)
    at org.jose4j.jwe.AesGcmContentEncryptionAlgorithm.decrypt(AesGcmContentEncryptionAlgorithm.java:79)
    at org.jose4j.jwe.JsonWebEncryption.decrypt(JsonWebEncryption.java:249)
    at org.jose4j.jwe.JsonWebEncryption.getPlaintextBytes(JsonWebEncryption.java:85)
    at org.jose4j.jwe.JsonWebEncryption.getPlaintextString(JsonWebEncryption.java:78)
    at Jose4jTest.testJWEEncryptANDDecrypt(JoseDirTest.java:139)

    Caused by: javax.crypto.AEADBadTagException: Tag mismatch!
    at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:620)
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
    at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
    at javax.crypto.Cipher.doFinal(Cipher.java:2168)
    at org.jose4j.jwe.SimpleAeadCipher.decrypt(SimpleAeadCipher.java:161)
    ... 16 more

    We are able to decrypt the above JWE using Nimbus Library.
    <!-- https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt -->
    <dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>9.0.1</version>
    </dependency>

    Here 'compactSerialization' is the output of encrypt operation of JOSE4j library.

    JWEObject jweObject = JWEObject.parse(compactSerialization);
    JWEDecrypter decrypter = new RSADecrypter((PrivateKey) privateKey);
    decrypter.getJCAContext().setKeyEncryptionProvider(new LunaProvider());
    jweObject.decrypt(decrypter);
    String nimbusOutput = new String(jweObject.getPayload().toBytes());
    System.out.println("Nimbus output: "+nimbusOutput);

    Could you please let us know why we are getting above 'Tag mismatch!' error when using JOSE4j library.

  3. Brian Campbell repo owner

    Troubleshooting something like this without the private key or access to an HSM is difficult at best. So there’s a lot of guessing and speculation from me here.

    The tag mismatch is saying the integrity check on the symmetric encrypted content is failing during the decryption process. Unfortunately the actual reason behind a tag mismatch can vary and is sometimes due to problems unwrapping/decrypting the symmetric content encryption key. Are you seeing a debug log statement with something like “Key unwrap failed. Substituting a randomly generated CEK and proceeding.” ?

    Glancing a bit at the nimbus code around OAEP and it looks like the algorithm parameters are done somewhat differently. My best guess is that there’s some issue somewhere in the interaction between the alg parameters and the provider. Although is odd that the luna provider was set for jose4j on both encrypt and decrypt operations but that it can’t decrypt the JWE that it produced. I’m not sure what to make of that. It raises a different question though - which is why use the HSM for encryption? Typically with public key encryption some other party uses your public key to encrypt something to you.

    Anyway, back to the algorithm parameters. Can you try using RSA-OAEP rather than RSA-OAEP-256? The former doesn’t use any algorithm parameters when setting up the Cipher object so maybe would avoid issues that might be occurring there.

  4. Log in to comment