RSADecrypter with Android keystore private key

Issue #262 resolved
Andy Wong created an issue

RSADecrypter with Android keystore private key

I'm developing an Android app (on Android 5.1) using Nimbus JWE to communicate with our server. It works fine in prototype using key files from file system. But once we migrated it to use Android keystore, RSADecrypter fails to work anymore. It always throw "com.nimbusds.jose.JOSEException: private exponent cannot be extracted" when trying to call the decrypt function with the private key from the keystore.

        KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
        ks.load(null);
        PrivateKey privateKey = (PrivateKey) ks.getKey(KS_ALIAS,null);

        decrypter = new RSADecrypter(privateKey);
        jweObject.decrypt(decrypter);
W/System.err: com.nimbusds.jose.JOSEException: private exponent cannot be extracted
W/System.err:     at com.nimbusds.jose.crypto.RSA_OAEP_256.decryptCEK(RSA_OAEP_256.java:119)
W/System.err:     at com.nimbusds.jose.crypto.RSADecrypter.decrypt(RSADecrypter.java:242)
W/System.err:     at com.nimbusds.jose.JWEObject.decrypt(JWEObject.java:415)
W/System.err:     at com.gzplanet.xposed.myapplication.MainActivity.decodeJWT(MainActivity.java:383)
W/System.err:     at com.gzplanet.xposed.myapplication.MainActivity$5.onClick(MainActivity.java:124)
W/System.err:     at android.view.View.performClick(View.java:4780)
W/System.err:     at android.view.View$PerformClick.run(View.java:19866)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:135)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5254)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
W/System.err: Caused by: java.lang.UnsupportedOperationException: private exponent cannot be extracted
W/System.err:     at com.android.org.conscrypt.OpenSSLRSAPrivateKey.getPrivateExponent(OpenSSLRSAPrivateKey.java:161)
W/System.err:     at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil.generatePrivateKeyParameter(RSAUtil.java:63)
W/System.err:     at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:271)
W/System.err:     at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineInit(CipherSpi.java:368)
W/System.err:     at javax.crypto.Cipher.init(Cipher.java:842)
W/System.err:     at javax.crypto.Cipher.init(Cipher.java:793)
W/System.err:     at com.nimbusds.jose.crypto.RSA_OAEP_256.decryptCEK(RSA_OAEP_256.java:110)
W/System.err:   ... 14 more

Comments (6)

  1. Vladimir Dzhuvinov

    You will need to configured the decrypter with the same crypto provider as the one for the KeyStore:

    decrypter = new RSADecrypter(privateKey);
    decrypter.getJCAContext().setKeyEncryptionProvider(provider);
    
  2. Andy Wong reporter

    It turns out AndroidOpenSSL doesn't support RSA_OAEP_256. So when RSA_OAEP_256 is used, Android selects BouncyCastle as the provider which is not allowed to use the keystore private key. When I use RSA1_5 (which is supported by AndroidOpenSSL), the payload is encrypted and signed successfully.

  3. Omar Joya

    Using RSA_OAEP_256 and setting decrypter.jcaContext.keyEncryptionProvider = keyStore.provider

    throws

    com.nimbusds.jose.JOSEException: no such algorithm: OAEP for provider AndroidKeyStore

    And using RSA1_5 throws

    com.nimbusds.jose.JOSEException: MAC check failed

  4. Henrik Hall

    Hi @Vladimir Dzhuvinov and others,

    This issue was marked as resolved almost two years ago; yet it seems impossible to use the Android KeyStore together with Nimbus JOSE? I have the same errors as Omar above.

    If I set the encryption provider as per Vladimir’s earlier comment, I get “No such algorithm: OAEP for provider AndroidKeyStore”.

    If I remove that line, I get a bit further, because `Cipher.getInstance()` doesn’t work on Android if the provider is passed (!), but get stuck on that Android only supports SHA-1 for MGF1, and nimbus-jose uses SHA-256.

    The only workaround I’ve found so far is to create my own custom sub-class of `RSAEncrypter` and `RSADecrypter` to override `encrypt()` / `decrypt()`, respectively, to use SHA-1 instead of SHA-256, which is not a very pretty solution.

    Are there any plans on getting this to work on Android out of the box? Alternatively, some refactoring of the static bindings to the algorithms inside `RSAEncrypter` and `RSADecrypter` would be nice (even extracting a `protected encryptCEK(…)` or even better `protected getCekCipher()` would simplify overriding the cipher parameters, so that custom algorithms could be more easily introduced by clients?

  5. Log in to comment