[Android] Implement RSA encryption using Android SDK (#45870)
authorEgor Bogatov <egorbo@gmail.com>
Wed, 6 Jan 2021 12:35:51 +0000 (15:35 +0300)
committerGitHub <noreply@github.com>
Wed, 6 Jan 2021 12:35:51 +0000 (15:35 +0300)
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.c [new file with mode: 0644]
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.h [new file with mode: 0644]
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c [new file with mode: 0644]
src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h [new file with mode: 0644]

index 579a9ec..9d380c4 100644 (file)
@@ -12,6 +12,8 @@ set(NATIVECRYPTO_SOURCES
     pal_hmac.c
     pal_bignum.c
     pal_ssl.c
+    pal_rsa.c
+    pal_err.c
 )
 
 add_library(System.Security.Cryptography.Native.Android
index 4aa0463..f390981 100644 (file)
@@ -11,7 +11,7 @@ jobject CryptoNative_BigNumFromBinary(uint8_t* bytes, int32_t len)
     (*env)->SetByteArrayRegion(env, buffArray, 0, len, (jbyte*)bytes);
     jobject bigNum = (*env)->NewObject(env, g_bigNumClass, g_bigNumCtor, buffArray);
     (*env)->DeleteLocalRef(env, buffArray);
-    return CheckJNIExceptions(env) ? FAIL : bigNum;
+    return CheckJNIExceptions(env) ? FAIL : ToGRef(env, bigNum);
 }
 
 int32_t CryptoNative_BigNumToBinary(jobject bignum, uint8_t* output)
diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.c
new file mode 100644 (file)
index 0000000..2db14df
--- /dev/null
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "pal_err.h"
+
+void CryptoNative_ErrClearError()
+{
+}
+
+uint64_t CryptoNative_ErrGetErrorAlloc(int32_t* isAllocFailure)
+{
+    return 0;
+}
+
+uint64_t CryptoNative_ErrPeekError()
+{
+    return 0;
+}
+
+uint64_t CryptoNative_ErrPeekLastError()
+{
+    return 0;
+}
+
+const char* CryptoNative_ErrReasonErrorString(uint64_t error)
+{
+    return "See logcat for more details.";
+}
+
+void CryptoNative_ErrErrorStringN(uint64_t e, char* buf, int32_t len)
+{
+    buf = "See logcat for more details.";
+}
diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.h
new file mode 100644 (file)
index 0000000..ef703f8
--- /dev/null
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "pal_jni.h"
+
+PALEXPORT void CryptoNative_ErrClearError(void);
+PALEXPORT uint64_t CryptoNative_ErrGetErrorAlloc(int32_t* isAllocFailure);
+PALEXPORT uint64_t CryptoNative_ErrPeekError(void);
+PALEXPORT uint64_t CryptoNative_ErrPeekLastError(void);
+PALEXPORT const char* CryptoNative_ErrReasonErrorString(uint64_t error);
+PALEXPORT void CryptoNative_ErrErrorStringN(uint64_t e, char* buf, int32_t len);
index d4ad6f5..3b6482a 100644 (file)
@@ -34,9 +34,11 @@ jmethodID g_sksCtor;
 jclass    g_cipherClass;
 jmethodID g_cipherGetInstanceMethod;
 jmethodID g_cipherDoFinalMethod;
+jmethodID g_cipherDoFinal2Method;
 jmethodID g_cipherUpdateMethod;
 jmethodID g_cipherUpdateAADMethod;
 jmethodID g_cipherInitMethod;
+jmethodID g_cipherInit2Method;
 jmethodID g_getBlockSizeMethod;
 
 // javax/crypto/spec/IvParameterSpec
@@ -61,6 +63,57 @@ jmethodID g_sslCtxGetDefaultSslParamsMethod;
 jclass    g_GCMParameterSpecClass;
 jmethodID g_GCMParameterSpecCtor;
 
+// java/security/interfaces/RSAKey
+jclass    g_RSAKeyClass;
+jmethodID g_RSAKeyGetModulus;
+
+// java/security/interfaces/RSAPublicKey
+jclass    g_RSAPublicKeyClass;
+jmethodID g_RSAPublicKeyGetPubExpMethod;
+
+// java/security/KeyPair
+jclass    g_keyPairClass;
+jmethodID g_keyPairGetPrivateMethod;
+jmethodID g_keyPairGetPublicMethod;
+
+// java/security/KeyPairGenerator
+jclass    g_keyPairGenClass;
+jmethodID g_keyPairGenGetInstanceMethod;
+jmethodID g_keyPairGenInitializeMethod;
+jmethodID g_keyPairGenGenKeyPairMethod;
+
+// java/security/interfaces/RSAPrivateCrtKey
+jclass    g_RSAPrivateCrtKeyClass;
+jmethodID g_RSAPrivateCrtKeyPubExpField;
+jmethodID g_RSAPrivateCrtKeyPrimePField;
+jmethodID g_RSAPrivateCrtKeyPrimeQField;
+jmethodID g_RSAPrivateCrtKeyPrimeExpPField;
+jmethodID g_RSAPrivateCrtKeyPrimeExpQField;
+jmethodID g_RSAPrivateCrtKeyCrtCoefField;
+jmethodID g_RSAPrivateCrtKeyModulusField;
+jmethodID g_RSAPrivateCrtKeyPrivExpField;
+
+// java/security/spec/RSAPrivateCrtKeySpec
+jclass    g_RSAPrivateCrtKeySpecClass;
+jmethodID g_RSAPrivateCrtKeySpecCtor;
+
+// java/security/spec/RSAPublicKeySpec
+jclass    g_RSAPublicCrtKeySpecClass;
+jmethodID g_RSAPublicCrtKeySpecCtor;
+
+// java/security/KeyFactory
+jclass    g_KeyFactoryClass;
+jmethodID g_KeyFactoryGetInstanceMethod;
+jmethodID g_KeyFactoryGenPrivateMethod;
+jmethodID g_KeyFactoryGenPublicMethod;
+
+// java/security/spec/X509EncodedKeySpec
+jclass    g_X509EncodedKeySpecClass;
+jmethodID g_X509EncodedKeySpecCtor;
+
+// com/android/org/conscrypt/NativeCrypto
+jclass    g_NativeCryptoClass;
+
 jobject ToGRef(JNIEnv *env, jobject lref)
 {
     if (!lref)
@@ -70,6 +123,13 @@ jobject ToGRef(JNIEnv *env, jobject lref)
     return gref;
 }
 
+jobject AddGRef(JNIEnv *env, jobject gref)
+{
+    if (!gref)
+        return NULL;
+    return (*env)->NewGlobalRef(env, gref);
+}
+
 void ReleaseGRef(JNIEnv *env, jobject gref)
 {
     if (gref)
@@ -116,6 +176,17 @@ jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name,
     return mid;
 }
 
+jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig)
+{
+    LOG_DEBUG("Finding %s field", name);
+    jfieldID fid = isStatic ? (*env)->GetStaticFieldID(env, klass, name, sig) : (*env)->GetFieldID(env, klass, name, sig);
+    if (!fid) {
+        LOG_ERROR("field %s %s was not found", name, sig);
+        assert(fid);
+    }
+    return fid;
+}
+
 JNIEnv* GetJNIEnv()
 {
     JNIEnv *env;
@@ -163,9 +234,11 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
     g_cipherGetInstanceMethod = GetMethod(env, true,  g_cipherClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
     g_getBlockSizeMethod =      GetMethod(env, false, g_cipherClass, "getBlockSize", "()I");
     g_cipherDoFinalMethod =     GetMethod(env, false, g_cipherClass, "doFinal", "()[B");
+    g_cipherDoFinal2Method =    GetMethod(env, false, g_cipherClass, "doFinal", "([B)[B");
     g_cipherUpdateMethod =      GetMethod(env, false, g_cipherClass, "update", "([B)[B");
     g_cipherUpdateAADMethod =   GetMethod(env, false, g_cipherClass, "updateAAD", "([B)V");
     g_cipherInitMethod =        GetMethod(env, false, g_cipherClass, "init", "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V");
+    g_cipherInit2Method =       GetMethod(env, false, g_cipherClass, "init", "(ILjava/security/Key;)V");
 
     g_ivPsClass =               GetClassGRef(env, "javax/crypto/spec/IvParameterSpec");
     g_ivPsCtor =                GetMethod(env, false, g_ivPsClass, "<init>", "([B)V");
@@ -184,5 +257,46 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
     g_sslCtxGetDefaultMethod =          GetMethod(env, true,  g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;");
     g_sslCtxGetDefaultSslParamsMethod = GetMethod(env, false, g_sslCtxClass, "getDefaultSSLParameters", "()Ljavax/net/ssl/SSLParameters;");
 
+    g_RSAKeyClass =                    GetClassGRef(env, "java/security/interfaces/RSAKey");
+    g_RSAKeyGetModulus =               GetMethod(env, false, g_RSAKeyClass, "getModulus", "()Ljava/math/BigInteger;");
+
+    g_RSAPublicKeyClass =              GetClassGRef(env, "java/security/interfaces/RSAPublicKey");
+    g_RSAPublicKeyGetPubExpMethod =    GetMethod(env, false, g_RSAPublicKeyClass, "getPublicExponent", "()Ljava/math/BigInteger;");
+
+    g_keyPairClass =                   GetClassGRef(env, "java/security/KeyPair");
+    g_keyPairGetPrivateMethod =        GetMethod(env, false, g_keyPairClass, "getPrivate", "()Ljava/security/PrivateKey;");
+    g_keyPairGetPublicMethod =         GetMethod(env, false, g_keyPairClass, "getPublic", "()Ljava/security/PublicKey;");
+
+    g_keyPairGenClass =                GetClassGRef(env, "java/security/KeyPairGenerator");
+    g_keyPairGenGetInstanceMethod =    GetMethod(env, true,  g_keyPairGenClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyPairGenerator;");
+    g_keyPairGenInitializeMethod =     GetMethod(env, false, g_keyPairGenClass, "initialize", "(I)V");
+    g_keyPairGenGenKeyPairMethod =     GetMethod(env, false, g_keyPairGenClass, "genKeyPair", "()Ljava/security/KeyPair;");
+
+    g_RSAPrivateCrtKeyClass =          GetClassGRef(env, "java/security/interfaces/RSAPrivateCrtKey");
+    g_RSAPrivateCrtKeyPubExpField =    GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPublicExponent", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyPrimePField =    GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPrimeP", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyPrimeQField =    GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPrimeQ", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyPrimeExpPField = GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPrimeExponentP", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyPrimeExpQField = GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPrimeExponentQ", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyCrtCoefField =   GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getCrtCoefficient", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyModulusField =   GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getModulus", "()Ljava/math/BigInteger;");
+    g_RSAPrivateCrtKeyPrivExpField =   GetMethod(env, false, g_RSAPrivateCrtKeyClass, "getPrivateExponent", "()Ljava/math/BigInteger;");
+
+    g_RSAPrivateCrtKeySpecClass =      GetClassGRef(env, "java/security/spec/RSAPrivateCrtKeySpec");
+    g_RSAPrivateCrtKeySpecCtor =       GetMethod(env, false, g_RSAPrivateCrtKeySpecClass, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;)V");
+
+    g_RSAPublicCrtKeySpecClass =       GetClassGRef(env, "java/security/spec/RSAPublicKeySpec");
+    g_RSAPublicCrtKeySpecCtor =        GetMethod(env, false, g_RSAPublicCrtKeySpecClass, "<init>", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V");
+
+    g_KeyFactoryClass =                GetClassGRef(env, "java/security/KeyFactory");
+    g_KeyFactoryGetInstanceMethod =    GetMethod(env, true, g_KeyFactoryClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyFactory;");
+    g_KeyFactoryGenPrivateMethod =     GetMethod(env, false, g_KeyFactoryClass, "generatePrivate", "(Ljava/security/spec/KeySpec;)Ljava/security/PrivateKey;");
+    g_KeyFactoryGenPublicMethod =      GetMethod(env, false, g_KeyFactoryClass, "generatePublic", "(Ljava/security/spec/KeySpec;)Ljava/security/PublicKey;");
+
+    g_X509EncodedKeySpecClass =        GetClassGRef(env, "java/security/spec/X509EncodedKeySpec");
+    g_X509EncodedKeySpecCtor =         GetMethod(env, false, g_X509EncodedKeySpecClass, "<init>", "([B)V");
+
+    g_NativeCryptoClass =              GetClassGRef(env, "com/android/org/conscrypt/NativeCrypto");
+
     return JNI_VERSION_1_6;
 }
index c5f0f8d..bdf22ed 100644 (file)
@@ -43,9 +43,11 @@ extern jmethodID g_sksCtor;
 extern jclass    g_cipherClass;
 extern jmethodID g_cipherGetInstanceMethod;
 extern jmethodID g_cipherDoFinalMethod;
+extern jmethodID g_cipherDoFinal2Method;
 extern jmethodID g_cipherUpdateMethod;
 extern jmethodID g_cipherUpdateAADMethod;
 extern jmethodID g_cipherInitMethod;
+extern jmethodID g_cipherInit2Method;
 extern jmethodID g_getBlockSizeMethod;
 
 // javax/crypto/spec/IvParameterSpec
@@ -70,6 +72,57 @@ extern jmethodID g_sslCtxGetDefaultSslParamsMethod;
 extern jclass    g_GCMParameterSpecClass;
 extern jmethodID g_GCMParameterSpecCtor;
 
+// java/security/interfaces/RSAKey
+extern jclass    g_RSAKeyClass;
+extern jmethodID g_RSAKeyGetModulus;
+
+// java/security/interfaces/RSAPublicKey
+extern jclass    g_RSAPublicKeyClass;
+extern jmethodID g_RSAPublicKeyGetPubExpMethod;
+
+// java/security/KeyPair
+extern jclass    g_keyPairClass;
+extern jmethodID g_keyPairGetPrivateMethod;
+extern jmethodID g_keyPairGetPublicMethod;
+
+// java/security/KeyPairGenerator
+extern jclass    g_keyPairGenClass;
+extern jmethodID g_keyPairGenGetInstanceMethod;
+extern jmethodID g_keyPairGenInitializeMethod;
+extern jmethodID g_keyPairGenGenKeyPairMethod;
+
+// com/android/org/conscrypt/RSAPrivateCrtKey
+extern jclass    g_RSAPrivateCrtKeyClass;
+extern jmethodID g_RSAPrivateCrtKeyPubExpField;
+extern jmethodID g_RSAPrivateCrtKeyPrimePField;
+extern jmethodID g_RSAPrivateCrtKeyPrimeQField;
+extern jmethodID g_RSAPrivateCrtKeyPrimeExpPField;
+extern jmethodID g_RSAPrivateCrtKeyPrimeExpQField;
+extern jmethodID g_RSAPrivateCrtKeyCrtCoefField;
+extern jmethodID g_RSAPrivateCrtKeyModulusField;
+extern jmethodID g_RSAPrivateCrtKeyPrivExpField;
+
+// java/security/spec/RSAPrivateCrtKeySpec
+extern jclass    g_RSAPrivateCrtKeySpecClass;
+extern jmethodID g_RSAPrivateCrtKeySpecCtor;
+
+// java/security/spec/RSAPublicKeySpec
+extern jclass    g_RSAPublicCrtKeySpecClass;
+extern jmethodID g_RSAPublicCrtKeySpecCtor;
+
+// java/security/KeyFactory
+extern jclass    g_KeyFactoryClass;
+extern jmethodID g_KeyFactoryGetInstanceMethod;
+extern jmethodID g_KeyFactoryGenPrivateMethod;
+extern jmethodID g_KeyFactoryGenPublicMethod;
+
+// java/security/spec/X509EncodedKeySpec
+extern jclass    g_X509EncodedKeySpecClass;
+extern jmethodID g_X509EncodedKeySpecCtor;
+
+// com/android/org/conscrypt/NativeCrypto
+extern jclass    g_NativeCryptoClass;
+
 // JNI helpers
 #define LOG_DEBUG(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))
 #define LOG_INFO(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))
@@ -78,8 +131,10 @@ extern jmethodID g_GCMParameterSpecCtor;
 
 void SaveTo(uint8_t* src, uint8_t** dst, size_t len);
 jobject ToGRef(JNIEnv *env, jobject lref);
+jobject AddGRef(JNIEnv *env, jobject gref);
 void ReleaseGRef(JNIEnv *env, jobject gref);
 jclass GetClassGRef(JNIEnv *env, const char* name);
 bool CheckJNIExceptions(JNIEnv* env);
 jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig);
+jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig);
 JNIEnv* GetJNIEnv(void);
diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c
new file mode 100644 (file)
index 0000000..6a70c41
--- /dev/null
@@ -0,0 +1,316 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "pal_rsa.h"
+
+PALEXPORT RSA* CryptoNative_RsaCreate()
+{
+    RSA* rsa = malloc(sizeof(RSA));
+    rsa->privateKey = NULL;
+    rsa->publicKey = NULL;
+    rsa->pubExp = NULL;
+    rsa->keyWidth = 0;
+    rsa->refCount = 1;
+    return rsa;
+}
+
+PALEXPORT int32_t CryptoNative_RsaUpRef(RSA* rsa)
+{
+    if (!rsa)
+        return FAIL;
+    rsa->refCount++;
+    return SUCCESS;
+}
+
+PALEXPORT void CryptoNative_RsaDestroy(RSA* rsa)
+{
+    if (rsa)
+    {
+        rsa->refCount--;
+        if (rsa->refCount == 0)
+        {
+            JNIEnv* env = GetJNIEnv();
+            ReleaseGRef(env, rsa->privateKey);
+            ReleaseGRef(env, rsa->publicKey);
+            ReleaseGRef(env, rsa->pubExp);
+            free(rsa);
+        }
+    }
+}
+
+PALEXPORT int32_t CryptoNative_RsaPublicEncrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding)
+{
+    if (!rsa)
+        return FAIL;
+
+    JNIEnv* env = GetJNIEnv();
+
+    jobject algName;
+    if (padding == Pkcs1)
+        algName = JSTRING("RSA/ECB/PKCS1Padding");
+    else if (padding == OaepSHA1)
+        algName = JSTRING("RSA/ECB/OAEPPadding");
+    else
+        algName = JSTRING("RSA/ECB/NoPadding");
+
+    jobject cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName);
+    (*env)->CallVoidMethod(env, cipher, g_cipherInit2Method, CIPHER_ENCRYPT_MODE, rsa->publicKey);
+    jbyteArray fromBytes = (*env)->NewByteArray(env, flen);
+    (*env)->SetByteArrayRegion(env, fromBytes, 0, flen, (jbyte*)from);
+    jbyteArray encryptedBytes = (jbyteArray)(*env)->CallObjectMethod(env, cipher, g_cipherDoFinal2Method, fromBytes);
+    jsize encryptedBytesLen = (*env)->GetArrayLength(env, encryptedBytes);
+    (*env)->GetByteArrayRegion(env, encryptedBytes, 0, encryptedBytesLen, (jbyte*) to);
+
+    (*env)->DeleteLocalRef(env, cipher);
+    (*env)->DeleteLocalRef(env, fromBytes);
+    (*env)->DeleteLocalRef(env, encryptedBytes);
+    (*env)->DeleteLocalRef(env, algName);
+
+    return (int32_t)encryptedBytesLen;
+}
+
+PALEXPORT int32_t CryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding)
+{
+    if (!rsa)
+        return FAIL;
+
+    JNIEnv* env = GetJNIEnv();
+
+    jobject algName;
+    if (padding == Pkcs1)
+        algName = JSTRING("RSA/ECB/PKCS1Padding"); // TODO: is ECB needed here?
+    else if (padding == OaepSHA1)
+        algName = JSTRING("RSA/ECB/OAEPPadding");
+    else
+        algName = JSTRING("RSA/ECB/NoPadding");
+
+    jobject cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName);
+    (*env)->CallVoidMethod(env, cipher, g_cipherInit2Method, CIPHER_DECRYPT_MODE, rsa->privateKey);
+    jbyteArray fromBytes = (*env)->NewByteArray(env, flen);
+    (*env)->SetByteArrayRegion(env, fromBytes, 0, flen, (jbyte*)from);
+    jbyteArray decryptedBytes = (jbyteArray)(*env)->CallObjectMethod(env, cipher, g_cipherDoFinal2Method, fromBytes);
+    jsize decryptedBytesLen = (*env)->GetArrayLength(env, decryptedBytes);
+    (*env)->GetByteArrayRegion(env, decryptedBytes, 0, decryptedBytesLen, (jbyte*) to);
+
+    (*env)->DeleteLocalRef(env, cipher);
+    (*env)->DeleteLocalRef(env, fromBytes);
+    (*env)->DeleteLocalRef(env, decryptedBytes);
+    (*env)->DeleteLocalRef(env, algName);
+
+    return (int32_t)decryptedBytesLen;
+}
+
+PALEXPORT int32_t CryptoNative_RsaSize(RSA* rsa)
+{
+    if (!rsa)
+        return FAIL;
+    return rsa->keyWidth / 8; 
+}
+
+PALEXPORT RSA* CryptoNative_DecodeRsaPublicKey(uint8_t* buf, int32_t len)
+{
+    if (!buf || !len)
+    {
+        return NULL;
+    }
+
+    JNIEnv* env = GetJNIEnv();
+
+    // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+    // X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(bytes);
+    // PublicKey publicKey = keyFactory.generatePublic(x509keySpec);
+
+    jobject algName = JSTRING("RSA");
+    jobject keyFactory = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, algName);
+    jbyteArray bytes = (*env)->NewByteArray(env, len);
+    (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)buf);
+    jobject x509keySpec = (*env)->NewObject(env, g_X509EncodedKeySpecClass, g_X509EncodedKeySpecCtor, bytes);
+
+    RSA* rsa = CryptoNative_RsaCreate();
+    rsa->publicKey = ToGRef(env, (*env)->CallObjectMethod(env, keyFactory, g_KeyFactoryGenPublicMethod, x509keySpec));
+
+    (*env)->DeleteLocalRef(env, algName);
+    (*env)->DeleteLocalRef(env, keyFactory);
+    (*env)->DeleteLocalRef(env, bytes);
+    (*env)->DeleteLocalRef(env, x509keySpec);
+
+    return rsa;
+}
+
+PALEXPORT int32_t CryptoNative_RsaSignPrimitive(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa)
+{
+    // TODO:
+    return FAIL;
+}
+
+PALEXPORT int32_t CryptoNative_RsaVerificationPrimitive(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa)
+{
+    // TODO:
+    return FAIL;
+}
+
+PALEXPORT int32_t CryptoNative_RsaSign(int32_t type, uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa)
+{
+    // TODO:
+    return FAIL;
+}
+
+PALEXPORT int32_t CryptoNative_RsaVerify(int32_t type, uint8_t* m, int32_t mlen, uint8_t* sigbuf, int32_t siglen, RSA* rsa)
+{
+    // TODO:
+    return FAIL;
+}
+
+PALEXPORT int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, jobject pubExp)
+{
+    if (!rsa)
+        return FAIL;
+
+    // KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+    // kpg.initialize(bits);
+    // KeyPair kp = kpg.genKeyPair();
+
+    JNIEnv* env = GetJNIEnv();
+    jobject rsaStr = JSTRING("RSA");
+    jobject kpgObj =  (*env)->CallStaticObjectMethod(env, g_keyPairGenClass, g_keyPairGenGetInstanceMethod, rsaStr);
+    (*env)->CallVoidMethod(env, kpgObj, g_keyPairGenInitializeMethod, bits);
+    jobject keyPair = (*env)->CallObjectMethod(env, kpgObj, g_keyPairGenGenKeyPairMethod);
+
+    rsa->privateKey = ToGRef(env, (*env)->CallObjectMethod(env, keyPair, g_keyPairGetPrivateMethod));
+    rsa->publicKey = ToGRef(env, (*env)->CallObjectMethod(env, keyPair, g_keyPairGetPublicMethod));
+    rsa->keyWidth = bits;
+    // pubExp is already expected to be a gref at this point but we need to create another one.
+    rsa->pubExp = AddGRef(env, pubExp);
+
+    (*env)->DeleteLocalRef(env, rsaStr);
+    (*env)->DeleteLocalRef(env, kpgObj);
+    (*env)->DeleteLocalRef(env, keyPair);
+
+    return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+PALEXPORT int32_t CryptoNative_GetRsaParameters(RSA* rsa, 
+    jobject* n, jobject* e, jobject* d, jobject* p, jobject* dmp1, jobject* q, jobject* dmq1, jobject* iqmp)
+{
+    if (!rsa || !n || !e || !d || !p || !dmp1 || !q || !dmq1 || !iqmp)
+    {
+        assert(false);
+
+        // since these parameters are 'out' parameters in managed code, ensure they are initialized
+        if (n)
+            *n = NULL;
+        if (e)
+            *e = NULL;
+        if (d)
+            *d = NULL;
+        if (p)
+            *p = NULL;
+        if (dmp1)
+            *dmp1 = NULL;
+        if (q)
+            *q = NULL;
+        if (dmq1)
+            *dmq1 = NULL;
+        if (iqmp)
+            *iqmp = NULL;
+
+        return FAIL;
+    }
+
+    JNIEnv* env = GetJNIEnv();
+    jobject privateKey = rsa->privateKey;
+    jobject publicKey = rsa->publicKey;
+
+    if (privateKey)
+    {
+        *e = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPubExpField));
+        *n = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyModulusField));
+        *d = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPrivExpField));
+        *p = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPrimePField));
+        *q = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPrimeQField));
+        *dmp1 = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPrimeExpPField));
+        *dmq1 = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPrimeExpQField));
+        *iqmp = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyCrtCoefField));
+    }
+    else
+    {
+        assert(publicKey);
+        *e = ToGRef(env, (*env)->CallObjectMethod(env, publicKey, g_RSAPublicKeyGetPubExpMethod));
+        *n = ToGRef(env, (*env)->CallObjectMethod(env, publicKey, g_RSAKeyGetModulus));
+        *d = NULL;
+        *p = NULL;
+        *q = NULL;
+        *dmp1 = NULL;
+        *dmq1 = NULL;
+        *iqmp = NULL;
+    }
+
+    return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+jobject BigNumFromBinary(JNIEnv* env, uint8_t* bytes, int32_t len)
+{
+    assert(len > 0);
+    jbyteArray buffArray = (*env)->NewByteArray(env, len);
+    (*env)->SetByteArrayRegion(env, buffArray, 0, len, (jbyte*)bytes);
+    jobject bigNum = (*env)->NewObject(env, g_bigNumClass, g_bigNumCtor, buffArray);
+    (*env)->DeleteLocalRef(env, buffArray);
+    return bigNum;
+}
+
+PALEXPORT int32_t CryptoNative_SetRsaParameters(RSA* rsa, 
+    uint8_t* n,    int32_t nLength,    uint8_t* e,    int32_t eLength,    uint8_t* d, int32_t dLength, 
+    uint8_t* p,    int32_t pLength,    uint8_t* dmp1, int32_t dmp1Length, uint8_t* q, int32_t qLength, 
+    uint8_t* dmq1, int32_t dmq1Length, uint8_t* iqmp, int32_t iqmpLength)
+{
+    if (!rsa)
+        return FAIL;
+
+    JNIEnv* env = GetJNIEnv();
+
+    jobject nObj = BigNumFromBinary(env, n, nLength);
+    jobject eObj = BigNumFromBinary(env, e, eLength);
+
+    rsa->keyWidth = (nLength - 1) * 8; // Android SDK has an extra byte in Modulus(?)
+
+    jobject algName = JSTRING("RSA");
+    jobject keyFactory = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, algName);
+
+    if (dLength > 0)
+    {
+        // private key section
+        jobject dObj = BigNumFromBinary(env, d, dLength);
+        jobject pObj = BigNumFromBinary(env, p, pLength);
+        jobject qObj = BigNumFromBinary(env, q, qLength);
+        jobject dmp1Obj = BigNumFromBinary(env, dmp1, dmp1Length);
+        jobject dmq1Obj = BigNumFromBinary(env, dmq1, dmq1Length);
+        jobject iqmpObj = BigNumFromBinary(env, iqmp, iqmpLength);
+
+        jobject rsaPrivateKeySpec = (*env)->NewObject(env, g_RSAPrivateCrtKeySpecClass, g_RSAPrivateCrtKeySpecCtor,
+            nObj, eObj, dObj, pObj, qObj, dmp1Obj, dmq1Obj, iqmpObj);
+
+        ReleaseGRef(env, rsa->privateKey);
+        rsa->privateKey = ToGRef(env, (*env)->CallObjectMethod(env, keyFactory, g_KeyFactoryGenPrivateMethod, rsaPrivateKeySpec));
+
+        (*env)->DeleteLocalRef(env, dObj);
+        (*env)->DeleteLocalRef(env, pObj);
+        (*env)->DeleteLocalRef(env, qObj);
+        (*env)->DeleteLocalRef(env, dmp1Obj);
+        (*env)->DeleteLocalRef(env, dmq1Obj);
+        (*env)->DeleteLocalRef(env, iqmpObj);
+        (*env)->DeleteLocalRef(env, rsaPrivateKeySpec);
+    }
+
+    jobject rsaPubKeySpec = (*env)->NewObject(env, g_RSAPublicCrtKeySpecClass, g_RSAPublicCrtKeySpecCtor, nObj, eObj);
+
+    ReleaseGRef(env, rsa->publicKey);
+    rsa->publicKey = ToGRef(env, (*env)->CallObjectMethod(env, keyFactory, g_KeyFactoryGenPublicMethod, rsaPubKeySpec));
+
+    (*env)->DeleteLocalRef(env, algName);
+    (*env)->DeleteLocalRef(env, keyFactory);
+    (*env)->DeleteLocalRef(env, nObj);
+    (*env)->DeleteLocalRef(env, eObj);
+    (*env)->DeleteLocalRef(env, rsaPubKeySpec);
+
+    return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h
new file mode 100644 (file)
index 0000000..f514da3
--- /dev/null
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "pal_jni.h"
+
+typedef enum
+{
+    Pkcs1 = 0,
+    OaepSHA1 = 1,
+    NoPadding = 2,
+} RsaPadding;
+
+typedef struct RSA
+{
+    jobject pubExp;
+    jobject privateKey; // RSAPrivateCrtKey
+    jobject publicKey;  // RSAPublicCrtKey
+    int32_t refCount;
+    int32_t keyWidth;
+} RSA;
+
+#define CIPHER_ENCRYPT_MODE 1
+#define CIPHER_DECRYPT_MODE 2
+
+jobject BigNumFromBinary(JNIEnv* env, uint8_t* bytes, int32_t len);
+
+PALEXPORT RSA* CryptoNative_RsaCreate(void);
+PALEXPORT int32_t CryptoNative_RsaUpRef(RSA* rsa);
+PALEXPORT void CryptoNative_RsaDestroy(RSA* rsa);
+PALEXPORT RSA* CryptoNative_DecodeRsaPublicKey(uint8_t* buf, int32_t len);
+PALEXPORT int32_t CryptoNative_RsaPublicEncrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
+PALEXPORT int32_t CryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
+PALEXPORT int32_t CryptoNative_RsaSignPrimitive(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa);
+PALEXPORT int32_t CryptoNative_RsaVerificationPrimitive(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa);
+PALEXPORT int32_t CryptoNative_RsaSize(RSA* rsa);
+PALEXPORT int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, jobject pubExp);
+PALEXPORT int32_t CryptoNative_RsaSign(int32_t type, uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa);
+PALEXPORT int32_t CryptoNative_RsaVerify(int32_t type, uint8_t* m, int32_t mlen, uint8_t* sigbuf, int32_t siglen, RSA* rsa);
+PALEXPORT int32_t CryptoNative_GetRsaParameters(RSA* rsa, 
+    jobject* n, jobject* e, jobject* d, jobject* p, jobject* dmp1, jobject* q, jobject* dmq1, jobject* iqmp);
+PALEXPORT int32_t CryptoNative_SetRsaParameters(RSA* rsa, 
+    uint8_t* n,    int32_t nLength,    uint8_t* e,    int32_t eLength,    uint8_t* d, int32_t dLength, 
+    uint8_t* p,    int32_t pLength,    uint8_t* dmp1, int32_t dmp1Length, uint8_t* q, int32_t qLength, 
+    uint8_t* dmq1, int32_t dmq1Length, uint8_t* iqmp, int32_t iqmpLength);