From: Egor Bogatov Date: Wed, 6 Jan 2021 12:35:51 +0000 (+0300) Subject: [Android] Implement RSA encryption using Android SDK (#45870) X-Git-Tag: submit/tizen/20210909.063632~3921 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b0de92b9e1459bbb3653c820e2d3cb311d656f5f;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [Android] Implement RSA encryption using Android SDK (#45870) --- diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt index 579a9ec..9d380c4 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt @@ -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 diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c index 4aa0463..f390981 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c @@ -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 index 0000000..2db14df --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.c @@ -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 index 0000000..ef703f8 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_err.h @@ -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); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c index d4ad6f5..3b6482a 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c @@ -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, "", "([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, "", "(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, "", "(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, "", "([B)V"); + + g_NativeCryptoClass = GetClassGRef(env, "com/android/org/conscrypt/NativeCrypto"); + return JNI_VERSION_1_6; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h index c5f0f8d..bdf22ed 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h @@ -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 index 0000000..6a70c41 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c @@ -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 index 0000000..f514da3 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h @@ -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);