cisco / node-jose

Apache License 2.0
698 stars 123 forks source link

RSA-OAEP-256 with MGF1 for compatibility with Android #283

Open henhal opened 4 years ago

henhal commented 4 years ago

Hi,

I'm working on a solution where JWE is needed between an Android app and a backend, using node-jose. I've been struggling a lot since it seems there is no out of the box compatibility between Android - if you want to use the HW backed keystore - and any node JWE library when it comes to the RSA key, because:

OAEP using SHA-1 might work, but it's discouraged by many parties. For example, the most popular Java library Nimbus JOSE has deprecated it.

In order to support JWE with an Android app using the algorithm recommended by Google, RSA/ECB/OAEPWithSHA-256AndMGF1Padding (with SHA-1 for MGF1) would have to be supported by node-jose. Now, node-jose uses node-forge which can handle this mode, but there's no way to request it. Either the alg would have to use special values to indicate the mask generation function, such as RSA-OAEP-256-MGF1-SHA1, but I think the alg values are standardized in the JWT specs, or there would have to be a way to pass custom params to the encrypter - or the JWK - such as {mgf1: {md: 'SHA-1'}}.

I might be able to help out with a PR, but I wanted to hear your thoughts on how this best would be implemented?

panva commented 4 years ago

Since you're mentioning a backend i assume your runtime is Node.js, in which case you can use RSA-OAEP-256 out of the box with https://www.npmjs.com/package/jose when Node.js >= 12.9.0 runtime is detected

henhal commented 4 years ago

Thanks @panva. I tried this out but with no success for RSA-OAEP-256 with MGF1/SHA-1. I can't see any option for setting MGF with jose either?

panva commented 4 years ago

Then maybe i misunderstood what you're looking for. JWA Algorithm already defines what encryption is used as follows

And there's no way to define anything else than the JWA algorithm name when it comes to RSA-OAEP(-*)

Now i assumed

But you're saying that RSA/ECB/OAEPWithSHA-256AndMGF1Padding applies MGF1 with SHA-1 mask generation function?

~That doesn't seem to be the case.~ Edit: It is by default, on how to control it, see below line.

But if so, the only interoperable algorithm of this family would be RSA-OAEP / RSA/ECB/OAEPWithSHA-1AndMGF1Padding which FWIW is only deprecated in Nimbus JOSE, not in the IANA registry, because it uses sha1.

RSA-OAEP-256 does not use sha-1, so i guess if you want to follow that deprecation reason what you ought to be looking for is to tell android NOT to use SHA-1 in MGF1 when RSA/ECB/OAEPWithSHA-256AndMGF1Padding is used.


Edit: This is how one would do that using https://developer.android.com/reference/javax/crypto/spec/OAEPParameterSpec?hl=en

   Cipher cipher =
Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
   OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1",
MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
   cipher.init(Cipher.WRAP_MODE, publicKey, oaepSpec);

I found this on the JOSE mailing list.

henhal commented 4 years ago

Thanks! Yes, in Java (or at least on Android), RSA/ECB/OAEPWithSHA-256AndMGF1Padding implies OAEP SHA-256 but MGF1 with SHA-1. :( I made another attempt now using OAEP-256 with MGF1/SHA-256 on my Android 9 phone, and it gives:

java.security.InvalidAlgorithmParameterException: Unsupported MGF1 digest: SHA-256. Only SHA-1 supported
at android.security.keystore.AndroidKeyStoreRSACipherSpi$OAEPWithMGF1Padding.initAlgorithmSpecificParameters(AndroidKeyStoreRSACipherSpi.java:228)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:147)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2980)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1288)
at javax.crypto.Cipher.init(Cipher.java:1223)

There are many SO posts etc about this, and the solution always presented is to specify the following AlgorithmParameterSpec:

new OAEPParameterSpec(
  "SHA-256", 
  "MGF1", 
  MGF1ParameterSpec.SHA1, 
  PSource.PSpecified.DEFAULT)

See for example https://issuetracker.google.com/issues/36708951#comment15

In that case the only interoperable algorithm of this family would be RSA-OAEP.

Yes, I'd have to agree. But regarding my statement about it being discouraged:

the discouraged method is Key Encryption with RSAES-PKCS1-v1_5, which matches the line from Nimbus JOSE readme and the JWA algorithm name RSA1_5

I based my statement on the fact that JWEAlgorithm.RSA_OAEP is deprecated

/**
 * RSAES using Optimal Asymmetric Encryption Padding (OAEP) (RFC 3447),
 * with the default parameters specified by RFC 3447 in section A.2.1.
 * Use of this encryption algorithm is no longer recommended, use
 * {@link #RSA_OAEP_256} instead.
 */
@Deprecated
public static final JWEAlgorithm RSA_OAEP = new JWEAlgorithm("RSA-OAEP", Requirement.OPTIONAL);

But as you said, it seems using RSA-OAEP is the only way forward to be compatible with Android. It was just that this deprecation made be a bit concerned.