From 907f395f041456ea7cde27f468a61ac1c1537c5d Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 6 Jul 2022 13:45:59 -0400 Subject: [PATCH] Use OS-provided RSA OAEP implementation for Android Instead of using the managed OAEP implementatation, allow the OS to handle OAEP with SHA2 algorithms. Android also supports algorithm identifiers like "RSA/ECB/OAEPwithSHA-256andMGF1Padding" which would make the code simpler, but it makes the hash algorithm used in MGF1 ambiguous. Since the MGF1 algorithm is always the same as the OAEP algorithm, we explicitly use the `OEAPParameterSpec` so that we can control both the MGF1 digest algorithm and the OAEP digest algorithm. --- .../Interop.Rsa.cs | 4 +- .../Security/Cryptography/RSAAndroid.cs | 118 ++++-------------- .../Cryptography/RsaPaddingProcessor.cs | 99 --------------- .../pal_jni.c | 27 ++++ .../pal_jni.h | 15 +++ .../pal_rsa.c | 113 ++++++++++++++--- .../pal_rsa.h | 4 +- 7 files changed, 173 insertions(+), 207 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs index 9aef7f78b94df..ac6f04bc41f72 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs @@ -197,7 +197,9 @@ internal enum RsaPadding : int { Pkcs1 = 0, OaepSHA1 = 1, - NoPadding = 2, + OaepSHA256 = 2, + OaepSHA384 = 3, + OaepSHA512 = 4, } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs index a9c7833021e5a..60b5f40c89ec2 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs @@ -78,7 +78,7 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(padding); - Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out HashAlgorithmName? oaepHashAlgorithmName); + Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding); SafeRsaHandle key = GetKey(); int rsaSize = Interop.AndroidCrypto.RsaSize(key); @@ -89,7 +89,7 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { destination = new Span(buf, 0, rsaSize); - if (!TryDecrypt(key, data, destination, rsaPadding, oaepHashAlgorithmName, out int bytesWritten)) + if (!TryDecrypt(key, data, destination, rsaPadding, out int bytesWritten)) { Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer"); throw new CryptographicException(); @@ -112,7 +112,7 @@ public override bool TryDecrypt( { ArgumentNullException.ThrowIfNull(padding); - Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out HashAlgorithmName? oaepHashAlgorithmName); + Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding); SafeRsaHandle key = GetKey(); int keySizeBytes = Interop.AndroidCrypto.RsaSize(key); @@ -135,7 +135,7 @@ public override bool TryDecrypt( tmp = rent; } - bool ret = TryDecrypt(key, data, tmp, rsaPadding, oaepHashAlgorithmName, out bytesWritten); + bool ret = TryDecrypt(key, data, tmp, rsaPadding, out bytesWritten); if (ret) { @@ -163,7 +163,7 @@ public override bool TryDecrypt( return ret; } - return TryDecrypt(key, data, destination, rsaPadding, oaepHashAlgorithmName, out bytesWritten); + return TryDecrypt(key, data, destination, rsaPadding, out bytesWritten); } private static bool TryDecrypt( @@ -171,15 +171,8 @@ private static bool TryDecrypt( ReadOnlySpan data, Span destination, Interop.AndroidCrypto.RsaPadding rsaPadding, - HashAlgorithmName? oaepHashAlgorithmName, out int bytesWritten) { - // If rsaPadding is PKCS1 or OAEP-SHA1 then no depadding method should be present. - // If rsaPadding is NoPadding then a depadding method should be present. - Debug.Assert( - (rsaPadding == Interop.AndroidCrypto.RsaPadding.NoPadding) == - (oaepHashAlgorithmName != null)); - // Caller should have already checked this. Debug.Assert(!key.IsInvalid); @@ -196,46 +189,10 @@ private static bool TryDecrypt( return false; } - Span decryptBuf = destination; - byte[]? paddingBuf = null; - - if (oaepHashAlgorithmName != null) - { - paddingBuf = CryptoPool.Rent(rsaSize); - decryptBuf = paddingBuf; - } - - try - { - int returnValue = Interop.AndroidCrypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding); - CheckReturn(returnValue); - - if (oaepHashAlgorithmName != null) - { - return RsaPaddingProcessor.DepadOaep(oaepHashAlgorithmName.Value, paddingBuf, destination, out bytesWritten); - } - else - { - // If the padding mode is RSA_NO_PADDING then the size of the decrypted block - // will be RSA_size. If any padding was used, then some amount (determined by the padding algorithm) - // will have been reduced, and only returnValue bytes were part of the decrypted - // body. Either way, we can just use returnValue, but some additional bytes may have been overwritten - // in the destination span. - bytesWritten = returnValue; - } - - return true; - } - finally - { - if (paddingBuf != null) - { - // DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it. - // If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared. - CryptographicOperations.ZeroMemory(decryptBuf); - CryptoPool.Return(paddingBuf, clearSize: 0); - } - } + int returnValue = Interop.AndroidCrypto.RsaPrivateDecrypt(data.Length, data, destination, key, rsaPadding); + CheckReturn(returnValue); + bytesWritten = returnValue; + return true; } public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) @@ -243,7 +200,7 @@ public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(padding); - Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out HashAlgorithmName? oaepHashAlgorithmName); + Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding); SafeRsaHandle key = GetKey(); byte[] buf = new byte[Interop.AndroidCrypto.RsaSize(key)]; @@ -253,7 +210,6 @@ public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) data, buf, rsaPadding, - oaepHashAlgorithmName, out int bytesWritten); if (!encrypted || bytesWritten != buf.Length) @@ -269,10 +225,10 @@ public override bool TryEncrypt(ReadOnlySpan data, Span destination, { ArgumentNullException.ThrowIfNull(padding); - Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out HashAlgorithmName? oaepHashAlgorithmName); + Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding); SafeRsaHandle key = GetKey(); - return TryEncrypt(key, data, destination, rsaPadding, oaepHashAlgorithmName, out bytesWritten); + return TryEncrypt(key, data, destination, rsaPadding, out bytesWritten); } private static bool TryEncrypt( @@ -280,7 +236,6 @@ private static bool TryEncrypt( ReadOnlySpan data, Span destination, Interop.AndroidCrypto.RsaPadding rsaPadding, - HashAlgorithmName? oaepHashAlgorithmName, out int bytesWritten) { int rsaSize = Interop.AndroidCrypto.RsaSize(key); @@ -291,60 +246,39 @@ private static bool TryEncrypt( return false; } - int returnValue; - - if (oaepHashAlgorithmName != null) - { - Debug.Assert(rsaPadding == Interop.AndroidCrypto.RsaPadding.NoPadding); - byte[] rented = CryptoPool.Rent(rsaSize); - Span tmp = new Span(rented, 0, rsaSize); - - try - { - RsaPaddingProcessor.PadOaep(oaepHashAlgorithmName.Value, data, tmp); - returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(tmp.Length, tmp, destination, key, rsaPadding); - } - finally - { - CryptographicOperations.ZeroMemory(tmp); - CryptoPool.Return(rented, clearSize: 0); - } - } - else - { - Debug.Assert(rsaPadding != Interop.AndroidCrypto.RsaPadding.NoPadding); - - returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(data.Length, data, destination, key, rsaPadding); - } - + int returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(data.Length, data, destination, key, rsaPadding); CheckReturn(returnValue); - bytesWritten = returnValue; Debug.Assert(returnValue == rsaSize, $"{returnValue} != {rsaSize}"); return true; } - private static Interop.AndroidCrypto.RsaPadding GetInteropPadding( - RSAEncryptionPadding padding, - out HashAlgorithmName? oaepHashAlgorithmName) + private static Interop.AndroidCrypto.RsaPadding GetInteropPadding(RSAEncryptionPadding padding) { if (padding == RSAEncryptionPadding.Pkcs1) { - oaepHashAlgorithmName = null; return Interop.AndroidCrypto.RsaPadding.Pkcs1; } if (padding == RSAEncryptionPadding.OaepSHA1) { - oaepHashAlgorithmName = null; return Interop.AndroidCrypto.RsaPadding.OaepSHA1; } - if (padding.Mode == RSAEncryptionPaddingMode.Oaep) + if (padding == RSAEncryptionPadding.OaepSHA256) + { + return Interop.AndroidCrypto.RsaPadding.OaepSHA256; + } + + if (padding == RSAEncryptionPadding.OaepSHA384) + { + return Interop.AndroidCrypto.RsaPadding.OaepSHA384; + } + + if (padding == RSAEncryptionPadding.OaepSHA512) { - oaepHashAlgorithmName = padding.OaepHashAlgorithm; - return Interop.AndroidCrypto.RsaPadding.NoPadding; + return Interop.AndroidCrypto.RsaPadding.OaepSHA512; } throw PaddingModeNotSupported(); diff --git a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs index fc084435482e5..aab824eac9ce0 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs @@ -256,105 +256,6 @@ internal static void PadOaep( } } - internal static bool DepadOaep( - HashAlgorithmName hashAlgorithmName, - ReadOnlySpan source, - Span destination, - out int bytesWritten) - { - int hLen = HashLength(hashAlgorithmName); - - // https://tools.ietf.org/html/rfc3447#section-7.1.2 - using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName)) - { - Debug.Assert(hasher.HashLengthInBytes == hLen); - - Span lHash = stackalloc byte[hLen]; - - if (!hasher.TryGetHashAndReset(lHash, out int hLen2) || hLen2 != hLen) - { - Debug.Fail("TryGetHashAndReset failed with exact-size destination"); - throw new CryptographicException(); - } - - int y = source[0]; - ReadOnlySpan maskedSeed = source.Slice(1, hLen); - ReadOnlySpan maskedDB = source.Slice(1 + hLen); - - Span seed = stackalloc byte[hLen]; - // seedMask = MGF(maskedDB, hLen) - Mgf1(hasher, maskedDB, seed); - - // seed = seedMask XOR maskedSeed - Xor(seed, maskedSeed); - - byte[] tmp = CryptoPool.Rent(source.Length); - - try - { - Span dbMask = new Span(tmp, 0, maskedDB.Length); - // dbMask = MGF(seed, k - hLen - 1) - Mgf1(hasher, seed, dbMask); - - // DB = dbMask XOR maskedDB - Xor(dbMask, maskedDB); - - ReadOnlySpan lHashPrime = dbMask.Slice(0, hLen); - - int separatorPos = int.MaxValue; - - for (int i = dbMask.Length - 1; i >= hLen; i--) - { - // if dbMask[i] is 1, val is 0. otherwise val is [01,FF] - byte dbMinus1 = (byte)(dbMask[i] - 1); - int val = dbMinus1; - - // if val is 0: FFFFFFFF & FFFFFFFF => FFFFFFFF - // if val is any other byte value, val-1 will be in the range 00000000 to 000000FE, - // and so the high bit will not be set. - val = (~val & (val - 1)) >> 31; - - // if val is 0: separator = (0 & i) | (~0 & separator) => separator - // else: separator = (~0 & i) | (0 & separator) => i - // - // Net result: non-branching "if (dbMask[i] == 1) separatorPos = i;" - separatorPos = (val & i) | (~val & separatorPos); - } - - bool lHashMatches = CryptographicOperations.FixedTimeEquals(lHash, lHashPrime); - bool yIsZero = y == 0; - bool separatorMadeSense = separatorPos < dbMask.Length; - - // This intentionally uses non-short-circuiting operations to hide the timing - // differential between the three failure cases - bool shouldContinue = lHashMatches & yIsZero & separatorMadeSense; - - if (!shouldContinue) - { - throw new CryptographicException(SR.Cryptography_OAEP_Decryption_Failed); - } - - Span message = dbMask.Slice(separatorPos + 1); - - if (message.Length <= destination.Length) - { - message.CopyTo(destination); - bytesWritten = message.Length; - return true; - } - else - { - bytesWritten = 0; - return false; - } - } - finally - { - CryptoPool.Return(tmp, source.Length); - } - } - } - internal static void EncodePss(HashAlgorithmName hashAlgorithmName, ReadOnlySpan mHash, Span destination, int keySize) { int hLen = HashLength(hashAlgorithmName); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index fe92ce9ebe3eb..aac61e9bf9d68 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -92,6 +92,21 @@ jmethodID g_sslCtxGetDefaultSslParamsMethod; jclass g_GCMParameterSpecClass; jmethodID g_GCMParameterSpecCtor; +// java/security/spec/MGF1ParameterSpec +jclass g_MGF1ParameterSpecClass; +jfieldID g_MGF1ParameterSpec_SHA1Field; +jfieldID g_MGF1ParameterSpec_SHA256Field; +jfieldID g_MGF1ParameterSpec_SHA384Field; +jfieldID g_MGF1ParameterSpec_SHA512Field; + +// javax/crypto/spec/OAEPParameterSpec +jclass g_OAEPParameterSpecClass; +jmethodID g_OAEPParameterSpecCtor; + +// javax/crypto/spec/PSource$PSpecified +jclass g_PSourcePSpecifiedClass; +jfieldID g_PSourcePSpecified_DefaultField; + // java/security/interfaces/DSAKey jclass g_DSAKeyClass; @@ -692,6 +707,18 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_GCMParameterSpecClass = GetClassGRef(env, "javax/crypto/spec/GCMParameterSpec"); g_GCMParameterSpecCtor = GetMethod(env, false, g_GCMParameterSpecClass, "", "(I[B)V"); + g_MGF1ParameterSpecClass = GetClassGRef(env, "java/security/spec/MGF1ParameterSpec"); + g_MGF1ParameterSpec_SHA1Field = GetField(env, true, g_MGF1ParameterSpecClass, "SHA1", "Ljava/security/spec/MGF1ParameterSpec;"); + g_MGF1ParameterSpec_SHA256Field = GetField(env, true, g_MGF1ParameterSpecClass, "SHA256", "Ljava/security/spec/MGF1ParameterSpec;"); + g_MGF1ParameterSpec_SHA384Field = GetField(env, true, g_MGF1ParameterSpecClass, "SHA384", "Ljava/security/spec/MGF1ParameterSpec;"); + g_MGF1ParameterSpec_SHA512Field = GetField(env, true, g_MGF1ParameterSpecClass, "SHA512", "Ljava/security/spec/MGF1ParameterSpec;"); + + g_OAEPParameterSpecClass = GetClassGRef(env, "javax/crypto/spec/OAEPParameterSpec"); + g_OAEPParameterSpecCtor = GetMethod(env, false, g_OAEPParameterSpecClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/security/spec/AlgorithmParameterSpec;Ljavax/crypto/spec/PSource;)V"); + + g_PSourcePSpecifiedClass = GetClassGRef(env, "javax/crypto/spec/PSource$PSpecified"); + g_PSourcePSpecified_DefaultField = GetField(env, true, g_PSourcePSpecifiedClass, "DEFAULT", "Ljavax/crypto/spec/PSource$PSpecified;"); + g_bigNumClass = GetClassGRef(env, "java/math/BigInteger"); g_bigNumCtor = GetMethod(env, false, g_bigNumClass, "", "([B)V"); g_bigNumCtorWithSign = GetMethod(env, false, g_bigNumClass, "", "(I[B)V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index f9e9bc7acf586..f8d89e2513115 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -101,6 +101,21 @@ extern jmethodID g_sslCtxGetDefaultSslParamsMethod; extern jclass g_GCMParameterSpecClass; extern jmethodID g_GCMParameterSpecCtor; +// java/security/spec/MGF1ParameterSpec +extern jclass g_MGF1ParameterSpecClass; +extern jfieldID g_MGF1ParameterSpec_SHA1Field; +extern jfieldID g_MGF1ParameterSpec_SHA256Field; +extern jfieldID g_MGF1ParameterSpec_SHA384Field; +extern jfieldID g_MGF1ParameterSpec_SHA512Field; + +// javax/crypto/spec/OAEPParameterSpec +extern jclass g_OAEPParameterSpecClass; +extern jmethodID g_OAEPParameterSpecCtor; + +// javax/crypto/spec/PSource$PSpecified +extern jclass g_PSourcePSpecifiedClass; +extern jfieldID g_PSourcePSpecified_DefaultField; + // java/security/cert/CertificateFactory extern jclass g_CertFactoryClass; extern jmethodID g_CertFactoryGetInstance; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.c index 91fcc38ffb29e..e212812b2d7eb 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.c @@ -8,6 +8,52 @@ #define RSA_FAIL -1 +static jobject GetRsaOaepPadding(JNIEnv* env, RsaPadding padding) +{ + assert(padding >= OaepSHA1 && padding <= OaepSHA512); + + jobject oaepParameterSpec; + INIT_LOCALS(oaepLocals, oaepDigest, mgf1, mgf1ParameterSpec, pSource); + + oaepLocals[mgf1] = make_java_string(env, "MGF1"); + oaepLocals[pSource] = (*env)->GetStaticObjectField(env, g_PSourcePSpecifiedClass, g_PSourcePSpecified_DefaultField); + + if (padding == OaepSHA1) + { + oaepLocals[mgf1ParameterSpec] = (*env)->GetStaticObjectField(env, g_MGF1ParameterSpecClass, g_MGF1ParameterSpec_SHA1Field); + oaepLocals[oaepDigest] = make_java_string(env, "SHA-1"); + } + else if (padding == OaepSHA256) + { + oaepLocals[mgf1ParameterSpec] = (*env)->GetStaticObjectField(env, g_MGF1ParameterSpecClass, g_MGF1ParameterSpec_SHA256Field); + oaepLocals[oaepDigest] = make_java_string(env, "SHA-256"); + } + else if (padding == OaepSHA384) + { + oaepLocals[mgf1ParameterSpec] = (*env)->GetStaticObjectField(env, g_MGF1ParameterSpecClass, g_MGF1ParameterSpec_SHA384Field); + oaepLocals[oaepDigest] = make_java_string(env, "SHA-384"); + } + else if (padding == OaepSHA512) + { + oaepLocals[mgf1ParameterSpec] = (*env)->GetStaticObjectField(env, g_MGF1ParameterSpecClass, g_MGF1ParameterSpec_SHA512Field); + oaepLocals[oaepDigest] = make_java_string(env, "SHA-512"); + } + else + { + RELEASE_LOCALS(oaepLocals, env); + return FAIL; + } + + oaepParameterSpec = (*env)->NewObject( + env, + g_OAEPParameterSpecClass, + g_OAEPParameterSpecCtor, + oaepLocals[oaepDigest], oaepLocals[mgf1], oaepLocals[mgf1ParameterSpec], oaepLocals[pSource]); + + RELEASE_LOCALS(oaepLocals, env); + return CheckJNIExceptions(env) ? FAIL : oaepParameterSpec; +} + PALEXPORT RSA* AndroidCryptoNative_RsaCreate() { RSA* rsa = xcalloc(1, sizeof(RSA)); @@ -50,26 +96,35 @@ PALEXPORT int32_t AndroidCryptoNative_RsaPublicEncrypt(int32_t flen, uint8_t* fr if ((flen > 0 && !from) || flen < 0) return RSA_FAIL; + if (padding < Pkcs1 || padding > OaepSHA512) + return RSA_FAIL; + JNIEnv* env = GetJNIEnv(); int32_t ret = RSA_FAIL; INIT_LOCALS(loc, algName, cipher, fromBytes, encryptedBytes); + jobject oaepParameterSpec = NULL; if (padding == Pkcs1) { loc[algName] = make_java_string(env, "RSA/ECB/PKCS1Padding"); - } - else if (padding == OaepSHA1) - { - loc[algName] = make_java_string(env, "RSA/ECB/OAEPPadding"); + loc[cipher] = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, loc[algName]); + (*env)->CallVoidMethod(env, loc[cipher], g_cipherInit2Method, CIPHER_ENCRYPT_MODE, rsa->publicKey); } else { - loc[algName] = make_java_string(env, "RSA/ECB/NoPadding"); + loc[algName] = make_java_string(env, "RSA/ECB/OAEPPadding"); + loc[cipher] = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, loc[algName]); + oaepParameterSpec = GetRsaOaepPadding(env, padding); + + if (oaepParameterSpec == FAIL) + { + goto cleanup; + } + + (*env)->CallVoidMethod(env, loc[cipher], g_cipherInitMethod, CIPHER_ENCRYPT_MODE, rsa->publicKey, oaepParameterSpec); } - loc[cipher] = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, loc[algName]); - (*env)->CallVoidMethod(env, loc[cipher], g_cipherInit2Method, CIPHER_ENCRYPT_MODE, rsa->publicKey); loc[fromBytes] = make_java_byte_array(env, flen); (*env)->SetByteArrayRegion(env, loc[fromBytes], 0, flen, (jbyte*)from); loc[encryptedBytes] = (jbyteArray)(*env)->CallObjectMethod(env, loc[cipher], g_cipherDoFinal2Method, loc[fromBytes]); @@ -82,6 +137,12 @@ PALEXPORT int32_t AndroidCryptoNative_RsaPublicEncrypt(int32_t flen, uint8_t* fr cleanup: RELEASE_LOCALS(loc, env); + + if (oaepParameterSpec != NULL && oaepParameterSpec != FAIL) + { + (*env)->DeleteLocalRef(env, oaepParameterSpec); + } + return ret; } @@ -93,21 +154,39 @@ PALEXPORT int32_t AndroidCryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* f if (!rsa->privateKey) return RSA_FAIL; + if (padding < Pkcs1 || padding > OaepSHA512) + return RSA_FAIL; + abort_if_invalid_pointer_argument (to); abort_if_invalid_pointer_argument (from); JNIEnv* env = GetJNIEnv(); - + jobject cipher; jobject algName; + jobject oaepParameterSpec = NULL; + if (padding == Pkcs1) - algName = make_java_string(env, "RSA/ECB/PKCS1Padding"); // TODO: is ECB needed here? - else if (padding == OaepSHA1) - algName = make_java_string(env, "RSA/ECB/OAEPPadding"); + { + algName = make_java_string(env, "RSA/ECB/PKCS1Padding"); + cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName); + (*env)->CallVoidMethod(env, cipher, g_cipherInit2Method, CIPHER_DECRYPT_MODE, rsa->privateKey); + } else - algName = make_java_string(env, "RSA/ECB/NoPadding"); + { + algName = make_java_string(env, "RSA/ECB/OAEPPadding"); + cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName); + oaepParameterSpec = GetRsaOaepPadding(env, padding); + + if (oaepParameterSpec == FAIL) + { + (*env)->DeleteLocalRef(env, algName); + (*env)->DeleteLocalRef(env, cipher); + return RSA_FAIL; + } + + (*env)->CallVoidMethod(env, cipher, g_cipherInitMethod, CIPHER_DECRYPT_MODE, rsa->privateKey, oaepParameterSpec); + } - jobject cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName); - (*env)->CallVoidMethod(env, cipher, g_cipherInit2Method, CIPHER_DECRYPT_MODE, rsa->privateKey); jbyteArray fromBytes = make_java_byte_array(env, flen); (*env)->SetByteArrayRegion(env, fromBytes, 0, flen, (jbyte*)from); jbyteArray decryptedBytes = (jbyteArray)(*env)->CallObjectMethod(env, cipher, g_cipherDoFinal2Method, fromBytes); @@ -117,6 +196,7 @@ PALEXPORT int32_t AndroidCryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* f (*env)->DeleteLocalRef(env, cipher); (*env)->DeleteLocalRef(env, fromBytes); (*env)->DeleteLocalRef(env, algName); + (*env)->DeleteLocalRef(env, oaepParameterSpec); return RSA_FAIL; } @@ -128,6 +208,11 @@ PALEXPORT int32_t AndroidCryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* f (*env)->DeleteLocalRef(env, decryptedBytes); (*env)->DeleteLocalRef(env, algName); + if (oaepParameterSpec != NULL && oaepParameterSpec != FAIL) + { + (*env)->DeleteLocalRef(env, oaepParameterSpec); + } + return (int32_t)decryptedBytesLen; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.h index 63b98d7738c25..13f6113aab4ee 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_rsa.h @@ -10,7 +10,9 @@ typedef enum { Pkcs1 = 0, OaepSHA1 = 1, - NoPadding = 2, + OaepSHA256 = 2, + OaepSHA384 = 3, + OaepSHA512 = 4, } RsaPadding; typedef struct RSA