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 4234a1aa8902d4..64b38631f9c1a6 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 @@ -104,6 +104,7 @@ internal static RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includ SafeBignumHandle n, e, d, p, dmp1, q, dmq1, iqmp; if (!GetRsaParameters(key, + includePrivateParameters ? 1 : 0, out n, out e, out d, @@ -163,6 +164,7 @@ internal static RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includ [return: MarshalAs(UnmanagedType.Bool)] private static partial bool GetRsaParameters( SafeRsaHandle key, + int includePrivateParameters, out SafeBignumHandle n, out SafeBignumHandle e, out SafeBignumHandle d, diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509StoreMutableTests.Android.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509StoreMutableTests.Android.cs index 76e6082691659f..842e3dd5e1fef8 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509StoreMutableTests.Android.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509StoreMutableTests.Android.cs @@ -52,5 +52,42 @@ public static void Add_CertWithPrivateKey_NotSupportedAlgorithm() Assert.Throws(() => store.Add(cert)); } } + + [Fact] + public static void AndroidKeyStoreRsaPrivateKey_PublicExport() + { + using (X509Store store = new(StoreName.My, StoreLocation.CurrentUser)) + using (X509Certificate2 cert = X509Certificate2.CreateFromPem(TestData.RsaCertificate, TestData.RsaPkcs8Key)) + using (X509Certificate2 certOnly = new(cert.RawData)) + { + store.Open(OpenFlags.ReadWrite); + store.Remove(certOnly); + + try + { + store.Add(cert); + + using (ImportedCollection coll = new ImportedCollection(store.Certificates)) + { + byte[] thumbprint = certOnly.GetCertHash(HashAlgorithmName.SHA256); + X509Certificate2 storeCert = Assert.Single( + coll.Collection.FindByThumbprint(HashAlgorithmName.SHA256, thumbprint)); + + Assert.True(storeCert.HasPrivateKey); + + using RSA rsa = storeCert.GetRSAPrivateKey(); + RSAParameters publicParameters = rsa.ExportParameters(false); + Assert.NotNull(publicParameters.Modulus); + Assert.NotNull(publicParameters.Exponent); + + Assert.Throws(() => rsa.ExportParameters(true)); + } + } + finally + { + store.Remove(certOnly); + } + } + } } } 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 e18272cb1cab46..e1356697ff0c9d 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 @@ -371,6 +371,7 @@ PALEXPORT int32_t AndroidCryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits) } PALEXPORT int32_t AndroidCryptoNative_GetRsaParameters(RSA* rsa, + int32_t includePrivateParameters, 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) @@ -398,12 +399,26 @@ PALEXPORT int32_t AndroidCryptoNative_GetRsaParameters(RSA* rsa, return FAIL; } + *n = NULL; + *e = NULL; + *d = NULL; + *p = NULL; + *q = NULL; + *dmp1 = NULL; + *dmq1 = NULL; + *iqmp = NULL; + JNIEnv* env = GetJNIEnv(); jobject privateKey = rsa->privateKey; jobject publicKey = rsa->publicKey; - if (privateKey) + if (includePrivateParameters) { + if (!privateKey || !(*env)->IsInstanceOf(env, privateKey, g_RSAPrivateCrtKeyClass)) + { + return FAIL; + } + *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)); @@ -417,12 +432,11 @@ PALEXPORT int32_t AndroidCryptoNative_GetRsaParameters(RSA* rsa, { *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; + } + else if (privateKey && (*env)->IsInstanceOf(env, privateKey, g_RSAPrivateCrtKeyClass)) + { + *e = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyPubExpField)); + *n = ToGRef(env, (*env)->CallObjectMethod(env, privateKey, g_RSAPrivateCrtKeyModulusField)); } else { 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 13f6113aab4ee3..dafa9c8bf93872 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 @@ -17,8 +17,8 @@ typedef enum typedef struct RSA { - jobject privateKey; // RSAPrivateCrtKey - jobject publicKey; // RSAPublicCrtKey + jobject privateKey; // RSAPrivateKey + jobject publicKey; // RSAPublicKey atomic_int refCount; int32_t keyWidthInBits; } RSA; @@ -36,7 +36,7 @@ PALEXPORT int32_t AndroidCryptoNative_RsaSignPrimitive(int32_t flen, uint8_t* fr PALEXPORT int32_t AndroidCryptoNative_RsaVerificationPrimitive(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa); PALEXPORT int32_t AndroidCryptoNative_RsaSize(RSA* rsa); PALEXPORT int32_t AndroidCryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits); -PALEXPORT int32_t AndroidCryptoNative_GetRsaParameters(RSA* rsa, +PALEXPORT int32_t AndroidCryptoNative_GetRsaParameters(RSA* rsa, int32_t includePrivateParameters, jobject* n, jobject* e, jobject* d, jobject* p, jobject* dmp1, jobject* q, jobject* dmq1, jobject* iqmp); PALEXPORT int32_t AndroidCryptoNative_SetRsaParameters(RSA* rsa, uint8_t* n, int32_t nLength, uint8_t* e, int32_t eLength, uint8_t* d, int32_t dLength,