From 2b6b45878b1be4d77eec34ab5bc80b626995a8c5 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Fri, 19 Mar 2021 15:05:41 -0700
Subject: [PATCH 01/11] Use EVP_PKEY for RSA key generation

---
 .../Interop.EvpPkey.Rsa.cs                    | 16 ++++++++
 .../Interop.Rsa.cs                            |  3 --
 .../Security/Cryptography/RSAOpenSsl.cs       | 37 ++++---------------
 .../apibridge.c                               |  8 ++++
 .../apibridge.h                               |  1 +
 .../opensslshim.h                             | 23 ++++++++++++
 .../pal_evp_pkey_rsa.c                        | 29 +++++++++++++++
 .../pal_evp_pkey_rsa.h                        |  5 +++
 .../pal_rsa.c                                 |  5 ---
 .../pal_rsa.h                                 |  7 ----
 ...em.Security.Cryptography.Algorithms.csproj |  3 ++
 11 files changed, 93 insertions(+), 44 deletions(-)

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
index 1f61a826a9c1..c28522784b5a 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
@@ -10,6 +10,22 @@ internal static partial class Interop
 {
     internal static partial class Crypto
     {
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaGenerateKey")]
+        private static extern SafeEvpPKeyHandle CryptoNative_RsaGenerateKey(int keySize);
+
+        internal static SafeEvpPKeyHandle RsaGenerateKey(int keySize)
+        {
+            SafeEvpPKeyHandle pkey = CryptoNative_RsaGenerateKey(keySize);
+
+            if (pkey.IsInvalid)
+            {
+                pkey.Dispose();
+                throw CreateOpenSslCryptographicException();
+            }
+
+            return pkey;
+        }
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
         internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
 
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
index a7b85ae01161..a05f020ada63 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
@@ -89,9 +89,6 @@ private static extern int RsaVerificationPrimitive(
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSize")]
         internal static extern int RsaSize(SafeRsaHandle rsa);
 
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaGenerateKeyEx")]
-        internal static extern int RsaGenerateKeyEx(SafeRsaHandle rsa, int bits, SafeBignumHandle e);
-
         internal static bool RsaSign(int type, ReadOnlySpan<byte> m, int m_len, Span<byte> sigret, out int siglen, SafeRsaHandle rsa) =>
             RsaSign(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigret), out siglen, rsa);
 
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
index 6741d28bbaee..4d4a8414b3fd 100644
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
@@ -25,9 +25,6 @@ public sealed partial class RSAOpenSsl : RSA
     {
         private const int BitsPerByte = 8;
 
-        // 65537 (0x10001) in big-endian form
-        private static readonly byte[] s_defaultExponent = { 0x01, 0x00, 0x01 };
-
         private Lazy<SafeRsaHandle> _key;
 
         public RSAOpenSsl()
@@ -585,36 +582,18 @@ private static void CheckBoolReturn(int returnValue)
 
         private SafeRsaHandle GenerateKey()
         {
-            SafeRsaHandle key = Interop.Crypto.RsaCreate();
-            bool generated = false;
-
-            Interop.Crypto.CheckValidOpenSslHandle(key);
-
-            try
+            using (SafeEvpPKeyHandle pkey = Interop.Crypto.RsaGenerateKey(KeySize))
             {
-                using (SafeBignumHandle exponent = Interop.Crypto.CreateBignum(s_defaultExponent))
-                {
-                    // The documentation for RSA_generate_key_ex does not say that it returns only
-                    // 0 or 1, so the call marshals it back as a full Int32 and checks for a value
-                    // of 1 explicitly.
-                    int response = Interop.Crypto.RsaGenerateKeyEx(
-                        key,
-                        KeySize,
-                        exponent);
-
-                    CheckBoolReturn(response);
-                    generated = true;
-                }
-            }
-            finally
-            {
-                if (!generated)
+                SafeRsaHandle rsa = Interop.Crypto.EvpPkeyGetRsa(pkey);
+
+                if (rsa.IsInvalid)
                 {
-                    key.Dispose();
+                    rsa.Dispose();
+                    throw Interop.Crypto.CreateOpenSslCryptographicException();
                 }
-            }
 
-            return key;
+                return rsa;
+            }
         }
 
         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
index 167de7fd8e6a..def7198deb98 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
@@ -728,4 +728,12 @@ void local_SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level)
     (void)ctx;
     (void)level;
 }
+
+int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2)
+{
+    // On OpenSSL 1.0.2 there aren't two different identifiers for RSA,
+    // so just pass the request on th EVP_PKEY_CTX_ctrl with the only identifier defined.
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, optype, cmd, p1, p2);
+}
+
 #endif
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
index 5f62864b2496..b58611ae7301 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
@@ -26,6 +26,7 @@ int32_t local_RSA_meth_get_flags(const RSA_METHOD* meth);
 int32_t local_RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
 int32_t local_RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
 int32_t local_RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
+int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
 int32_t local_SSL_is_init_finished(const SSL* ssl);
 unsigned long local_SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options);
 void local_SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index ed3994926d59..dff6091e9eaa 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -145,6 +145,7 @@ void RSA_get0_factors(const RSA* rsa, const BIGNUM** p, const BIGNUM** q);
 void RSA_get0_key(const RSA* rsa, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d);
 int32_t RSA_meth_get_flags(const RSA_METHOD* meth);
 const RSA_METHOD* RSA_PKCS1_OpenSSL(void);
+int32_t RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
 int32_t RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
 int32_t RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
 int32_t RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
@@ -170,6 +171,13 @@ const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x509);
 X509_PUBKEY* X509_get_X509_PUBKEY(const X509* x509);
 int32_t X509_get_version(const X509* x509);
 int32_t X509_up_ref(X509* x509);
+
+// Redefine EVP_PKEY_CTX_set_rsa operations to use (local_)RSA_pkey_ctx_ctrl so the path is the same
+// for 1.0-built on 1.1 as on 1.1-built on 1.1.
+#undef EVP_PKEY_CTX_set_rsa_keygen_bits
+#define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
+    RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
+
 #endif
 
 #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
@@ -341,8 +349,12 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     RENAMED_FUNCTION(EVP_MD_CTX_free, EVP_MD_CTX_destroy) \
     RENAMED_FUNCTION(EVP_MD_CTX_new, EVP_MD_CTX_create) \
     REQUIRED_FUNCTION(EVP_MD_size) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
+    REQUIRED_FUNCTION(EVP_PKEY_base_id) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive) \
@@ -350,6 +362,8 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_RSA) \
+    REQUIRED_FUNCTION(EVP_PKEY_keygen) \
+    REQUIRED_FUNCTION(EVP_PKEY_keygen_init) \
     REQUIRED_FUNCTION(EVP_PKEY_new) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
@@ -432,6 +446,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     FALLBACK_FUNCTION(RSA_get0_key) \
     FALLBACK_FUNCTION(RSA_meth_get_flags) \
     REQUIRED_FUNCTION(RSA_new) \
+    FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \
     RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \
     REQUIRED_FUNCTION(RSA_private_decrypt) \
     REQUIRED_FUNCTION(RSA_private_encrypt) \
@@ -727,8 +742,12 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_MD_CTX_free EVP_MD_CTX_free_ptr
 #define EVP_MD_CTX_new EVP_MD_CTX_new_ptr
 #define EVP_MD_size EVP_MD_size_ptr
+#define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr
 #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr
+#define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr
 #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
+#define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
+#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
 #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr
 #define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr
 #define EVP_PKEY_derive EVP_PKEY_derive_ptr
@@ -736,6 +755,8 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
 #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
 #define EVP_PKEY_get1_RSA EVP_PKEY_get1_RSA_ptr
+#define EVP_PKEY_keygen EVP_PKEY_keygen_ptr
+#define EVP_PKEY_keygen_init EVP_PKEY_keygen_init_ptr
 #define EVP_PKEY_new EVP_PKEY_new_ptr
 #define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr
 #define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr
@@ -818,6 +839,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_get_method RSA_get_method_ptr
 #define RSA_meth_get_flags RSA_meth_get_flags_ptr
 #define RSA_new RSA_new_ptr
+#define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr
 #define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr
 #define RSA_private_decrypt RSA_private_decrypt_ptr
 #define RSA_private_encrypt RSA_private_encrypt_ptr
@@ -1026,6 +1048,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_set0_crt_params local_RSA_set0_crt_params
 #define RSA_set0_factors local_RSA_set0_factors
 #define RSA_set0_key local_RSA_set0_key
+#define RSA_pkey_ctx_ctrl local_RSA_pkey_ctx_ctrl
 #define SSL_CTX_set_security_level local_SSL_CTX_set_security_level
 #define SSL_is_init_finished local_SSL_is_init_finished
 #define X509_CRL_get0_nextUpdate local_X509_CRL_get0_nextUpdate
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
index e8d961dbd22d..29f9238ce99b 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
@@ -4,6 +4,35 @@
 
 #include "pal_evp_pkey_rsa.h"
 
+EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
+{
+    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+
+    if (ctx == NULL)
+    {
+        return NULL;
+    }
+
+    EVP_PKEY* pkey = NULL;
+    EVP_PKEY* ret = NULL;
+
+    if (EVP_PKEY_keygen_init(ctx) == 1 &&
+        EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
+        EVP_PKEY_keygen(ctx, &pkey) == 1)
+    {
+        ret = pkey;
+        pkey = NULL;
+    }
+
+    if (pkey != NULL)
+    {
+        EVP_PKEY_free(pkey);
+    }
+
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
 RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey)
 {
     return EVP_PKEY_get1_RSA(pkey);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
index d8ff369670c7..1fda1494146b 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
@@ -6,6 +6,11 @@
 #include "pal_compiler.h"
 #include "opensslshim.h"
 
+/*
+Creates an RSA key of the requested size.
+*/
+DLLEXPORT EVP_PKEY* CryptoNative_RsaGenerateKey(int32_t keySize);
+
 /*
 Shims the EVP_PKEY_get1_RSA method.
 
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
index f764815a043e..080027de0e14 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
@@ -143,11 +143,6 @@ int32_t CryptoNative_RsaSize(RSA* rsa)
     return RSA_size(rsa);
 }
 
-int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e)
-{
-    return RSA_generate_key_ex(rsa, bits, e, NULL);
-}
-
 int32_t
 CryptoNative_RsaSign(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa)
 {
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
index b85fed627f4d..1c0bc2df4755 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
@@ -86,13 +86,6 @@ Returns the RSA modulus size in bytes.
 */
 DLLEXPORT int32_t CryptoNative_RsaSize(RSA* rsa);
 
-/*
-Shims the RSA_generate_key_ex method.
-
-Returns 1 upon success, otherwise 0.
-*/
-DLLEXPORT int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e);
-
 /*
 Shims the RSA_sign method.
 
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
index fa63b6e2fee1..6ad2b78e016e 100644
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
@@ -507,6 +507,9 @@
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Ecdh.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Ecdh.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs">
+      <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
     </Compile>

From 16f162f76cdbdd150487eb9824f9d8f8e39df5ca Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Wed, 24 Mar 2021 10:27:42 -0700
Subject: [PATCH 02/11] Use EVP_PKEY for RSA Decrypt

---
 .../Interop.EVP.DigestAlgs.cs                 |  58 +++++
 .../Interop.EVP.cs                            |  18 +-
 .../Interop.EvpPkey.Rsa.cs                    |  37 +++
 .../Interop.EvpPkey.cs                        |   3 +
 .../Interop.Rsa.cs                            |  16 --
 .../Security/Cryptography/RSAOpenSsl.cs       | 222 ++++++++++--------
 .../apibridge.c                               |  17 ++
 .../apibridge.h                               |   1 +
 .../opensslshim.h                             |  17 +-
 .../pal_evp_pkey.c                            |   7 +
 .../pal_evp_pkey.h                            |   5 +
 .../pal_evp_pkey_rsa.c                        | 137 ++++++++++-
 .../pal_evp_pkey_rsa.h                        |  27 ++-
 .../pal_rsa.c                                 |  13 -
 .../pal_rsa.h                                 |   8 -
 .../HashProviderDispenser.Unix.cs             |  36 +--
 ...em.Security.Cryptography.Algorithms.csproj |   3 +
 ...ystem.Security.Cryptography.OpenSsl.csproj |   3 +
 18 files changed, 444 insertions(+), 184 deletions(-)
 create mode 100644 src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs
new file mode 100644
index 000000000000..53ef644d84b9
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+
+internal static partial class Interop
+{
+    internal static partial class Crypto
+    {
+        private static volatile IntPtr s_evpMd5;
+        private static volatile IntPtr s_evpSha1;
+        private static volatile IntPtr s_evpSha256;
+        private static volatile IntPtr s_evpSha384;
+        private static volatile IntPtr s_evpSha512;
+
+        [DllImport(Libraries.CryptoNative)]
+        private static extern IntPtr CryptoNative_EvpMd5();
+
+        internal static IntPtr EvpMd5() =>
+            s_evpMd5 != IntPtr.Zero ? s_evpMd5 : (s_evpMd5 = CryptoNative_EvpMd5());
+
+        [DllImport(Libraries.CryptoNative)]
+        internal static extern IntPtr CryptoNative_EvpSha1();
+
+        internal static IntPtr EvpSha1() =>
+            s_evpSha1 != IntPtr.Zero ? s_evpSha1 : (s_evpSha1 = CryptoNative_EvpSha1());
+
+        [DllImport(Libraries.CryptoNative)]
+        internal static extern IntPtr CryptoNative_EvpSha256();
+
+        internal static IntPtr EvpSha256() =>
+            s_evpSha256 != IntPtr.Zero ? s_evpSha256 : (s_evpSha256 = CryptoNative_EvpSha256());
+
+        [DllImport(Libraries.CryptoNative)]
+        internal static extern IntPtr CryptoNative_EvpSha384();
+
+        internal static IntPtr EvpSha384() =>
+            s_evpSha384 != IntPtr.Zero ? s_evpSha384 : (s_evpSha384 = CryptoNative_EvpSha384());
+
+        [DllImport(Libraries.CryptoNative)]
+        internal static extern IntPtr CryptoNative_EvpSha512();
+
+        internal static IntPtr EvpSha512() =>
+            s_evpSha512 != IntPtr.Zero ? s_evpSha512 : (s_evpSha512 = CryptoNative_EvpSha512());
+
+        internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch
+        {
+            nameof(HashAlgorithmName.SHA1) => EvpSha1(),
+            nameof(HashAlgorithmName.SHA256) => EvpSha256(),
+            nameof(HashAlgorithmName.SHA384) => EvpSha384(),
+            nameof(HashAlgorithmName.SHA512) => EvpSha512(),
+            nameof(HashAlgorithmName.MD5) => EvpMd5(),
+            _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId))
+        };
+    }
+}
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
index 67a9b542306a..ea9dc5186d4f 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs
@@ -4,6 +4,7 @@
 
 using System;
 using System.Runtime.InteropServices;
+using System.Security.Cryptography;
 using Microsoft.Win32.SafeHandles;
 
 internal static partial class Interop
@@ -31,23 +32,6 @@ internal static int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ReadOnlySpan<byte> d
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMdSize")]
         internal extern static int EvpMdSize(IntPtr md);
 
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMd5")]
-        internal extern static IntPtr EvpMd5();
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha1")]
-        internal extern static IntPtr EvpSha1();
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha256")]
-        internal extern static IntPtr EvpSha256();
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha384")]
-        internal extern static IntPtr EvpSha384();
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha512")]
-        internal extern static IntPtr EvpSha512();
-
-
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetMaxMdSize")]
         private extern static int GetMaxMdSize();
 
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
index c28522784b5a..f023ced7f969 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
@@ -2,6 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System;
+using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Security.Cryptography;
 using Microsoft.Win32.SafeHandles;
@@ -26,6 +28,41 @@ internal static SafeEvpPKeyHandle RsaGenerateKey(int keySize)
             return pkey;
         }
 
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaDecrypt")]
+        private static extern int CryptoNative_RsaDecrypt(
+            SafeEvpPKeyHandle pkey,
+            ref byte source,
+            int sourceLength,
+            RSAEncryptionPaddingMode paddingMode,
+            IntPtr digestAlgorithm,
+            ref byte destination,
+            int destinationLength);
+
+        internal static int RsaDecrypt(
+            SafeEvpPKeyHandle pkey,
+            ReadOnlySpan<byte> source,
+            RSAEncryptionPaddingMode paddingMode,
+            IntPtr digestAlgorithm,
+            Span<byte> destination)
+        {
+            int written = CryptoNative_RsaDecrypt(
+                pkey,
+                ref MemoryMarshal.GetReference(source),
+                source.Length,
+                paddingMode,
+                digestAlgorithm,
+                ref MemoryMarshal.GetReference(destination),
+                destination.Length);
+
+            if (written < 0)
+            {
+                Debug.Assert(written == -1);
+                throw CreateOpenSslCryptographicException();
+            }
+
+            return written;
+        }
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
         internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
 
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
index 2eff6bfcd19d..3ad4bc9d08b7 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs
@@ -16,6 +16,9 @@ internal static partial class Crypto
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyDestroy")]
         internal static extern void EvpPkeyDestroy(IntPtr pkey);
 
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeySize")]
+        internal static extern int EvpPKeySize(SafeEvpPKeyHandle pkey);
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_UpRefEvpPkey")]
         internal static extern int UpRefEvpPkey(SafeEvpPKeyHandle handle);
     }
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
index a05f020ada63..5ad534a8f26d 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
@@ -44,22 +44,6 @@ private extern static int RsaPublicEncrypt(
             SafeRsaHandle rsa,
             RsaPadding padding);
 
-        internal static int RsaPrivateDecrypt(
-            int flen,
-            ReadOnlySpan<byte> from,
-            Span<byte> to,
-            SafeRsaHandle rsa,
-            RsaPadding padding) =>
-            RsaPrivateDecrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding);
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaPrivateDecrypt")]
-        private extern static int RsaPrivateDecrypt(
-            int flen,
-            ref byte from,
-            ref byte to,
-            SafeRsaHandle rsa,
-            RsaPadding padding);
-
         internal static int RsaSignPrimitive(
             ReadOnlySpan<byte> from,
             Span<byte> to,
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
index 4d4a8414b3fd..87e31b9dde42 100644
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
@@ -85,10 +85,9 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
             if (padding == null)
                 throw new ArgumentNullException(nameof(padding));
 
-            Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = GetKey();
-
-            int rsaSize = Interop.Crypto.RsaSize(key);
+            ValidatePadding(padding);
+            SafeEvpPKeyHandle key = GetPKey();
+            int rsaSize = Interop.Crypto.EvpPKeySize(key);
             byte[] buf = null;
             Span<byte> destination = default;
 
@@ -97,18 +96,15 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
                 buf = CryptoPool.Rent(rsaSize);
                 destination = new Span<byte>(buf, 0, rsaSize);
 
-                if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten))
-                {
-                    Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer");
-                    throw new CryptographicException();
-                }
-
+                int bytesWritten = Decrypt(key, data, destination, padding);
                 return destination.Slice(0, bytesWritten).ToArray();
             }
             finally
             {
                 CryptographicOperations.ZeroMemory(destination);
                 CryptoPool.Return(buf, clearSize: 0);
+                // Until EVP_PKEY is what gets stored, free the temporary key handle.
+                key.Dispose();
             }
         }
 
@@ -119,82 +115,73 @@ public override bool TryDecrypt(
             out int bytesWritten)
         {
             if (padding == null)
-            {
                 throw new ArgumentNullException(nameof(padding));
-            }
-
-            Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor);
-            SafeRsaHandle key = GetKey();
 
-            int keySizeBytes = Interop.Crypto.RsaSize(key);
+            ValidatePadding(padding);
+            SafeEvpPKeyHandle key = GetPKey();
+            int keySizeBytes = Interop.Crypto.EvpPKeySize(key);
 
-            // OpenSSL does not take a length value for the destination, so it can write out of bounds.
-            // To prevent the OOB write, decrypt into a temporary buffer.
+            // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size.
+            // So if the destination is too small, use a temporary buffer so we can match
+            // Windows behavior of succeeding so long as the buffer can hold the final output.
             if (destination.Length < keySizeBytes)
             {
-                Span<byte> tmp = stackalloc byte[0];
+                // RSA up through 4096 bits use a stackalloc
+                Span<byte> tmp = stackalloc byte[512];
                 byte[] rent = null;
 
-                // RSA up through 4096 stackalloc
-                if (keySizeBytes <= 512)
+                if (keySizeBytes > tmp.Length)
                 {
-                    tmp = stackalloc byte[keySizeBytes];
-                }
-                else
-                {
-                    rent = ArrayPool<byte>.Shared.Rent(keySizeBytes);
+                    rent = CryptoPool.Rent(keySizeBytes);
                     tmp = rent;
                 }
 
-                bool ret = TryDecrypt(key, data, tmp, rsaPadding, oaepProcessor, out bytesWritten);
+                int written = Decrypt(key, data, tmp, padding);
+                // Until EVP_PKEY is what gets stored, free the temporary key handle.
+                key.Dispose();
+                bool ret;
 
-                if (ret)
+                if (destination.Length < written)
                 {
-                    tmp = tmp.Slice(0, bytesWritten);
-
-                    if (bytesWritten > destination.Length)
-                    {
-                        ret = false;
-                        bytesWritten = 0;
-                    }
-                    else
-                    {
-                        tmp.CopyTo(destination);
-                    }
-
-                    CryptographicOperations.ZeroMemory(tmp);
+                    bytesWritten = 0;
+                    ret = false;
+                }
+                else
+                {
+                    tmp.Slice(0, written).CopyTo(destination);
+                    bytesWritten = written;
+                    ret = true;
                 }
 
+                // Whether a stackalloc or a rented array, clear our copy of
+                // the decrypted content.
+                CryptographicOperations.ZeroMemory(tmp.Slice(0, written));
+
                 if (rent != null)
                 {
-                    // Already cleared
-                    ArrayPool<byte>.Shared.Return(rent);
+                    // Already cleared.
+                    CryptoPool.Return(rent, clearSize: 0);
                 }
 
                 return ret;
             }
 
-            return TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten);
+            bytesWritten = Decrypt(key, data, destination, padding);
+            // Until EVP_PKEY is what gets stored, free the temporary key handle.
+            key.Dispose();
+            return true;
         }
 
-        private static bool TryDecrypt(
-            SafeRsaHandle key,
+        private static int Decrypt(
+            SafeEvpPKeyHandle key,
             ReadOnlySpan<byte> data,
             Span<byte> destination,
-            Interop.Crypto.RsaPadding rsaPadding,
-            RsaPaddingProcessor rsaPaddingProcessor,
-            out int bytesWritten)
+            RSAEncryptionPadding padding)
         {
-            // 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.Crypto.RsaPadding.NoPadding) ==
-                (rsaPaddingProcessor != null));
-
             // Caller should have already checked this.
             Debug.Assert(!key.IsInvalid);
 
-            int rsaSize = Interop.Crypto.RsaSize(key);
+            int rsaSize = Interop.Crypto.EvpPKeySize(key);
 
             if (data.Length != rsaSize)
             {
@@ -203,50 +190,24 @@ private static bool TryDecrypt(
 
             if (destination.Length < rsaSize)
             {
-                bytesWritten = 0;
-                return false;
+                Debug.Fail("Caller is responsible for temporary decryption buffer creation");
+                throw new CryptographicException();
             }
 
-            Span<byte> decryptBuf = destination;
-            byte[] paddingBuf = null;
+            IntPtr hashAlgorithm = IntPtr.Zero;
 
-            if (rsaPaddingProcessor != null)
+            if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
             {
-                paddingBuf = CryptoPool.Rent(rsaSize);
-                decryptBuf = paddingBuf;
+                Debug.Assert(padding.OaepHashAlgorithm.Name != null);
+                hashAlgorithm = Interop.Crypto.HashAlgorithmToEvp(padding.OaepHashAlgorithm.Name);
             }
 
-            try
-            {
-                int returnValue = Interop.Crypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding);
-                CheckReturn(returnValue);
-
-                if (rsaPaddingProcessor != null)
-                {
-                    return rsaPaddingProcessor.DepadOaep(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);
-                }
-            }
+            return Interop.Crypto.RsaDecrypt(
+                key,
+                data,
+                padding.Mode,
+                hashAlgorithm,
+                destination);
         }
 
         public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
@@ -550,6 +511,30 @@ private void ThrowIfDisposed()
             }
         }
 
+        private SafeEvpPKeyHandle GetPKey()
+        {
+            SafeRsaHandle currentKey = GetKey();
+            SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate();
+
+            try
+            {
+                // Wrapping our key in an EVP_PKEY will up_ref our key.
+                // When the EVP_PKEY is Disposed it will down_ref the key.
+                // So everything should be copacetic.
+                if (!Interop.Crypto.EvpPkeySetRsa(pkeyHandle, currentKey))
+                {
+                    throw Interop.Crypto.CreateOpenSslCryptographicException();
+                }
+            }
+            catch
+            {
+                pkeyHandle.Dispose();
+                throw;
+            }
+
+            return pkeyHandle;
+        }
+
         private SafeRsaHandle GetKey()
         {
             ThrowIfDisposed();    
@@ -843,6 +828,55 @@ private static int GetAlgorithmNid(HashAlgorithmName hashAlgorithmName)
             return nid;
         }
 
+        private static void ValidatePadding(RSAEncryptionPadding padding)
+        {
+            if (padding == null)
+            {
+                throw new ArgumentNullException(nameof(padding));
+            }
+
+            // There are currently two defined padding modes:
+            // * Oaep has an option (the hash algorithm)
+            // * Pkcs1 has no options
+            //
+            // Anything other than those to modes is an error,
+            // and Pkcs1 having options set is an error, so compare it to
+            // the padding struct instead of the padding mode enum.
+            if (padding.Mode != RSAEncryptionPaddingMode.Oaep &&
+                padding != RSAEncryptionPadding.Pkcs1)
+            {
+                throw PaddingModeNotSupported();
+            }
+        }
+
+        private static void ValidatePadding(RSASignaturePadding padding)
+        {
+            if (padding == null)
+            {
+                throw new ArgumentNullException(nameof(padding));
+            }
+
+            // RSASignaturePadding currently only has the mode property, so
+            // there's no need for a runtime check that PKCS#1 doesn't use
+            // nonsensical options like with RSAEncryptionPadding.
+            //
+            // This would change if we supported PSS with an MGF other than MGF-1,
+            // or with a custom salt size, or with a different MGF digest algorithm
+            // than the data digest algorithm.
+            if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
+            {
+                Debug.Assert(padding == RSASignaturePadding.Pkcs1);
+            }
+            else if (padding.Mode == RSASignaturePaddingMode.Pss)
+            {
+                Debug.Assert(padding == RSASignaturePadding.Pss);
+            }
+            else
+            {
+                throw PaddingModeNotSupported();
+            }
+        }
+
         private static Exception PaddingModeNotSupported() =>
             new CryptographicException(SR.Cryptography_InvalidPaddingMode);
 
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
index def7198deb98..ff7110583793 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
@@ -258,6 +258,23 @@ int32_t local_DSA_set0_key(DSA* dsa, BIGNUM* bnY, BIGNUM* bnX)
     return 1;
 }
 
+RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey)
+{
+    if (pkey == NULL)
+    {
+        return NULL;
+    }
+
+    RSA* rsa = EVP_PKEY_get1_RSA(pkey);
+
+    if (rsa != NULL)
+    {
+        RSA_free(rsa);
+    }
+
+    return rsa;
+}
+
 int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey)
 {
     if (!pkey)
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
index b58611ae7301..e1315499f3df 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
@@ -15,6 +15,7 @@ int32_t local_DSA_set0_pqg(DSA* dsa, BIGNUM* bnP, BIGNUM* bnQ, BIGNUM* bnG);
 void local_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX* ctx);
 EVP_CIPHER_CTX* local_EVP_CIPHER_CTX_new(void);
 int32_t local_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
+RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
 int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey);
 void local_HMAC_CTX_free(HMAC_CTX* ctx);
 HMAC_CTX* local_HMAC_CTX_new(void);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index dff6091e9eaa..47cb142d2572 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -128,6 +128,7 @@ EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void);
 int32_t EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
 void EVP_MD_CTX_free(EVP_MD_CTX* ctx);
 EVP_MD_CTX* EVP_MD_CTX_new(void);
+RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
 int32_t EVP_PKEY_up_ref(EVP_PKEY* pkey);
 void HMAC_CTX_free(HMAC_CTX* ctx);
 HMAC_CTX* HMAC_CTX_new(void);
@@ -178,6 +179,12 @@ int32_t X509_up_ref(X509* x509);
 #define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
     RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
 
+#undef EVP_PKEY_CTX_set_rsa_padding
+#define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \
+    RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL)
+
+// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here.
+
 #endif
 
 #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
@@ -355,10 +362,13 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
     REQUIRED_FUNCTION(EVP_PKEY_base_id) \
+    REQUIRED_FUNCTION(EVP_PKEY_decrypt) \
+    REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive) \
     REQUIRED_FUNCTION(EVP_PKEY_free) \
+    FALLBACK_FUNCTION(EVP_PKEY_get0_RSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_RSA) \
@@ -368,6 +378,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \
+    REQUIRED_FUNCTION(EVP_PKEY_size) \
     FALLBACK_FUNCTION(EVP_PKEY_up_ref) \
     REQUIRED_FUNCTION(EVP_rc2_cbc) \
     REQUIRED_FUNCTION(EVP_rc2_ecb) \
@@ -448,7 +459,6 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(RSA_new) \
     FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \
     RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \
-    REQUIRED_FUNCTION(RSA_private_decrypt) \
     REQUIRED_FUNCTION(RSA_private_encrypt) \
     REQUIRED_FUNCTION(RSA_public_decrypt) \
     REQUIRED_FUNCTION(RSA_public_encrypt) \
@@ -748,10 +758,13 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
 #define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
 #define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
+#define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr
+#define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr
 #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr
 #define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr
 #define EVP_PKEY_derive EVP_PKEY_derive_ptr
 #define EVP_PKEY_free EVP_PKEY_free_ptr
+#define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr
 #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
 #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
 #define EVP_PKEY_get1_RSA EVP_PKEY_get1_RSA_ptr
@@ -761,6 +774,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr
 #define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr
 #define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr
+#define EVP_PKEY_size EVP_PKEY_size_ptr
 #define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr
 #define EVP_rc2_cbc EVP_rc2_cbc_ptr
 #define EVP_rc2_ecb EVP_rc2_ecb_ptr
@@ -841,7 +855,6 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_new RSA_new_ptr
 #define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr
 #define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr
-#define RSA_private_decrypt RSA_private_decrypt_ptr
 #define RSA_private_encrypt RSA_private_encrypt_ptr
 #define RSA_public_decrypt RSA_public_decrypt_ptr
 #define RSA_public_encrypt RSA_public_encrypt_ptr
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
index 4d479dde6c43..f232b382ead4 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+#include <assert.h>
 #include "pal_evp_pkey.h"
 
 EVP_PKEY* CryptoNative_EvpPkeyCreate()
@@ -17,6 +18,12 @@ void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey)
     }
 }
 
+int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey)
+{
+    assert(pkey != NULL);
+    return EVP_PKEY_size(pkey);
+}
+
 int32_t CryptoNative_UpRefEvpPkey(EVP_PKEY* pkey)
 {
     if (!pkey)
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
index 7baf997d8dbc..750282efdb65 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h
@@ -24,6 +24,11 @@ Always succeeds.
 */
 DLLEXPORT void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey);
 
+/*
+Returns the maximum size, in bytes, of an operation with the provided key.
+*/
+DLLEXPORT int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey);
+
 /*
 Used by System.Security.Cryptography.X509Certificates' OpenSslX509CertificateReader when
 duplicating a private key context as part of duplicating the Pal object.
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
index 29f9238ce99b..6235c905dba5 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
@@ -3,6 +3,10 @@
 // See the LICENSE file in the project root for more information.
 
 #include "pal_evp_pkey_rsa.h"
+#include "pal_utilities.h"
+#include <assert.h>
+
+static int HasNoPrivateKey(RSA* rsa);
 
 EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
 {
@@ -16,8 +20,7 @@ EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
     EVP_PKEY* pkey = NULL;
     EVP_PKEY* ret = NULL;
 
-    if (EVP_PKEY_keygen_init(ctx) == 1 &&
-        EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
+    if (EVP_PKEY_keygen_init(ctx) == 1 && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
         EVP_PKEY_keygen(ctx, &pkey) == 1)
     {
         ret = pkey;
@@ -33,6 +36,82 @@ EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
     return ret;
 }
 
+int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
+                                const uint8_t* source,
+                                int32_t sourceLen,
+                                RsaPaddingMode padding,
+                                const EVP_MD* digest,
+                                uint8_t* destination,
+                                int32_t destinationLen)
+{
+    assert(pkey != NULL);
+    assert(source != NULL);
+    assert(destination != NULL);
+    assert(padding >= RsaPaddingPkcs1 && padding <= RsaPaddingOaepOrPss);
+    assert(digest != NULL || padding == RsaPaddingPkcs1);
+
+    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
+
+    int ret = -1;
+
+    if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) <= 0)
+    {
+        goto done;
+    }
+
+    if (padding == RsaPaddingPkcs1)
+    {
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+        {
+            goto done;
+        }
+    }
+    else
+    {
+        assert(padding == RsaPaddingOaepOrPss);
+
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+        {
+            goto done;
+        }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+        if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, digest) <= 0)
+#pragma clang diagnostic pop
+        {
+            goto done;
+        }
+    }
+
+    // This check may no longer be needed on OpenSSL 3.0
+    {
+        RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+
+        if (rsa == NULL || HasNoPrivateKey(rsa))
+        {
+            ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
+            ret = -1;
+            goto done;
+        }
+    }
+
+    size_t written = Int32ToSizeT(destinationLen);
+
+    if (EVP_PKEY_decrypt(ctx, destination, &written, source, Int32ToSizeT(sourceLen)) > 0)
+    {
+        ret = SizeTToInt32(written);
+    }
+
+done:
+    if (ctx != NULL)
+    {
+        EVP_PKEY_CTX_free(ctx);
+    }
+
+    return ret;
+}
+
 RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey)
 {
     return EVP_PKEY_get1_RSA(pkey);
@@ -42,3 +121,57 @@ int32_t CryptoNative_EvpPkeySetRsa(EVP_PKEY* pkey, RSA* rsa)
 {
     return EVP_PKEY_set1_RSA(pkey, rsa);
 }
+
+static int HasNoPrivateKey(RSA* rsa)
+{
+    if (rsa == NULL)
+        return 1;
+
+    // Shared pointer, don't free.
+    const RSA_METHOD* meth = RSA_get_method(rsa);
+
+    // The method has descibed itself as having the private key external to the structure.
+    // That doesn't mean it's actually present, but we can't tell.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+    if (RSA_meth_get_flags((RSA_METHOD*)meth) & RSA_FLAG_EXT_PKEY)
+#pragma clang diagnostic pop
+    {
+        return 0;
+    }
+
+    // In the event that there's a middle-ground where we report failure when success is expected,
+    // one could do something like check if the RSA_METHOD intercepts all private key operations:
+    //
+    // * meth->rsa_priv_enc
+    // * meth->rsa_priv_dec
+    // * meth->rsa_sign (in 1.0.x this is only respected if the RSA_FLAG_SIGN_VER flag is asserted)
+    //
+    // But, for now, leave it at the EXT_PKEY flag test.
+
+    // The module is documented as accepting either d or the full set of CRT parameters (p, q, dp, dq, qInv)
+    // So if we see d, we're good. Otherwise, if any of the rest are missing, we're public-only.
+    const BIGNUM* d;
+    RSA_get0_key(rsa, NULL, NULL, &d);
+
+    if (d != NULL)
+    {
+        return 0;
+    }
+
+    const BIGNUM* p;
+    const BIGNUM* q;
+    const BIGNUM* dmp1;
+    const BIGNUM* dmq1;
+    const BIGNUM* iqmp;
+
+    RSA_get0_factors(rsa, &p, &q);
+    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+
+    if (p == NULL || q == NULL || dmp1 == NULL || dmq1 == NULL || iqmp == NULL)
+    {
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
index 1fda1494146b..d220065adf77 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
@@ -2,15 +2,38 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-#include "pal_types.h"
-#include "pal_compiler.h"
 #include "opensslshim.h"
+#include "pal_compiler.h"
+#include "pal_types.h"
+
+/*
+Padding options for RSA.
+Matches RSAEncryptionPaddingMode / RSASignaturePaddingMode.
+*/
+typedef enum
+{
+    RsaPaddingPkcs1,
+    RsaPaddingOaepOrPss,
+} RsaPaddingMode;
 
 /*
 Creates an RSA key of the requested size.
 */
 DLLEXPORT EVP_PKEY* CryptoNative_RsaGenerateKey(int32_t keySize);
 
+/*
+Decrypt source into destination using the specified RSA key (wrapped in an EVP_PKEY) and padding/digest options.
+
+Returns the number of bytes written to destination, -1 on error.
+*/
+DLLEXPORT int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
+                                          const uint8_t* source,
+                                          int32_t sourceLen,
+                                          RsaPaddingMode padding,
+                                          const EVP_MD* digest,
+                                          uint8_t* destination,
+                                          int32_t destinationLen);
+
 /*
 Shims the EVP_PKEY_get1_RSA method.
 
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
index 080027de0e14..0c635dfca756 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
@@ -109,19 +109,6 @@ CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RS
     return RSA_public_encrypt(flen, from, to, rsa, openSslPadding);
 }
 
-int32_t
-CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding)
-{
-    if (HasNoPrivateKey(rsa))
-    {
-        ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
-        return -1;
-    }
-
-    int openSslPadding = GetOpenSslPadding(padding);
-    return RSA_private_decrypt(flen, from, to, rsa, openSslPadding);
-}
-
 int32_t CryptoNative_RsaSignPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa)
 {
     if (HasNoPrivateKey(rsa))
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
index 1c0bc2df4755..30d7d9fa59f6 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
@@ -55,14 +55,6 @@ Returns the size of the signature, or -1 on error.
 DLLEXPORT int32_t
 CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
 
-/*
-Shims the RSA_private_decrypt method.
-
-Returns the size of the signature, or -1 on error.
-*/
-DLLEXPORT int32_t
-CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding);
-
 /*
 Shims RSA_private_encrypt with a fixed value of RSA_NO_PADDING.
 
diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
index ff8e91e7c939..589ff882bfd1 100644
--- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
+++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs
@@ -12,40 +12,16 @@ namespace Internal.Cryptography
 {
     internal static partial class HashProviderDispenser
     {
-        public static HashProvider CreateHashProvider(string hashAlgorithmId)
+        internal static HashProvider CreateHashProvider(string hashAlgorithmId)
         {
-            switch (hashAlgorithmId)
-            {
-                case HashAlgorithmNames.SHA1:
-                    return new EvpHashProvider(Interop.Crypto.EvpSha1());
-                case HashAlgorithmNames.SHA256:
-                    return new EvpHashProvider(Interop.Crypto.EvpSha256());
-                case HashAlgorithmNames.SHA384:
-                    return new EvpHashProvider(Interop.Crypto.EvpSha384());
-                case HashAlgorithmNames.SHA512:
-                    return new EvpHashProvider(Interop.Crypto.EvpSha512());
-                case HashAlgorithmNames.MD5:
-                    return new EvpHashProvider(Interop.Crypto.EvpMd5());
-            }
-            throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
+            IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
+            return new EvpHashProvider(evpType);
         }
 
-        public static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
+        internal static unsafe HashProvider CreateMacProvider(string hashAlgorithmId, byte[] key)
         {
-            switch (hashAlgorithmId)
-            {
-                case HashAlgorithmNames.SHA1:
-                    return new HmacHashProvider(Interop.Crypto.EvpSha1(), key);
-                case HashAlgorithmNames.SHA256:
-                    return new HmacHashProvider(Interop.Crypto.EvpSha256(), key);
-                case HashAlgorithmNames.SHA384:
-                    return new HmacHashProvider(Interop.Crypto.EvpSha384(), key);
-                case HashAlgorithmNames.SHA512:
-                    return new HmacHashProvider(Interop.Crypto.EvpSha512(), key);
-                case HashAlgorithmNames.MD5:
-                    return new HmacHashProvider(Interop.Crypto.EvpMd5(), key);
-            }
-            throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
+            IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId);
+            return new HmacHashProvider(evpType, key);
         }
 
         // -----------------------------
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
index 6ad2b78e016e..c6e8b5b69a17 100644
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
@@ -513,6 +513,9 @@
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs">
+      <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs</Link>
     </Compile>
diff --git a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
index 17ab176ec2f5..dbbc4848e863 100644
--- a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
+++ b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
@@ -57,6 +57,9 @@
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs">
+      <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.DigestAlgs.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.cs</Link>
     </Compile>

From 7111a92546253d6fc857f7cad8b0bff425df0798 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Fri, 2 Apr 2021 09:10:08 -0700
Subject: [PATCH 03/11] Use EVP_PKEY for RSA signing operations

With this change all RSA private key operations (excluding import/export) use the EVP_PKEY APIs.

* RSAPaddingProcessor is no longer used in conjunction with the private keys, on Linux.
* The pal_rsa.c copy of HasPrivateKey has been removed.
---
 .../Interop.EvpPkey.Rsa.cs                    |  35 ++++++
 .../Interop.Rsa.cs                            |  20 ----
 .../Security/Cryptography/RSAOpenSsl.cs       |  87 ++++-----------
 .../opensslshim.h                             |  19 +++-
 .../pal_evp_pkey_rsa.c                        |  76 ++++++++++++-
 .../pal_evp_pkey_rsa.h                        |  14 +++
 .../pal_rsa.c                                 | 100 ------------------
 7 files changed, 156 insertions(+), 195 deletions(-)

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
index f023ced7f969..6aab764cff14 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
@@ -63,6 +63,41 @@ ref MemoryMarshal.GetReference(destination),
             return written;
         }
 
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSignHash")]
+        private static extern int CryptoNative_RsaSignHash(
+            SafeEvpPKeyHandle pkey,
+            RSASignaturePaddingMode paddingMode,
+            IntPtr digestAlgorithm,
+            ref byte hash,
+            int hashLength,
+            ref byte destination,
+            int destinationLength);
+
+        internal static int RsaSignHash(
+            SafeEvpPKeyHandle pkey,
+            RSASignaturePaddingMode paddingMode,
+            IntPtr digestAlgorithm,
+            ReadOnlySpan<byte> hash,
+            Span<byte> destination)
+        {
+            int written = CryptoNative_RsaSignHash(
+                pkey,
+                paddingMode,
+                digestAlgorithm,
+                ref MemoryMarshal.GetReference(hash),
+                hash.Length,
+                ref MemoryMarshal.GetReference(destination),
+                destination.Length);
+
+            if (written < 0)
+            {
+                Debug.Assert(written == -1);
+                throw CreateOpenSslCryptographicException();
+            }
+
+            return written;
+        }
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
         internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
 
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
index 5ad534a8f26d..b2f250ffe99f 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
@@ -44,19 +44,6 @@ private extern static int RsaPublicEncrypt(
             SafeRsaHandle rsa,
             RsaPadding padding);
 
-        internal static int RsaSignPrimitive(
-            ReadOnlySpan<byte> from,
-            Span<byte> to,
-            SafeRsaHandle rsa) =>
-            RsaSignPrimitive(from.Length, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa);
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSignPrimitive")]
-        private static extern int RsaSignPrimitive(
-            int flen,
-            ref byte from,
-            ref byte to,
-            SafeRsaHandle rsa);
-
         internal static int RsaVerificationPrimitive(
             ReadOnlySpan<byte> from,
             Span<byte> to,
@@ -73,13 +60,6 @@ private static extern int RsaVerificationPrimitive(
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSize")]
         internal static extern int RsaSize(SafeRsaHandle rsa);
 
-        internal static bool RsaSign(int type, ReadOnlySpan<byte> m, int m_len, Span<byte> sigret, out int siglen, SafeRsaHandle rsa) =>
-            RsaSign(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigret), out siglen, rsa);
-
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSign")]
-        [return: MarshalAs(UnmanagedType.Bool)]
-        private static extern bool RsaSign(int type, ref byte m, int m_len, ref byte sigret, out int siglen, SafeRsaHandle rsa);
-
         internal static bool RsaVerify(int type, ReadOnlySpan<byte> m, ReadOnlySpan<byte> sigbuf, SafeRsaHandle rsa)
         {
             bool ret = RsaVerify(
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
index 87e31b9dde42..225968fc50ad 100644
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
@@ -655,84 +655,33 @@ private bool TrySignHash(
         {
             Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name));
             Debug.Assert(padding != null);
+            ValidatePadding(padding);
 
             signature = null;
 
-            // Do not factor out getting _key.Value, since the key creation should not happen on
-            // invalid padding modes.
+            IntPtr digestAlgorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithm.Name);
+            SafeEvpPKeyHandle key = GetPKey();
+            int bytesRequired = Interop.Crypto.EvpPKeySize(key);
 
-            if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
+            if (allocateSignature)
             {
-                int algorithmNid = GetAlgorithmNid(hashAlgorithm);
-                SafeRsaHandle rsa = GetKey();
-
-                int bytesRequired = Interop.Crypto.RsaSize(rsa);
-
-                if (allocateSignature)
-                {
-                    Debug.Assert(destination.Length == 0);
-                    signature = new byte[bytesRequired];
-                    destination = signature;
-                }
-
-                if (destination.Length < bytesRequired)
-                {
-                    bytesWritten = 0;
-                    return false;
-                }
-
-                if (!Interop.Crypto.RsaSign(algorithmNid, hash, hash.Length, destination, out int signatureSize, rsa))
-                {
-                    throw Interop.Crypto.CreateOpenSslCryptographicException();
-                }
-
-                Debug.Assert(
-                    signatureSize == bytesRequired,
-                    $"RSA_sign reported signatureSize was {signatureSize}, when {bytesRequired} was expected");
-
-                bytesWritten = signatureSize;
-                return true;
+                Debug.Assert(destination.Length == 0);
+                signature = new byte[bytesRequired];
+                destination = signature;
             }
-            else if (padding.Mode == RSASignaturePaddingMode.Pss)
+            else if (destination.Length < bytesRequired)
             {
-                RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
-                SafeRsaHandle rsa = GetKey();
-
-                int bytesRequired = Interop.Crypto.RsaSize(rsa);
-
-                if (allocateSignature)
-                {
-                    Debug.Assert(destination.Length == 0);
-                    signature = new byte[bytesRequired];
-                    destination = signature;
-                }
-
-                if (destination.Length < bytesRequired)
-                {
-                    bytesWritten = 0;
-                    return false;
-                }
-
-                byte[] pssRented = CryptoPool.Rent(bytesRequired);
-                Span<byte> pssBytes = new Span<byte>(pssRented, 0, bytesRequired);
-
-                processor.EncodePss(hash, pssBytes, KeySize);
-
-                int ret = Interop.Crypto.RsaSignPrimitive(pssBytes, destination, rsa);
-
-                CryptoPool.Return(pssRented, bytesRequired);
-
-                CheckReturn(ret);
-
-                Debug.Assert(
-                    ret == bytesRequired,
-                    $"RSA_private_encrypt returned {ret} when {bytesRequired} was expected");
-
-                bytesWritten = ret;
-                return true;
+                bytesWritten = 0;
+                return false;
             }
 
-            throw PaddingModeNotSupported();
+            int written = Interop.Crypto.RsaSignHash(key, padding.Mode, digestAlgorithm, hash, destination);
+            Debug.Assert(written == bytesRequired);
+            bytesWritten = written;
+
+            // Until EVP_PKEY is what gets stored, free the temporary key handle.
+            key.Dispose();
+            return true;
         }
 
         public override bool VerifyHash(
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index 47cb142d2572..4c15914d25be 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -179,11 +179,15 @@ int32_t X509_up_ref(X509* x509);
 #define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
     RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
 
+// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here.
+
 #undef EVP_PKEY_CTX_set_rsa_padding
 #define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \
     RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL)
 
-// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here.
+#undef EVP_PKEY_CTX_set_rsa_pss_saltlen
+#define EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, len) \
+    RSA_pkey_ctx_ctrl(ctx, (EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY), EVP_PKEY_CTRL_RSA_PSS_SALTLEN, len, NULL)
 
 #endif
 
@@ -209,6 +213,11 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx,
 void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsigned int* len);
 #endif
 
+// The value -1 has the correct meaning on 1.0.x, but the constant wasn't named.
+#ifndef RSA_PSS_SALTLEN_DIGEST
+#define RSA_PSS_SALTLEN_DIGEST -1
+#endif
+
 #define API_EXISTS(fn) (fn != NULL)
 
 // List of all functions from the libssl that are used in the System.Security.Cryptography.Native.
@@ -378,6 +387,8 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \
+    REQUIRED_FUNCTION(EVP_PKEY_sign) \
+    REQUIRED_FUNCTION(EVP_PKEY_sign_init) \
     REQUIRED_FUNCTION(EVP_PKEY_size) \
     FALLBACK_FUNCTION(EVP_PKEY_up_ref) \
     REQUIRED_FUNCTION(EVP_rc2_cbc) \
@@ -459,14 +470,12 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(RSA_new) \
     FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \
     RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \
-    REQUIRED_FUNCTION(RSA_private_encrypt) \
     REQUIRED_FUNCTION(RSA_public_decrypt) \
     REQUIRED_FUNCTION(RSA_public_encrypt) \
     FALLBACK_FUNCTION(RSA_set0_crt_params) \
     FALLBACK_FUNCTION(RSA_set0_factors) \
     FALLBACK_FUNCTION(RSA_set0_key) \
     REQUIRED_FUNCTION(RSA_set_method) \
-    REQUIRED_FUNCTION(RSA_sign) \
     REQUIRED_FUNCTION(RSA_size) \
     REQUIRED_FUNCTION(RSA_up_ref) \
     REQUIRED_FUNCTION(RSA_verify) \
@@ -774,6 +783,8 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr
 #define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr
 #define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr
+#define EVP_PKEY_sign_init EVP_PKEY_sign_init_ptr
+#define EVP_PKEY_sign EVP_PKEY_sign_ptr
 #define EVP_PKEY_size EVP_PKEY_size_ptr
 #define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr
 #define EVP_rc2_cbc EVP_rc2_cbc_ptr
@@ -855,14 +866,12 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_new RSA_new_ptr
 #define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr
 #define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr
-#define RSA_private_encrypt RSA_private_encrypt_ptr
 #define RSA_public_decrypt RSA_public_decrypt_ptr
 #define RSA_public_encrypt RSA_public_encrypt_ptr
 #define RSA_set0_crt_params RSA_set0_crt_params_ptr
 #define RSA_set0_factors RSA_set0_factors_ptr
 #define RSA_set0_key RSA_set0_key_ptr
 #define RSA_set_method RSA_set_method_ptr
-#define RSA_sign RSA_sign_ptr
 #define RSA_size RSA_size_ptr
 #define RSA_up_ref RSA_up_ref_ptr
 #define RSA_verify RSA_verify_ptr
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
index 6235c905dba5..68b6a34a5dc4 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
@@ -91,7 +91,6 @@ int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
         if (rsa == NULL || HasNoPrivateKey(rsa))
         {
             ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
-            ret = -1;
             goto done;
         }
     }
@@ -112,6 +111,81 @@ int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
     return ret;
 }
 
+int32_t CryptoNative_RsaSignHash(EVP_PKEY* pkey,
+                                 RsaPaddingMode padding,
+                                 const EVP_MD* digest,
+                                 const uint8_t* hash,
+                                 int32_t hashLen,
+                                 uint8_t* destination,
+                                 int32_t destinationLen)
+{
+    assert(pkey != NULL);
+    assert(destination != NULL);
+    assert(padding >= RsaPaddingPkcs1 && padding <= RsaPaddingOaepOrPss);
+    assert(digest != NULL || padding == RsaPaddingPkcs1);
+
+    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
+
+    int ret = -1;
+
+    if (ctx == NULL || EVP_PKEY_sign_init(ctx) <= 0)
+    {
+        goto done;
+    }
+
+    if (padding == RsaPaddingPkcs1)
+    {
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+        {
+            goto done;
+        }
+    }
+    else
+    {
+        assert(padding == RsaPaddingOaepOrPss);
+
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0 ||
+            EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_DIGEST) <= 0)
+        {
+            goto done;
+        }
+    }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+    if (EVP_PKEY_CTX_set_signature_md(ctx, digest) <= 0)
+#pragma clang diagnostic pop
+    {
+        goto done;
+    }
+
+    // This check may no longer be needed on OpenSSL 3.0
+    {
+        RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+
+        if (rsa == NULL || HasNoPrivateKey(rsa))
+        {
+            ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
+            goto done;
+        }
+    }
+
+    size_t written = Int32ToSizeT(destinationLen);
+
+    if (EVP_PKEY_sign(ctx, destination, &written, hash, Int32ToSizeT(hashLen)) > 0)
+    {
+        ret = SizeTToInt32(written);
+    }
+
+done:
+    if (ctx != NULL)
+    {
+        EVP_PKEY_CTX_free(ctx);
+    }
+
+    return ret;
+}
+
 RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey)
 {
     return EVP_PKEY_get1_RSA(pkey);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
index d220065adf77..f811523f7817 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
@@ -34,6 +34,20 @@ DLLEXPORT int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
                                           uint8_t* destination,
                                           int32_t destinationLen);
 
+/*
+Complete the RSA signature generation for the specified hash using the provided RSA key
+(wrapped in an EVP_PKEY) and padding/digest options.
+
+Returns the number of bytes written to destination, -1 on error.
+*/
+DLLEXPORT int32_t CryptoNative_RsaSignHash(EVP_PKEY* pkey,
+                                           RsaPaddingMode padding,
+                                           const EVP_MD* digest,
+                                           const uint8_t* hash,
+                                           int32_t hashLen,
+                                           uint8_t* destination,
+                                           int32_t destinationLen);
+
 /*
 Shims the EVP_PKEY_get1_RSA method.
 
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
index 0c635dfca756..43268e88e165 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
@@ -48,60 +48,6 @@ static int GetOpenSslPadding(RsaPadding padding)
     }
 }
 
-static int HasNoPrivateKey(RSA* rsa)
-{
-    if (rsa == NULL)
-        return 1;
-
-    // Shared pointer, don't free.
-    const RSA_METHOD* meth = RSA_get_method(rsa);
-
-    // The method has descibed itself as having the private key external to the structure.
-    // That doesn't mean it's actually present, but we can't tell.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-    if (RSA_meth_get_flags((RSA_METHOD*)meth) & RSA_FLAG_EXT_PKEY)
-#pragma clang diagnostic pop
-    {
-        return 0;
-    }
-
-    // In the event that there's a middle-ground where we report failure when success is expected,
-    // one could do something like check if the RSA_METHOD intercepts all private key operations:
-    //
-    // * meth->rsa_priv_enc
-    // * meth->rsa_priv_dec
-    // * meth->rsa_sign (in 1.0.x this is only respected if the RSA_FLAG_SIGN_VER flag is asserted)
-    //
-    // But, for now, leave it at the EXT_PKEY flag test.
-
-    // The module is documented as accepting either d or the full set of CRT parameters (p, q, dp, dq, qInv)
-    // So if we see d, we're good. Otherwise, if any of the rest are missing, we're public-only.
-    const BIGNUM* d;
-    RSA_get0_key(rsa, NULL, NULL, &d);
-
-    if (d != NULL)
-    {
-        return 0;
-    }
-
-    const BIGNUM* p;
-    const BIGNUM* q;
-    const BIGNUM* dmp1;
-    const BIGNUM* dmq1;
-    const BIGNUM* iqmp;
-
-    RSA_get0_factors(rsa, &p, &q);
-    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
-
-    if (p == NULL || q == NULL || dmp1 == NULL || dmq1 == NULL || iqmp == NULL)
-    {
-        return 1;
-    }
-
-    return 0;
-}
-
 int32_t
 CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding)
 {
@@ -109,17 +55,6 @@ CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RS
     return RSA_public_encrypt(flen, from, to, rsa, openSslPadding);
 }
 
-int32_t CryptoNative_RsaSignPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa)
-{
-    if (HasNoPrivateKey(rsa))
-    {
-        ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_ENCRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
-        return -1;
-    }
-
-    return RSA_private_encrypt(flen, from, to, rsa, RSA_NO_PADDING);
-}
-
 int32_t CryptoNative_RsaVerificationPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa)
 {
     return RSA_public_decrypt(flen, from, to, rsa, RSA_NO_PADDING);
@@ -130,41 +65,6 @@ int32_t CryptoNative_RsaSize(RSA* rsa)
     return RSA_size(rsa);
 }
 
-int32_t
-CryptoNative_RsaSign(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa)
-{
-    if (siglen == NULL)
-    {
-        assert(false);
-        return 0;
-    }
-
-    *siglen = 0;
-
-    if (HasNoPrivateKey(rsa))
-    {
-        ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_VALUE_MISSING, __FILE__, __LINE__);
-        return 0;
-    }
-
-    // Shared pointer to the metadata about the message digest algorithm
-    const EVP_MD* digest = EVP_get_digestbynid(type);
-
-    // If the digest itself isn't known then RSA_R_UNKNOWN_ALGORITHM_TYPE will get reported, but
-    // we have to check that the digest size matches what we expect.
-    if (digest != NULL && mlen != EVP_MD_size(digest))
-    {
-        ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_INVALID_MESSAGE_LENGTH, __FILE__, __LINE__);
-        return 0;
-    }
-
-    unsigned int unsignedSigLen = 0;
-    int32_t ret = RSA_sign(type, m, Int32ToUint32(mlen), sigret, &unsignedSigLen, rsa);
-    assert(unsignedSigLen <= INT32_MAX);
-    *siglen = (int32_t)unsignedSigLen;
-    return ret;
-}
-
 int32_t
 CryptoNative_RsaVerify(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigbuf, int32_t siglen, RSA* rsa)
 {

From 49dc6e515d9ec0db1841e5d2d86f52916d35f667 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Mon, 5 Apr 2021 11:07:29 -0700
Subject: [PATCH 04/11] Support compiling against OpenSSL 3 headers

Building against OpenSSL 3's headers fails to compile, as X509_V_ERR_INVALID_CA has changed from 24 to 79, tripping a static assert.

* Rename the managed X509VerifyStatusCode enum to X509VerifyStatusCodeUniversal, to represent the name/values that are present in all current versions of OpenSSL (1.0.2, 1.1.1, 3.0 alpha)
* Add new enums for the name/value pairs that are unique to a given version
* Add an X509VerifyStatusCode struct that just wraps the int and is a faux-union of the various enums
* Use the OpenSSL runtime version to determine which mapping table to use (after the Universal table fails)

In addition to that, there are a few const-related changes in the 3.0 headers that are addressed.

`corefx/src/Native$ ./build_native.sh -portablebuild=false` on systems where find_package(OpenSSL) maps to 3.0 succeeds with these changes.  Portable builds still fail.

Not all tests pass with OpenSSL 3.0 (alpha 13) with these changes, but it does reduce to three categories of error:

* ICryptoTransform reset/reuse tests fail (OpenSSL regression is open)
* DSA small key generation fails (OpenSSL has fixed the regression for the next alpha/beta release)
* Some OuterLoop X.509 tests are failing as positively revoked when they expect ambiguous revocation states (investigation pending)
---
 .../Interop.OCSP.cs                           |   4 +-
 .../Interop.X509.cs                           | 109 +++++++++++-
 .../pal_evp_pkey_rsa.c                        |   8 +-
 .../pal_x509.c                                |  24 ++-
 .../pal_x509.h                                |  29 +++-
 .../Pal.Unix/OpenSslX509ChainProcessor.cs     | 155 ++++++++++++------
 6 files changed, 266 insertions(+), 63 deletions(-)

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs
index bcf9e2af48ef..8be162e284b9 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs
@@ -43,7 +43,7 @@ internal static X509VerifyStatusCode X509ChainGetCachedOcspStatus(SafeX509StoreC
         {
             X509VerifyStatusCode response = CryptoNative_X509ChainGetCachedOcspStatus(ctx, cachePath);
 
-            if (response < 0)
+            if (response.Code < 0)
             {
                 Debug.Fail($"Unexpected response from X509ChainGetCachedOcspSuccess: {response}");
                 throw new CryptographicException();
@@ -67,7 +67,7 @@ internal static X509VerifyStatusCode X509ChainVerifyOcsp(
         {
             X509VerifyStatusCode response = CryptoNative_X509ChainVerifyOcsp(ctx, req, resp, cachePath);
 
-            if (response < 0)
+            if (response.Code < 0)
             {
                 Debug.Fail($"Unexpected response from X509ChainGetCachedOcspSuccess: {response}");
                 throw new CryptographicException();
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs
index 8ffc70af6a54..99747c276b8d 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs
@@ -216,13 +216,13 @@ internal static bool X509StoreCtxRebuildChain(SafeX509StoreCtxHandle ctx)
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509StoreCtxSetVerifyCallback")]
         internal static extern void X509StoreCtxSetVerifyCallback(SafeX509StoreCtxHandle ctx, X509StoreVerifyCallback callback);
 
-        internal static string GetX509VerifyCertErrorString(X509VerifyStatusCode n)
+        internal static string GetX509VerifyCertErrorString(int n)
         {
             return Marshal.PtrToStringAnsi(X509VerifyCertErrorString(n));
         }
 
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509VerifyCertErrorString")]
-        private static extern IntPtr X509VerifyCertErrorString(X509VerifyStatusCode n);
+        private static extern IntPtr X509VerifyCertErrorString(int n);
 
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509CrlDestroy")]
         internal static extern void X509CrlDestroy(IntPtr a);
@@ -239,11 +239,13 @@ internal static string GetX509VerifyCertErrorString(X509VerifyStatusCode n)
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EncodeX509SubjectPublicKeyInfo")]
         internal static extern int EncodeX509SubjectPublicKeyInfo(SafeX509Handle x509, byte[] buf);
 
-        internal enum X509VerifyStatusCode : int
+        internal enum X509VerifyStatusCodeUniversal
         {
             X509_V_OK = 0,
+            X509_V_ERR_UNSPECIFIED = 1,
             X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2,
             X509_V_ERR_UNABLE_TO_GET_CRL = 3,
+            X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4,
             X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5,
             X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6,
             X509_V_ERR_CERT_SIGNATURE_FAILURE = 7,
@@ -263,18 +265,25 @@ internal enum X509VerifyStatusCode : int
             X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21,
             X509_V_ERR_CERT_CHAIN_TOO_LONG = 22,
             X509_V_ERR_CERT_REVOKED = 23,
-            X509_V_ERR_INVALID_CA = 24,
+
+            // Code 24 varies.
+
             X509_V_ERR_PATH_LENGTH_EXCEEDED = 25,
             X509_V_ERR_INVALID_PURPOSE = 26,
             X509_V_ERR_CERT_UNTRUSTED = 27,
             X509_V_ERR_CERT_REJECTED = 28,
+            X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29,
+            X509_V_ERR_AKID_SKID_MISMATCH = 30,
+            X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH = 31,
             X509_V_ERR_KEYUSAGE_NO_CERTSIGN = 32,
             X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER = 33,
             X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION = 34,
             X509_V_ERR_KEYUSAGE_NO_CRL_SIGN = 35,
             X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION = 36,
             X509_V_ERR_INVALID_NON_CA = 37,
+            X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED = 38,
             X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE = 39,
+            X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED = 40,
             X509_V_ERR_INVALID_EXTENSION = 41,
             X509_V_ERR_INVALID_POLICY_EXTENSION = 42,
             X509_V_ERR_NO_EXPLICIT_POLICY = 43,
@@ -289,7 +298,6 @@ internal enum X509VerifyStatusCode : int
             X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 52,
             X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 53,
             X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 54,
-            X509_V_ERR_PATH_LOOP = 55,
             X509_V_ERR_SUITE_B_INVALID_VERSION = 56,
             X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 57,
             X509_V_ERR_SUITE_B_INVALID_CURVE = 58,
@@ -299,6 +307,41 @@ internal enum X509VerifyStatusCode : int
             X509_V_ERR_HOSTNAME_MISMATCH = 62,
             X509_V_ERR_EMAIL_MISMATCH = 63,
             X509_V_ERR_IP_ADDRESS_MISMATCH = 64,
+        }
+        internal enum X509VerifyStatusCode102
+        {
+            X509_V_ERR_INVALID_CA = 24,
+
+            X509_V_ERR_INVALID_CALL = 65,
+            X509_V_ERR_STORE_LOOKUP = 66,
+            X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION = 67,
+        }
+
+        internal enum X509VerifyStatusCode111
+        {
+            X509_V_ERR_INVALID_CA = 24,
+
+            X509_V_ERR_DANE_NO_MATCH = 65,
+            X509_V_ERR_EE_KEY_TOO_SMALL = 66,
+            X509_V_ERR_CA_KEY_TOO_SMALL = 67,
+            X509_V_ERR_CA_MD_TOO_WEAK = 68,
+            X509_V_ERR_INVALID_CALL = 69,
+            X509_V_ERR_STORE_LOOKUP = 70,
+            X509_V_ERR_NO_VALID_SCTS = 71,
+            X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION = 72,
+            X509_V_ERR_OCSP_VERIFY_NEEDED = 73,
+            X509_V_ERR_OCSP_VERIFY_FAILED = 74,
+            X509_V_ERR_OCSP_CERT_UNKNOWN = 75,
+            X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH = 76,
+            X509_V_ERR_NO_ISSUER_PUBLIC_KEY = 77,
+            X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM = 78,
+            X509_V_ERR_EC_KEY_EXPLICIT_PARAMS = 79,
+        }
+
+        internal enum X509VerifyStatusCode30
+        {
+            X509_V_ERR_NO_ISSUER_PUBLIC_KEY = 24,
+
             X509_V_ERR_DANE_NO_MATCH = 65,
             X509_V_ERR_EE_KEY_TOO_SMALL = 66,
             X509_V_ERR_CA_KEY_TOO_SMALL = 67,
@@ -310,6 +353,62 @@ internal enum X509VerifyStatusCode : int
             X509_V_ERR_OCSP_VERIFY_NEEDED = 73,
             X509_V_ERR_OCSP_VERIFY_FAILED = 74,
             X509_V_ERR_OCSP_CERT_UNKNOWN = 75,
+            X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM = 76,
+            X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH = 77,
+            X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY = 78,
+            X509_V_ERR_INVALID_CA = 79,
+            X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA = 80,
+            X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN = 81,
+            X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA = 82,
+            X509_V_ERR_ISSUER_NAME_EMPTY = 83,
+            X509_V_ERR_SUBJECT_NAME_EMPTY = 84,
+            X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER = 85,
+            X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER = 86,
+            X509_V_ERR_EMPTY_SUBJECT_ALT_NAME = 87,
+            X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL = 88,
+            X509_V_ERR_CA_BCONS_NOT_CRITICAL = 89,
+            X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL = 90,
+            X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL = 91,
+            X509_V_ERR_CA_CERT_MISSING_KEY_USAGE = 92,
+            X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3 = 93,
+            X509_V_ERR_EC_KEY_EXPLICIT_PARAMS = 94,
+        }
+
+        internal readonly struct X509VerifyStatusCode : IEquatable<X509VerifyStatusCode>
+        {
+            internal static readonly X509VerifyStatusCode X509_V_OK = X509VerifyStatusCodeUniversal.X509_V_OK;
+
+            public int Code { get; }
+
+            internal X509VerifyStatusCode(int code)
+            {
+                Code = code;
+            }
+
+            public X509VerifyStatusCodeUniversal UniversalCode => (X509VerifyStatusCodeUniversal)Code;
+            public X509VerifyStatusCode102 Code102 => (X509VerifyStatusCode102)Code;
+            public X509VerifyStatusCode111 Code111 => (X509VerifyStatusCode111)Code;
+            public X509VerifyStatusCode30 Code30 => (X509VerifyStatusCode30)Code;
+
+            public bool Equals(X509VerifyStatusCode other) => Code == other.Code;
+
+            public override bool Equals(object obj) => obj is X509VerifyStatusCode other && Equals(other);
+
+            public override int GetHashCode() => Code.GetHashCode();
+
+            public static bool operator ==(X509VerifyStatusCode left, X509VerifyStatusCode right) => left.Equals(right);
+
+            public static bool operator !=(X509VerifyStatusCode left, X509VerifyStatusCode right) => !left.Equals(right);
+
+            public static explicit operator X509VerifyStatusCode(int code)
+            {
+                return new X509VerifyStatusCode(code);
+            }
+
+            public static implicit operator X509VerifyStatusCode(X509VerifyStatusCodeUniversal code)
+            {
+                return new X509VerifyStatusCode((int)code);
+            }
         }
     }
 }
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
index 68b6a34a5dc4..02b31b473738 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
@@ -6,7 +6,7 @@
 #include "pal_utilities.h"
 #include <assert.h>
 
-static int HasNoPrivateKey(RSA* rsa);
+static int HasNoPrivateKey(const RSA* rsa);
 
 EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
 {
@@ -86,7 +86,7 @@ int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey,
 
     // This check may no longer be needed on OpenSSL 3.0
     {
-        RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+        const RSA* rsa = EVP_PKEY_get0_RSA(pkey);
 
         if (rsa == NULL || HasNoPrivateKey(rsa))
         {
@@ -161,7 +161,7 @@ int32_t CryptoNative_RsaSignHash(EVP_PKEY* pkey,
 
     // This check may no longer be needed on OpenSSL 3.0
     {
-        RSA* rsa = EVP_PKEY_get0_RSA(pkey);
+        const RSA* rsa = EVP_PKEY_get0_RSA(pkey);
 
         if (rsa == NULL || HasNoPrivateKey(rsa))
         {
@@ -196,7 +196,7 @@ int32_t CryptoNative_EvpPkeySetRsa(EVP_PKEY* pkey, RSA* rsa)
     return EVP_PKEY_set1_RSA(pkey, rsa);
 }
 
-static int HasNoPrivateKey(RSA* rsa)
+static int HasNoPrivateKey(const RSA* rsa)
 {
     if (rsa == NULL)
         return 1;
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.c
index 5dd31d0e62cc..0554c8d3e8c6 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.c
@@ -33,7 +33,6 @@ c_static_assert(PAL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY == X509_V_ERR_U
 c_static_assert(PAL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
 c_static_assert(PAL_X509_V_ERR_CERT_CHAIN_TOO_LONG == X509_V_ERR_CERT_CHAIN_TOO_LONG);
 c_static_assert(PAL_X509_V_ERR_CERT_REVOKED == X509_V_ERR_CERT_REVOKED);
-c_static_assert(PAL_X509_V_ERR_INVALID_CA == X509_V_ERR_INVALID_CA);
 c_static_assert(PAL_X509_V_ERR_PATH_LENGTH_EXCEEDED == X509_V_ERR_PATH_LENGTH_EXCEEDED);
 c_static_assert(PAL_X509_V_ERR_INVALID_PURPOSE == X509_V_ERR_INVALID_PURPOSE);
 c_static_assert(PAL_X509_V_ERR_CERT_UNTRUSTED == X509_V_ERR_CERT_UNTRUSTED);
@@ -48,6 +47,26 @@ c_static_assert(PAL_X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE == X509_V_ERR_KEYUS
 c_static_assert(PAL_X509_V_ERR_INVALID_EXTENSION == X509_V_ERR_INVALID_EXTENSION);
 c_static_assert(PAL_X509_V_ERR_INVALID_POLICY_EXTENSION == X509_V_ERR_INVALID_POLICY_EXTENSION);
 c_static_assert(PAL_X509_V_ERR_NO_EXPLICIT_POLICY == X509_V_ERR_NO_EXPLICIT_POLICY);
+c_static_assert(PAL_X509_V_ERR_DIFFERENT_CRL_SCOPE == X509_V_ERR_DIFFERENT_CRL_SCOPE);
+c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE == X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
+c_static_assert(PAL_X509_V_ERR_UNNESTED_RESOURCE == X509_V_ERR_UNNESTED_RESOURCE);
+c_static_assert(PAL_X509_V_ERR_PERMITTED_VIOLATION == X509_V_ERR_PERMITTED_VIOLATION);
+c_static_assert(PAL_X509_V_ERR_EXCLUDED_VIOLATION == X509_V_ERR_EXCLUDED_VIOLATION);
+c_static_assert(PAL_X509_V_ERR_SUBTREE_MINMAX == X509_V_ERR_SUBTREE_MINMAX);
+c_static_assert(PAL_X509_V_ERR_APPLICATION_VERIFICATION == X509_V_ERR_APPLICATION_VERIFICATION);
+c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE == X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
+c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX == X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
+c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_NAME_SYNTAX == X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
+c_static_assert(PAL_X509_V_ERR_CRL_PATH_VALIDATION_ERROR == X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_VERSION == X509_V_ERR_SUITE_B_INVALID_VERSION);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_ALGORITHM == X509_V_ERR_SUITE_B_INVALID_ALGORITHM);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_CURVE == X509_V_ERR_SUITE_B_INVALID_CURVE);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM == X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED);
+c_static_assert(PAL_X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 == X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256);
+c_static_assert(PAL_X509_V_ERR_HOSTNAME_MISMATCH == X509_V_ERR_HOSTNAME_MISMATCH);
+c_static_assert(PAL_X509_V_ERR_EMAIL_MISMATCH == X509_V_ERR_EMAIL_MISMATCH);
+c_static_assert(PAL_X509_V_ERR_IP_ADDRESS_MISMATCH == X509_V_ERR_IP_ADDRESS_MISMATCH);
 
 EVP_PKEY* CryptoNative_GetX509EvpPublicKey(X509* x509)
 {
@@ -1109,7 +1128,10 @@ CryptoNative_X509ChainVerifyOcsp(X509_STORE_CTX* storeCtx, OCSP_REQUEST* req, OC
 
                     if (bio != NULL)
                     {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
                         if (i2d_OCSP_RESPONSE_bio(bio, resp))
+#pragma clang diagnostic pop
                         {
                             clearErr = 0;
                         }
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.h
index 7f242b4c2e88..f7114e9642fe 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_x509.h
@@ -18,7 +18,10 @@ typedef enum {
 /*
 The error codes used when verifying X509 certificate chains.
 
-These values should be kept in sync with Interop.Crypto.X509VerifyStatusCode.
+These values should be kept in sync with Interop.Crypto.X509VerifyStatusCodeUniversal.
+
+Codes specific to specific versions of OpenSSL can also be returned,
+but are not represented in this enum due to their non-constant nature.
 */
 typedef enum {
     PAL_X509_V_OK = 0,
@@ -43,7 +46,9 @@ typedef enum {
     PAL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21,
     PAL_X509_V_ERR_CERT_CHAIN_TOO_LONG = 22,
     PAL_X509_V_ERR_CERT_REVOKED = 23,
-    PAL_X509_V_ERR_INVALID_CA = 24,
+
+    // Code 24 varies
+
     PAL_X509_V_ERR_PATH_LENGTH_EXCEEDED = 25,
     PAL_X509_V_ERR_INVALID_PURPOSE = 26,
     PAL_X509_V_ERR_CERT_UNTRUSTED = 27,
@@ -58,6 +63,26 @@ typedef enum {
     PAL_X509_V_ERR_INVALID_EXTENSION = 41,
     PAL_X509_V_ERR_INVALID_POLICY_EXTENSION = 42,
     PAL_X509_V_ERR_NO_EXPLICIT_POLICY = 43,
+    PAL_X509_V_ERR_DIFFERENT_CRL_SCOPE = 44,
+    PAL_X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE = 45,
+    PAL_X509_V_ERR_UNNESTED_RESOURCE = 46,
+    PAL_X509_V_ERR_PERMITTED_VIOLATION = 47,
+    PAL_X509_V_ERR_EXCLUDED_VIOLATION = 48,
+    PAL_X509_V_ERR_SUBTREE_MINMAX = 49,
+    PAL_X509_V_ERR_APPLICATION_VERIFICATION = 50,
+    PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE = 51,
+    PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 52,
+    PAL_X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 53,
+    PAL_X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 54,
+    PAL_X509_V_ERR_SUITE_B_INVALID_VERSION = 56,
+    PAL_X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 57,
+    PAL_X509_V_ERR_SUITE_B_INVALID_CURVE = 58,
+    PAL_X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM = 59,
+    PAL_X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED = 60,
+    PAL_X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 61,
+    PAL_X509_V_ERR_HOSTNAME_MISMATCH = 62,
+    PAL_X509_V_ERR_EMAIL_MISMATCH = 63,
+    PAL_X509_V_ERR_IP_ADDRESS_MISMATCH = 64,
 } X509VerifyStatusCode;
 
 typedef int32_t (*X509StoreVerifyCallback)(int32_t, X509_STORE_CTX*);
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
index d28286f0169f..a7f777261e53 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
@@ -13,10 +13,14 @@
 using System.Security.Cryptography.X509Certificates.Asn1;
 using Microsoft.Win32.SafeHandles;
 
+using X509VerifyStatusCodeUniversal = Interop.Crypto.X509VerifyStatusCodeUniversal;
+
 namespace Internal.Cryptography.Pal
 {
     internal sealed class OpenSslX509ChainProcessor : IChainPal
     {
+        private delegate X509ChainStatusFlags MapVersionSpecificCode(Interop.Crypto.X509VerifyStatusCode code);
+
         // The average chain is 3 (End-Entity, Intermediate, Root)
         // 10 is plenty big.
         private const int DefaultChainCapacity = 10;
@@ -30,6 +34,8 @@ internal sealed class OpenSslX509ChainProcessor : IChainPal
         private static readonly CachedDirectoryStoreProvider s_userPersonalStore =
             new CachedDirectoryStoreProvider(X509Store.MyStoreName);
 
+        private static readonly MapVersionSpecificCode s_mapVersionSpecificCode = GetVersionLookup();
+
         private SafeX509Handle _leafHandle;
         private SafeX509StoreHandle _store;
         private readonly SafeX509StackHandle _untrustedLookup;
@@ -156,10 +162,10 @@ internal Interop.Crypto.X509VerifyStatusCode FindFirstChain(X509Certificate2Coll
 
         internal static bool IsCompleteChain(Interop.Crypto.X509VerifyStatusCode statusCode)
         {
-            switch (statusCode)
+            switch (statusCode.UniversalCode)
             {
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
                     return false;
                 default:
                     return true;
@@ -173,7 +179,7 @@ internal Interop.Crypto.X509VerifyStatusCode FindChainViaAia(
             SafeX509StoreCtxHandle storeCtx = _storeCtx;
 
             Interop.Crypto.X509VerifyStatusCode statusCode =
-                Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
+                X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;
 
             while (!IsCompleteChain(statusCode))
             {
@@ -426,7 +432,7 @@ private Interop.Crypto.X509VerifyStatusCode CheckOcsp()
             Interop.Crypto.X509VerifyStatusCode status =
                 Interop.Crypto.X509ChainGetCachedOcspStatus(_storeCtx, ocspCache);
 
-            if (status != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL)
+            if (status != X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL)
             {
                 return status;
             }
@@ -468,7 +474,7 @@ private Interop.Crypto.X509VerifyStatusCode CheckOcsp()
                 {
                     if (resp == null || resp.IsInvalid)
                     {
-                        return Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL;
+                        return X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL;
                     }
 
                     try
@@ -744,77 +750,111 @@ private static void AddUniqueStatus(IList<X509ChainStatus> list, ref X509ChainSt
 
         private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.Crypto.X509VerifyStatusCode code)
         {
-            switch (code)
+            switch (code.UniversalCode)
             {
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_OK:
+                case X509VerifyStatusCodeUniversal.X509_V_OK:
                     return X509ChainStatusFlags.NoError;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_NOT_YET_VALID:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_HAS_EXPIRED:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_NOT_YET_VALID:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_HAS_EXPIRED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
                     return X509ChainStatusFlags.NotTimeValid;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REVOKED:
                     return X509ChainStatusFlags.Revoked;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_SIGNATURE_FAILURE:
                     return X509ChainStatusFlags.NotSignatureValid;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_UNTRUSTED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
                     return X509ChainStatusFlags.UntrustedRoot;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_HAS_EXPIRED:
                     return X509ChainStatusFlags.OfflineRevocation;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_SIGNATURE_FAILURE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
                     return X509ChainStatusFlags.RevocationStatusUnknown;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_EXTENSION:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_EXTENSION:
                     return X509ChainStatusFlags.InvalidExtension;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
                     return X509ChainStatusFlags.PartialChain;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_PURPOSE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_PURPOSE:
                     return X509ChainStatusFlags.NotValidForUsage;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_CA:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_NON_CA:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_PATH_LENGTH_EXCEEDED:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_NON_CA:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_PATH_LENGTH_EXCEEDED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
                     return X509ChainStatusFlags.InvalidBasicConstraints;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_POLICY_EXTENSION:
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_NO_EXPLICIT_POLICY:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_POLICY_EXTENSION:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_NO_EXPLICIT_POLICY:
                     return X509ChainStatusFlags.InvalidPolicyConstraints;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REJECTED:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REJECTED:
                     return X509ChainStatusFlags.ExplicitDistrust;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
                     return X509ChainStatusFlags.HasNotSupportedCriticalExtension;
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_CHAIN_TOO_LONG:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_CHAIN_TOO_LONG:
                     throw new CryptographicException();
 
-                case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_OUT_OF_MEM:
+                case X509VerifyStatusCodeUniversal.X509_V_ERR_OUT_OF_MEM:
                     throw new OutOfMemoryException();
 
+                default:
+                    return s_mapVersionSpecificCode(code);
+            }
+        }
+
+        private static X509ChainStatusFlags MapOpenSsl30Code(Interop.Crypto.X509VerifyStatusCode code)
+        {
+            switch (code.Code30)
+            {
+                case Interop.Crypto.X509VerifyStatusCode30.X509_V_ERR_INVALID_CA:
+                    return X509ChainStatusFlags.InvalidBasicConstraints;
+                default:
+                    Debug.Fail("Unrecognized X509VerifyStatusCode:" + code);
+                    throw new CryptographicException();
+            }
+        }
+
+        private static X509ChainStatusFlags MapOpenSsl102Code(Interop.Crypto.X509VerifyStatusCode code)
+        {
+            switch (code.Code102)
+            {
+                case Interop.Crypto.X509VerifyStatusCode102.X509_V_ERR_INVALID_CA:
+                    return X509ChainStatusFlags.InvalidBasicConstraints;
+                default:
+                    Debug.Fail("Unrecognized X509VerifyStatusCode:" + code);
+                    throw new CryptographicException();
+            }
+        }
+
+        private static X509ChainStatusFlags MapOpenSsl111Code(Interop.Crypto.X509VerifyStatusCode code)
+        {
+            switch (code.Code111)
+            {
+                case Interop.Crypto.X509VerifyStatusCode111.X509_V_ERR_INVALID_CA:
+                    return X509ChainStatusFlags.InvalidBasicConstraints;
                 default:
                     Debug.Fail("Unrecognized X509VerifyStatusCode:" + code);
                     throw new CryptographicException();
@@ -969,7 +1009,7 @@ internal int VerifyCallback(int ok, IntPtr ctx)
                         int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx);
 
                         if (AbortOnSignatureError &&
-                            errorCode == Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE)
+                            errorCode == X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_SIGNATURE_FAILURE)
                         {
                             AbortedForSignatureError = true;
                             return 0;
@@ -979,9 +1019,9 @@ internal int VerifyCallback(int ok, IntPtr ctx)
                         // * For compatibility with Windows / .NET Framework, do not report X509_V_CRL_NOT_YET_VALID.
                         // * X509_V_ERR_DIFFERENT_CRL_SCOPE will result in X509_V_ERR_UNABLE_TO_GET_CRL
                         //   which will trigger OCSP, so is ignorable.
-                        if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK &&
-                            errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID &&
-                            errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_DIFFERENT_CRL_SCOPE)
+                        if (errorCode != X509VerifyStatusCodeUniversal.X509_V_OK &&
+                            errorCode != X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID &&
+                            errorCode != X509VerifyStatusCodeUniversal.X509_V_ERR_DIFFERENT_CRL_SCOPE)
                         {
                             if (_errors == null)
                             {
@@ -1016,6 +1056,23 @@ internal int VerifyCallback(int ok, IntPtr ctx)
             }
         }
 
+        private static MapVersionSpecificCode GetVersionLookup()
+        {
+            // 3.0+ are M_NN_00_PP_p (Major, Minor, 0, Patch, Preview)
+            // 1.x.y are 1_XX_YY_PP_p
+            if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0)
+            {
+                return MapOpenSsl30Code;
+            }
+
+            if (SafeEvpPKeyHandle.OpenSslVersion >= 0x1_01_01_00_0)
+            {
+                return MapOpenSsl111Code;
+            }
+
+            return MapOpenSsl102Code;
+        }
+
         private unsafe struct ErrorCollection
         {
             // As of OpenSSL 1.1.1 there are 75 defined X509_V_ERR values,
@@ -1059,7 +1116,7 @@ public Enumerator GetEnumerator()
 
             private static int FindBucket(Interop.Crypto.X509VerifyStatusCode statusCode, out int bitValue)
             {
-                int val = (int)statusCode;
+                int val = statusCode.Code;
 
                 int bucket;
 

From 07c2b5773e994e8922a24757605a5eff05073167 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Wed, 14 Apr 2021 16:38:19 -0700
Subject: [PATCH 05/11] Make portable builds work across OpenSSL
 1.0.2/1.1.1/3.0

Overall structure of changes

* Pull compatibility headers out into separate include files, because opensslshim.h is too big.
* Use forward definition of EVP_PKEY_CTX_set_rsa_keygen_bits and friends.
  * These are in a new apibridge file because they're for bridging up to 3.0, and the existing one was for 1.1(.1)
  * Some constants needed for this file changed between 1.1 and 3.0, so there are a lot of asserts and redefines.
* On OpenSSL 3.0, build a legacy version of ERR_put_error since it has the easier signature to work with.
* FALLBACK_FUNCTION doesn't care which version it bound to, if it doesn't find it use a local_ function.
* Renamed NEW_REQUIRED_FUNCTION to REQUIRED_FUNCTION_110 because "new" is now "sort of old".
* There's a manual sanity test that either ERR_put_error or the three new functions that together replace it are found, so we don't end up in a state where we can't report shim-injected errors.

Portable build checker:
* Built with OpenSSL 1.0.2 headers (Ubuntu 16.04 default libssl-dev)
  * Ran with 1.0.2 (Ubuntu 16.04 default libssl)
  * Ran with 1.1.1 (Ubuntu 18.04 default libssl)
  * Ran with 3.0 (Ubuntu 16.04 with local build of OpenSSL 3.0 alpha 13)
* Built with OpenSSL 1.1.1 headers (Ubuntu 18.04 default libssl-dev)
  * Ran with 1.0.2 (Ubuntu 16.04 default libssl)
  * Ran with 1.1.1 (Ubuntu 18.04 default libssl)
  * Ran with 3.0 (Ubuntu 16.04 with local build of OpenSSL 3.0 alpha 13)
* Built with OpenSSL 3.0 headers (Ubuntu 16.04 with local build of OpenSSL 3.0 alpha 13 and some surgery to the extra_libs.cmake)
  * Ran with 1.0.2 (Ubuntu 16.04 default libssl)
  * Ran with 1.1.1 (Ubuntu 18.04 default libssl)
  * Ran with 3.0 (Ubuntu 16.04 with local build of OpenSSL 3.0 alpha 13)

3.0 doesn't run error-free, but it runs with the same error rate from portable and direct builds.   All verification was limited to the System.Security.Cryptography.Algorithms.Tests run, but that's generally representative of the bindings.
---
 .../CMakeLists.txt                            |   1 +
 .../apibridge_30.c                            | 104 +++++++++
 .../apibridge_30.h                            |  13 ++
 .../apibridge_30_rev.h                        |  10 +
 .../openssl.c                                 |   2 +-
 .../opensslshim.c                             |  29 ++-
 .../opensslshim.h                             | 204 +++++++-----------
 .../osslcompat_102.h                          |  34 +++
 .../osslcompat_111.h                          |  80 +++++++
 .../osslcompat_30.h                           |  23 ++
 .../pal_ssl.c                                 |   2 +-
 11 files changed, 367 insertions(+), 135 deletions(-)
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.c
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.h
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/apibridge_30_rev.h
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/osslcompat_102.h
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h
 create mode 100644 src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h

diff --git a/src/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt b/src/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt
index b2f4e33f0b27..19dab3035d33 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt
+++ b/src/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt
@@ -23,6 +23,7 @@ include_directories(${OPENSSL_INCLUDE_DIR})
 
 set(NATIVECRYPTO_SOURCES
     apibridge.c
+    apibridge_30.c
     openssl.c
     pal_asn1.c
     pal_bignum.c
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.c b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.c
new file mode 100644
index 000000000000..63b553186327
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.c
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "opensslshim.h"
+#include "pal_crypto_types.h"
+#include "pal_types.h"
+
+#include "../Common/pal_safecrt.h"
+#include <assert.h>
+
+#if defined NEED_OPENSSL_1_0 || defined NEED_OPENSSL_1_1
+
+#include "apibridge_30.h"
+
+// 1.0 and 1.1 agree on the values of the EVP_PKEY_ values, but some of them changed in 3.0.
+// If we're running on 3.0 we already call the real methods, not these fallbacks, so we need to always use
+// the 1.0/1.1 values here.
+
+// These values are in common.
+c_static_assert(EVP_PKEY_CTRL_MD == 1);
+c_static_assert(EVP_PKEY_CTRL_RSA_KEYGEN_BITS == 0x1003);
+c_static_assert(EVP_PKEY_CTRL_RSA_OAEP_MD == 0x1009);
+c_static_assert(EVP_PKEY_CTRL_RSA_PADDING == 0x1001);
+c_static_assert(EVP_PKEY_CTRL_RSA_PSS_SALTLEN == 0x1002);
+c_static_assert(EVP_PKEY_OP_KEYGEN == (1 << 2));
+c_static_assert(EVP_PKEY_RSA == 6);
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM
+
+c_static_assert(EVP_PKEY_OP_SIGN == (1 << 3));
+c_static_assert(EVP_PKEY_OP_VERIFY == (1 << 4));
+c_static_assert(EVP_PKEY_OP_TYPE_CRYPT == ((1 << 8) | (1 << 9)));
+c_static_assert(EVP_PKEY_OP_TYPE_SIG == 0xF8);
+
+#else
+
+#undef EVP_PKEY_OP_SIGN
+#define EVP_PKEY_OP_SIGN (1 << 3)
+#undef EVP_PKEY_OP_VERIFY
+#define EVP_PKEY_OP_VERIFY (1 << 4)
+#undef EVP_PKEY_OP_TYPE_CRYPT
+#define EVP_PKEY_OP_TYPE_CRYPT ((1 << 8) | (1 << 9))
+#undef EVP_PKEY_OP_TYPE_SIG
+#define EVP_PKEY_OP_TYPE_SIG 0xF8 // OP_SIGN | OP_VERIFY | OP_VERIFYRECOVER | OP_SIGNCTX | OP_VERIFYCTX
+
+#endif
+
+int local_EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits)
+{
+    return RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL);
+}
+
+int local_EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md)
+{
+    // set_rsa_oaep_md doesn't route through RSA_pkey_ctx_ctrl n 1.1, unlike the other set_rsa operations.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT, EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void*)md);
+#pragma clang diagnostic pop
+}
+
+int local_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode)
+{
+    return RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad_mode, NULL);
+}
+
+int local_EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen)
+{
+    return RSA_pkey_ctx_ctrl(
+        ctx, (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY), EVP_PKEY_CTRL_RSA_PSS_SALTLEN, saltlen, NULL);
+}
+
+int local_EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md)
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+    return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_MD, 0, (void*)md);
+#pragma clang diagnostic pop
+}
+
+#endif // defined NEED_OPENSSL_1_0 || defined NEED_OPENSSL_1_1
+
+#ifdef NEED_OPENSSL_3_0
+
+#include "apibridge_30_rev.h"
+
+void local_ERR_put_error(int32_t lib, int32_t func, int32_t reason, const char* file, int32_t line)
+{
+    // In portable builds, ensure that we found the 3.0 error reporting functions.
+    // In non-portable builds, this is just assert(true), but then we call the functions,
+    // so the compiler ensures they're there anyways.
+    assert(API_EXISTS(ERR_new) && API_EXISTS(ERR_set_debug) && API_EXISTS(ERR_set_error));
+    ERR_new();
+
+    // ERR_set_debug saves only the pointer, not the value, as it expects constants.
+    // So just ignore the legacy numeric code, and use the 3.0 "Uh, I don't know"
+    // function name.
+    (void)func;
+    ERR_set_debug(file, line, "(unknown function)");
+
+    ERR_set_error(lib, reason, NULL);
+}
+
+#endif // defined NEED_OPENSSL_3_0
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.h
new file mode 100644
index 000000000000..0f28900cb7b5
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30.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.
+
+// Functions based on OpenSSL 3.0 API, used when building against/running with older versions.
+
+#pragma once
+#include "pal_types.h"
+
+int local_EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
+int local_EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
+int local_EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
+int local_EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen);
+int local_EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30_rev.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30_rev.h
new file mode 100644
index 000000000000..657cc969d283
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge_30_rev.h
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Functions based on OpenSSL 3.0 API, used when building against/running with older versions.
+
+#pragma once
+#include "pal_types.h"
+
+// For 3.0 to behave like previous versions.
+void local_ERR_put_error(int32_t lib, int32_t func, int32_t reason, const char* file, int32_t line);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
index 1a9ea0483975..456741360d0e 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
@@ -1256,7 +1256,7 @@ static int32_t EnsureOpenSsl10Initialized()
 }
 #endif // NEED_OPENSSL_1_0 */
 
-#ifdef NEED_OPENSSL_1_1
+#if defined NEED_OPENSSL_1_1 || defined NEED_OPENSSL_3_0
 
 // Only defined in OpenSSL 1.1.1+, has no effect on 1.1.0.
 #ifndef OPENSSL_INIT_NO_ATEXIT
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.c b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.c
index b085114a6bcd..edd7a6dd2d60 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.c
@@ -13,7 +13,7 @@
 
 // Define pointers to all the used OpenSSL functions
 #define REQUIRED_FUNCTION(fn) __typeof(fn) fn##_ptr;
-#define NEW_REQUIRED_FUNCTION(fn) __typeof(fn) fn##_ptr;
+#define REQUIRED_FUNCTION_110(fn) __typeof(fn) fn##_ptr;
 #define LIGHTUP_FUNCTION(fn) __typeof(fn) fn##_ptr;
 #define FALLBACK_FUNCTION(fn) __typeof(fn) fn##_ptr;
 #define RENAMED_FUNCTION(fn,oldfn) __typeof(fn) fn##_ptr;
@@ -23,7 +23,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #undef RENAMED_FUNCTION
 #undef FALLBACK_FUNCTION
 #undef LIGHTUP_FUNCTION
-#undef NEW_REQUIRED_FUNCTION
+#undef REQUIRED_FUNCTION_110
 #undef REQUIRED_FUNCTION
 
 // x.x.x, considering the max number of decimal digits for each component
@@ -73,7 +73,12 @@ static bool OpenLibrary()
 
     if (libssl == NULL)
     {
-        // Prefer OpenSSL 1.1.x
+        // Prefer OpenSSL 3.x
+        DlOpen(MAKELIB("3"));
+    }
+
+    if (libssl == NULL)
+    {
         DlOpen(MAKELIB("1.1"));
     }
 
@@ -117,7 +122,7 @@ static void InitializeOpenSSLShim()
 #define REQUIRED_FUNCTION(fn) \
     if (!(fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
 
-#define NEW_REQUIRED_FUNCTION(fn) \
+#define REQUIRED_FUNCTION_110(fn) \
     if (!v1_0_sentinel && !(fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
 
 #define LIGHTUP_FUNCTION(fn) \
@@ -127,8 +132,8 @@ static void InitializeOpenSSLShim()
     if (!(fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn)))) { fn##_ptr = (__typeof(fn))local_##fn; }
 
 #define RENAMED_FUNCTION(fn,oldfn) \
-    if (!v1_0_sentinel && !(fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); } \
-    if (v1_0_sentinel && !(fn##_ptr = (__typeof(fn))(dlsym(libssl, #oldfn)))) { fprintf(stderr, "Cannot get required symbol " #oldfn " from libssl\n"); abort(); }
+    fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn));\
+    if (!fn##_ptr && !(fn##_ptr = (__typeof(fn))(dlsym(libssl, #oldfn)))) { fprintf(stderr, "Cannot get required symbol " #oldfn " from libssl\n"); abort(); }
 
 #define LEGACY_FUNCTION(fn) \
     if (v1_0_sentinel && !(fn##_ptr = (__typeof(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
@@ -138,8 +143,18 @@ static void InitializeOpenSSLShim()
 #undef RENAMED_FUNCTION
 #undef FALLBACK_FUNCTION
 #undef LIGHTUP_FUNCTION
-#undef NEW_REQUIRED_FUNCTION
+#undef REQUIRED_FUNCTION_110
 #undef REQUIRED_FUNCTION
+
+    // Sanity check that we have at least one functioning way of reporting errors.
+    if (ERR_put_error_ptr == &local_ERR_put_error)
+    {
+        if (ERR_new_ptr == NULL || ERR_set_debug_ptr == NULL || ERR_set_error_ptr == NULL)
+        {
+            fprintf(stderr, "Cannot determine the error reporting routine from libssl\n");
+            abort();
+        }
+    }
 }
 
 __attribute__((destructor))
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index 4c15914d25be..1dc9a8c35c03 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -36,6 +36,7 @@
 #include <openssl/x509v3.h>
 
 #include "pal_crypto_config.h"
+#define OPENSSL_VERSION_3_0_RTM 0x30000000L
 #define OPENSSL_VERSION_1_1_1_RTM 0x10101000L
 #define OPENSSL_VERSION_1_1_0_RTM 0x10100000L
 #define OPENSSL_VERSION_1_0_2_RTM 0x10002000L
@@ -64,6 +65,22 @@
 #undef SSLv23_method
 #endif
 
+#ifdef ERR_put_error
+#undef ERR_put_error
+void ERR_put_error(int32_t lib, int32_t func, int32_t reason, const char* file, int32_t line);
+#endif
+
+// The value -1 has the correct meaning on 1.0.x, but the constant wasn't named.
+#ifndef RSA_PSS_SALTLEN_DIGEST
+#define RSA_PSS_SALTLEN_DIGEST -1
+#endif
+
+#if defined FEATURE_DISTRO_AGNOSTIC_SSL || OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
+#include "apibridge_30_rev.h"
+#endif
+#if defined FEATURE_DISTRO_AGNOSTIC_SSL || OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM
+#include "apibridge_30.h"
+#endif
 #if defined FEATURE_DISTRO_AGNOSTIC_SSL || OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0_RTM
 #include "apibridge.h"
 #endif
@@ -72,6 +89,7 @@
 
 #define NEED_OPENSSL_1_0 true
 #define NEED_OPENSSL_1_1 true
+#define NEED_OPENSSL_3_0 true
 
 #if !HAVE_OPENSSL_EC2M
 // In portable build, we need to support the following functions even if they were not present
@@ -93,110 +111,16 @@ int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str);
 const SSL_CIPHER* SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr);
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM
-typedef struct stack_st _STACK;
-int CRYPTO_add_lock(int* pointer, int amount, int type, const char* file, int line);
-int CRYPTO_num_locks(void);
-void CRYPTO_set_locking_callback(void (*func)(int mode, int type, const char* file, int line));
-void ERR_load_crypto_strings(void);
-int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX* a);
-int EVP_CIPHER_CTX_init(EVP_CIPHER_CTX* a);
-void HMAC_CTX_cleanup(HMAC_CTX* ctx);
-void HMAC_CTX_init(HMAC_CTX* ctx);
-void OPENSSL_add_all_algorithms_conf(void);
-int SSL_library_init(void);
-void SSL_load_error_strings(void);
-int SSL_state(const SSL* ssl);
-unsigned long SSLeay(void);
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
+#include "osslcompat_102.h"
+#elif OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM
+#include "osslcompat_30.h"
+#include "osslcompat_102.h"
 #else
-typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS;
-typedef struct stack_st OPENSSL_STACK;
-
-#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
-#define OPENSSL_INIT_ADD_ALL_CIPHERS 0x00000004L
-#define OPENSSL_INIT_ADD_ALL_DIGESTS 0x00000008L
-#define OPENSSL_INIT_LOAD_CONFIG 0x00000040L
-#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
-
-const BIGNUM* DSA_get0_key(const DSA* dsa, const BIGNUM** pubKey, const BIGNUM** privKey);
-void DSA_get0_pqg(const DSA* dsa, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g);
-const DSA_METHOD* DSA_get_method(const DSA* dsa);
-int32_t DSA_set0_key(DSA* dsa, BIGNUM* bnY, BIGNUM* bnX);
-int32_t DSA_set0_pqg(DSA* dsa, BIGNUM* bnP, BIGNUM* bnQ, BIGNUM* bnG);
-void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX* ctx);
-EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void);
-int32_t EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
-void EVP_MD_CTX_free(EVP_MD_CTX* ctx);
-EVP_MD_CTX* EVP_MD_CTX_new(void);
-RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
-int32_t EVP_PKEY_up_ref(EVP_PKEY* pkey);
-void HMAC_CTX_free(HMAC_CTX* ctx);
-HMAC_CTX* HMAC_CTX_new(void);
-int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS* settings);
-void OPENSSL_sk_free(OPENSSL_STACK*);
-OPENSSL_STACK* OPENSSL_sk_new_null(void);
-int OPENSSL_sk_num(const OPENSSL_STACK*);
-void* OPENSSL_sk_pop(OPENSSL_STACK* st);
-void OPENSSL_sk_pop_free(OPENSSL_STACK* st, void (*func)(void*));
-int OPENSSL_sk_push(OPENSSL_STACK* st, const void* data);
-void* OPENSSL_sk_value(const OPENSSL_STACK*, int);
-long OpenSSL_version_num(void);
-void RSA_get0_crt_params(const RSA* rsa, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp);
-void RSA_get0_factors(const RSA* rsa, const BIGNUM** p, const BIGNUM** q);
-void RSA_get0_key(const RSA* rsa, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d);
-int32_t RSA_meth_get_flags(const RSA_METHOD* meth);
-const RSA_METHOD* RSA_PKCS1_OpenSSL(void);
-int32_t RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
-int32_t RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
-int32_t RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
-int32_t RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
-int32_t SSL_is_init_finished(SSL* ssl);
-#undef SSL_CTX_set_options
-unsigned long SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options);
-void SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level);
-#undef SSL_session_reused
-int SSL_session_reused(SSL* ssl);
-const SSL_METHOD* TLS_method(void);
-const ASN1_TIME* X509_CRL_get0_nextUpdate(const X509_CRL* crl);
-int32_t X509_NAME_get0_der(X509_NAME* x509Name, const uint8_t** pder, size_t* pderlen);
-int32_t X509_PUBKEY_get0_param(
-    ASN1_OBJECT** palgOid, const uint8_t** pkeyBytes, int* pkeyBytesLen, X509_ALGOR** palg, X509_PUBKEY* pubkey);
-X509* X509_STORE_CTX_get0_cert(X509_STORE_CTX* ctx);
-STACK_OF(X509)* X509_STORE_CTX_get0_chain(X509_STORE_CTX* ctx);
-STACK_OF(X509)* X509_STORE_CTX_get0_untrusted(X509_STORE_CTX* ctx);
-X509_VERIFY_PARAM* X509_STORE_get0_param(X509_STORE* ctx);
-const ASN1_TIME* X509_get0_notAfter(const X509* x509);
-const ASN1_TIME* X509_get0_notBefore(const X509* x509);
-ASN1_BIT_STRING* X509_get0_pubkey_bitstr(const X509* x509);
-const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x509);
-X509_PUBKEY* X509_get_X509_PUBKEY(const X509* x509);
-int32_t X509_get_version(const X509* x509);
-int32_t X509_up_ref(X509* x509);
-
-// Redefine EVP_PKEY_CTX_set_rsa operations to use (local_)RSA_pkey_ctx_ctrl so the path is the same
-// for 1.0-built on 1.1 as on 1.1-built on 1.1.
-#undef EVP_PKEY_CTX_set_rsa_keygen_bits
-#define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
-    RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
-
-// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here.
-
-#undef EVP_PKEY_CTX_set_rsa_padding
-#define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \
-    RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL)
-
-#undef EVP_PKEY_CTX_set_rsa_pss_saltlen
-#define EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, len) \
-    RSA_pkey_ctx_ctrl(ctx, (EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY), EVP_PKEY_CTRL_RSA_PSS_SALTLEN, len, NULL)
-
+#include "osslcompat_30.h"
+#include "osslcompat_111.h"
 #endif
 
-#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
-X509_STORE* X509_STORE_CTX_get0_store(X509_STORE_CTX* ctx);
-int32_t X509_check_host(X509* x509, const char* name, size_t namelen, unsigned int flags, char** peername);
-#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 4
-
-#endif
 
 #if !HAVE_OPENSSL_ALPN
 #undef HAVE_OPENSSL_ALPN
@@ -213,11 +137,6 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx,
 void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsigned int* len);
 #endif
 
-// The value -1 has the correct meaning on 1.0.x, but the constant wasn't named.
-#ifndef RSA_PSS_SALTLEN_DIGEST
-#define RSA_PSS_SALTLEN_DIGEST -1
-#endif
-
 #define API_EXISTS(fn) (fn != NULL)
 
 // List of all functions from the libssl that are used in the System.Security.Cryptography.Native.
@@ -326,10 +245,13 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(ERR_error_string_n) \
     REQUIRED_FUNCTION(ERR_get_error) \
     LEGACY_FUNCTION(ERR_load_crypto_strings) \
-    REQUIRED_FUNCTION(ERR_put_error) \
+    LIGHTUP_FUNCTION(ERR_new) \
     REQUIRED_FUNCTION(ERR_peek_error) \
     REQUIRED_FUNCTION(ERR_peek_last_error) \
+    FALLBACK_FUNCTION(ERR_put_error) \
     REQUIRED_FUNCTION(ERR_reason_error_string) \
+    LIGHTUP_FUNCTION(ERR_set_debug) \
+    LIGHTUP_FUNCTION(ERR_set_error) \
     REQUIRED_FUNCTION(EVP_aes_128_cbc) \
     REQUIRED_FUNCTION(EVP_aes_128_ccm) \
     REQUIRED_FUNCTION(EVP_aes_128_ecb) \
@@ -370,6 +292,11 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
+    FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_keygen_bits) \
+    FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_oaep_md) \
+    FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_padding) \
+    FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_pss_saltlen) \
+    FALLBACK_FUNCTION(EVP_PKEY_CTX_set_signature_md) \
     REQUIRED_FUNCTION(EVP_PKEY_base_id) \
     REQUIRED_FUNCTION(EVP_PKEY_decrypt) \
     REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \
@@ -438,7 +365,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(OCSP_RESPONSE_new) \
     LEGACY_FUNCTION(OPENSSL_add_all_algorithms_conf) \
     REQUIRED_FUNCTION(OPENSSL_cleanse) \
-    NEW_REQUIRED_FUNCTION(OPENSSL_init_ssl) \
+    REQUIRED_FUNCTION_110(OPENSSL_init_ssl) \
     RENAMED_FUNCTION(OPENSSL_sk_free, sk_free) \
     RENAMED_FUNCTION(OPENSSL_sk_new_null, sk_new_null) \
     RENAMED_FUNCTION(OPENSSL_sk_num, sk_num) \
@@ -510,11 +437,11 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(SSL_get_error) \
     REQUIRED_FUNCTION(SSL_get_finished) \
     REQUIRED_FUNCTION(SSL_get_peer_cert_chain) \
-    REQUIRED_FUNCTION(SSL_get_peer_certificate) \
     REQUIRED_FUNCTION(SSL_get_peer_finished) \
     REQUIRED_FUNCTION(SSL_get_SSL_CTX) \
     REQUIRED_FUNCTION(SSL_get_version) \
     LIGHTUP_FUNCTION(SSL_get0_alpn_selected) \
+    RENAMED_FUNCTION(SSL_get1_peer_certificate, SSL_get_peer_certificate) \
     LEGACY_FUNCTION(SSL_library_init) \
     LEGACY_FUNCTION(SSL_load_error_strings) \
     REQUIRED_FUNCTION(SSL_new) \
@@ -606,7 +533,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
 
 // Declare pointers to all the used OpenSSL functions
 #define REQUIRED_FUNCTION(fn) extern __typeof(fn)* fn##_ptr;
-#define NEW_REQUIRED_FUNCTION(fn) extern __typeof(fn)* fn##_ptr;
+#define REQUIRED_FUNCTION_110(fn) extern __typeof(fn)* fn##_ptr;
 #define LIGHTUP_FUNCTION(fn) extern __typeof(fn)* fn##_ptr;
 #define FALLBACK_FUNCTION(fn) extern __typeof(fn)* fn##_ptr;
 #define RENAMED_FUNCTION(fn,oldfn) extern __typeof(fn)* fn##_ptr;
@@ -616,7 +543,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #undef RENAMED_FUNCTION
 #undef FALLBACK_FUNCTION
 #undef LIGHTUP_FUNCTION
-#undef NEW_REQUIRED_FUNCTION
+#undef REQUIRED_FUNCTION_110
 #undef REQUIRED_FUNCTION
 
 // Redefine all calls to OpenSSL functions as calls through pointers that are set
@@ -722,10 +649,13 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define ERR_error_string_n ERR_error_string_n_ptr
 #define ERR_get_error ERR_get_error_ptr
 #define ERR_load_crypto_strings ERR_load_crypto_strings_ptr
+#define ERR_new ERR_new_ptr
 #define ERR_peek_error ERR_peek_error_ptr
 #define ERR_peek_last_error ERR_peek_last_error_ptr
 #define ERR_put_error ERR_put_error_ptr
 #define ERR_reason_error_string ERR_reason_error_string_ptr
+#define ERR_set_debug ERR_set_debug_ptr
+#define ERR_set_error ERR_set_error_ptr
 #define EVP_aes_128_cbc EVP_aes_128_cbc_ptr
 #define EVP_aes_128_ecb EVP_aes_128_ecb_ptr
 #define EVP_aes_128_gcm EVP_aes_128_gcm_ptr
@@ -766,6 +696,11 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr
 #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
 #define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
+#define EVP_PKEY_CTX_set_rsa_keygen_bits EVP_PKEY_CTX_set_rsa_keygen_bits_ptr
+#define EVP_PKEY_CTX_set_rsa_oaep_md EVP_PKEY_CTX_set_rsa_oaep_md_ptr
+#define EVP_PKEY_CTX_set_rsa_padding EVP_PKEY_CTX_set_rsa_padding_ptr
+#define EVP_PKEY_CTX_set_rsa_pss_saltlen EVP_PKEY_CTX_set_rsa_pss_saltlen_ptr
+#define EVP_PKEY_CTX_set_signature_md EVP_PKEY_CTX_set_signature_md_ptr
 #define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
 #define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr
 #define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr
@@ -875,13 +810,6 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_size RSA_size_ptr
 #define RSA_up_ref RSA_up_ref_ptr
 #define RSA_verify RSA_verify_ptr
-#define sk_free OPENSSL_sk_free_ptr
-#define sk_new_null OPENSSL_sk_new_null_ptr
-#define sk_num OPENSSL_sk_num_ptr
-#define sk_pop OPENSSL_sk_pop_ptr
-#define sk_pop_free OPENSSL_sk_pop_free_ptr
-#define sk_push OPENSSL_sk_push_ptr
-#define sk_value OPENSSL_sk_value_ptr
 #define SSL_CIPHER_get_bits SSL_CIPHER_get_bits_ptr
 #define SSL_CIPHER_find SSL_CIPHER_find_ptr
 #define SSL_CIPHER_get_id SSL_CIPHER_get_id_ptr
@@ -912,11 +840,11 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define SSL_get_error SSL_get_error_ptr
 #define SSL_get_finished SSL_get_finished_ptr
 #define SSL_get_peer_cert_chain SSL_get_peer_cert_chain_ptr
-#define SSL_get_peer_certificate SSL_get_peer_certificate_ptr
 #define SSL_get_peer_finished SSL_get_peer_finished_ptr
 #define SSL_get_SSL_CTX SSL_get_SSL_CTX_ptr
 #define SSL_get_version SSL_get_version_ptr
 #define SSL_get0_alpn_selected SSL_get0_alpn_selected_ptr
+#define SSL_get1_peer_certificate SSL_get1_peer_certificate_ptr
 #define SSL_is_init_finished SSL_is_init_finished_ptr
 #define SSL_library_init SSL_library_init_ptr
 #define SSL_load_error_strings SSL_load_error_strings_ptr
@@ -1011,7 +939,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 // STACK_OF types will have been declared with inline functions to handle the pointer casting.
 // Since these inline functions are strongly bound to the OPENSSL_sk_* functions in 1.1 we need to
 // rebind things here.
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM && OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM
 // type-safe OPENSSL_sk_free
 #define sk_GENERAL_NAME_free(stack) OPENSSL_sk_free((OPENSSL_STACK*)(1 ? stack : (STACK_OF(GENERAL_NAME)*)0))
 #define sk_X509_free(stack) OPENSSL_sk_free((OPENSSL_STACK*)(1 ? stack : (STACK_OF(X509)*)0))
@@ -1039,6 +967,17 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define sk_GENERAL_NAME_value(stack, idx) (GENERAL_NAME*)OPENSSL_sk_value((const OPENSSL_STACK*)(1 ? stack : (const STACK_OF(GENERAL_NAME)*)0), idx)
 #define sk_X509_NAME_value(stack, idx) (X509_NAME*)OPENSSL_sk_value((const OPENSSL_STACK*)(1 ? stack : (const STACK_OF(X509_NAME)*)0), idx)
 #define sk_X509_value(stack, idx) (X509*)OPENSSL_sk_value((const OPENSSL_STACK*)(1 ? stack : (const STACK_OF(X509)*)0), idx)
+
+#elif OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0_RTM
+
+#define sk_free OPENSSL_sk_free_ptr
+#define sk_new_null OPENSSL_sk_new_null_ptr
+#define sk_num OPENSSL_sk_num_ptr
+#define sk_pop OPENSSL_sk_pop_ptr
+#define sk_pop_free OPENSSL_sk_pop_free_ptr
+#define sk_push OPENSSL_sk_push_ptr
+#define sk_value OPENSSL_sk_value_ptr
+
 #endif
 
 
@@ -1046,9 +985,26 @@ FOR_ALL_OPENSSL_FUNCTIONS
 
 #define API_EXISTS(fn) true
 
-#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0_RTM
-
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
+#define NEED_OPENSSL_3_0 true
+#elif OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM
+#define NEED_OPENSSL_1_1 true
+#else
 #define NEED_OPENSSL_1_0 true
+#endif
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM
+
+// Undo renames for renamed-in-3.0
+#define SSL_get1_peer_certificate SSL_get_peer_certificate
+
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
+
+#define ERR_put_error local_ERR_put_error
+
+#elif OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0_RTM
 
 // Alias "future" API to the local_ version.
 #define DSA_get0_key local_DSA_get0_key
@@ -1110,10 +1066,6 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define OPENSSL_sk_value sk_value
 #define TLS_method SSLv23_method
 
-#else // if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0_RTM
-
-#define NEED_OPENSSL_1_1 true
-
 #endif
 
 #endif // FEATURE_DISTRO_AGNOSTIC_SSL
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_102.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_102.h
new file mode 100644
index 000000000000..2ee440c320d3
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_102.h
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+#pragma once
+
+// Function prototypes unique to OpenSSL 1.0.2
+
+typedef struct stack_st _STACK;
+
+#undef CRYPTO_num_locks
+#undef CRYPTO_set_locking_callback
+#undef ERR_load_crypto_strings
+#undef EVP_CIPHER_CTX_cleanup
+#undef EVP_CIPHER_CTX_init
+#undef OPENSSL_add_all_algorithms_conf
+#undef SSL_library_init
+#undef SSL_load_error_strings
+#undef SSL_state
+#undef SSLeay
+
+int CRYPTO_add_lock(int* pointer, int amount, int type, const char* file, int line);
+int CRYPTO_num_locks(void);
+void CRYPTO_set_locking_callback(void (*func)(int mode, int type, const char* file, int line));
+void ERR_load_crypto_strings(void);
+int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX* a);
+int EVP_CIPHER_CTX_init(EVP_CIPHER_CTX* a);
+void HMAC_CTX_cleanup(HMAC_CTX* ctx);
+void HMAC_CTX_init(HMAC_CTX* ctx);
+void OPENSSL_add_all_algorithms_conf(void);
+int SSL_library_init(void);
+void SSL_load_error_strings(void);
+int SSL_state(const SSL* ssl);
+unsigned long SSLeay(void);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h
new file mode 100644
index 000000000000..0a730cef8907
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_111.h
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Function prototypes unique to OpenSSL 1.1.x
+
+#pragma once
+#include "pal_types.h"
+
+#undef SSL_CTX_set_options
+#undef SSL_session_reused
+
+typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS;
+typedef struct stack_st OPENSSL_STACK;
+
+#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
+#define OPENSSL_INIT_ADD_ALL_CIPHERS 0x00000004L
+#define OPENSSL_INIT_ADD_ALL_DIGESTS 0x00000008L
+#define OPENSSL_INIT_LOAD_CONFIG 0x00000040L
+#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
+
+const BIGNUM* DSA_get0_key(const DSA* dsa, const BIGNUM** pubKey, const BIGNUM** privKey);
+void DSA_get0_pqg(const DSA* dsa, const BIGNUM** p, const BIGNUM** q, const BIGNUM** g);
+const DSA_METHOD* DSA_get_method(const DSA* dsa);
+int32_t DSA_set0_key(DSA* dsa, BIGNUM* bnY, BIGNUM* bnX);
+int32_t DSA_set0_pqg(DSA* dsa, BIGNUM* bnP, BIGNUM* bnQ, BIGNUM* bnG);
+void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX* ctx);
+EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void);
+int32_t EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx);
+void EVP_MD_CTX_free(EVP_MD_CTX* ctx);
+EVP_MD_CTX* EVP_MD_CTX_new(void);
+RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey);
+int32_t EVP_PKEY_up_ref(EVP_PKEY* pkey);
+void HMAC_CTX_free(HMAC_CTX* ctx);
+HMAC_CTX* HMAC_CTX_new(void);
+int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS* settings);
+void OPENSSL_sk_free(OPENSSL_STACK*);
+OPENSSL_STACK* OPENSSL_sk_new_null(void);
+int OPENSSL_sk_num(const OPENSSL_STACK*);
+void* OPENSSL_sk_pop(OPENSSL_STACK* st);
+void OPENSSL_sk_pop_free(OPENSSL_STACK* st, void (*func)(void*));
+int OPENSSL_sk_push(OPENSSL_STACK* st, const void* data);
+void* OPENSSL_sk_value(const OPENSSL_STACK*, int);
+long OpenSSL_version_num(void);
+const RSA_METHOD* RSA_PKCS1_OpenSSL(void);
+void RSA_get0_crt_params(const RSA* rsa, const BIGNUM** dmp1, const BIGNUM** dmq1, const BIGNUM** iqmp);
+void RSA_get0_factors(const RSA* rsa, const BIGNUM** p, const BIGNUM** q);
+void RSA_get0_key(const RSA* rsa, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d);
+int32_t RSA_meth_get_flags(const RSA_METHOD* meth);
+int32_t RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
+int32_t RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
+int32_t RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
+int32_t RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
+int SSL_CTX_config(SSL_CTX* ctx, const char* name);
+unsigned long SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options);
+void SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level);
+int32_t SSL_is_init_finished(SSL* ssl);
+int SSL_session_reused(SSL* ssl);
+const SSL_METHOD* TLS_method(void);
+const ASN1_TIME* X509_CRL_get0_nextUpdate(const X509_CRL* crl);
+int32_t X509_NAME_get0_der(X509_NAME* x509Name, const uint8_t** pder, size_t* pderlen);
+int32_t X509_PUBKEY_get0_param(
+    ASN1_OBJECT** palgOid, const uint8_t** pkeyBytes, int* pkeyBytesLen, X509_ALGOR** palg, X509_PUBKEY* pubkey);
+X509* X509_STORE_CTX_get0_cert(X509_STORE_CTX* ctx);
+STACK_OF(X509) * X509_STORE_CTX_get0_chain(X509_STORE_CTX* ctx);
+STACK_OF(X509) * X509_STORE_CTX_get0_untrusted(X509_STORE_CTX* ctx);
+X509_VERIFY_PARAM* X509_STORE_get0_param(X509_STORE* ctx);
+const ASN1_TIME* X509_get0_notAfter(const X509* x509);
+const ASN1_TIME* X509_get0_notBefore(const X509* x509);
+ASN1_BIT_STRING* X509_get0_pubkey_bitstr(const X509* x509);
+const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x509);
+X509_PUBKEY* X509_get_X509_PUBKEY(const X509* x509);
+int32_t X509_get_version(const X509* x509);
+int32_t X509_up_ref(X509* x509);
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
+int32_t X509_check_host(X509* x509, const char* name, size_t namelen, unsigned int flags, char** peername);
+X509_STORE* X509_STORE_CTX_get0_store(X509_STORE_CTX* ctx);
+#define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 4
+
+#endif
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
new file mode 100644
index 000000000000..0fe57c91325a
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Function prototypes unique to OpenSSL 3.0
+
+#pragma once
+#include "pal_types.h"
+
+#undef EVP_PKEY_CTX_set_rsa_keygen_bits
+#undef EVP_PKEY_CTX_set_rsa_oaep_md
+#undef EVP_PKEY_CTX_set_rsa_padding
+#undef EVP_PKEY_CTX_set_rsa_pss_saltlen
+#undef EVP_PKEY_CTX_set_signature_md
+
+void ERR_new(void);
+void ERR_set_debug(const char *file, int line, const char *func);
+void ERR_set_error(int lib, int reason, const char *fmt, ...);
+int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
+int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
+int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
+int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen);
+int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
+X509* SSL_get1_peer_certificate(const SSL* ssl);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c
index 7764464bc8ce..c2e3fb202843 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c
@@ -285,7 +285,7 @@ int32_t CryptoNative_IsSslStateOK(SSL* ssl)
 
 X509* CryptoNative_SslGetPeerCertificate(SSL* ssl)
 {
-    return SSL_get_peer_certificate(ssl);
+    return SSL_get1_peer_certificate(ssl);
 }
 
 X509Stack* CryptoNative_SslGetPeerCertChain(SSL* ssl)

From 5848349f1e0df84949a01b41d41904036cc070f7 Mon Sep 17 00:00:00 2001
From: Omair Majid <omajid@redhat.com>
Date: Fri, 4 Jun 2021 17:21:28 -0400
Subject: [PATCH 06/11] Fix merge issues and make the build work

---
 .../Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs      | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
index a7f777261e53..d5ec28b1ae85 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
@@ -370,8 +370,8 @@ internal void Finish(OidCollection applicationPolicy, OidCollection certificateP
                 // chain is just fine (unless it returned a negative code for an exception)
                 Debug.Assert(verify, "verify should have returned true");
 
-                const Interop.Crypto.X509VerifyStatusCode NoCrl =
-                    Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL;
+                Interop.Crypto.X509VerifyStatusCode NoCrl =
+                   X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL;
 
                 ErrorCollection? errors =
                     workingChain.LastError > 0 ? (ErrorCollection?)workingChain[0] : null;
@@ -726,7 +726,7 @@ private static void AddElementStatus(
             X509ChainStatus chainStatus = new X509ChainStatus
             {
                 Status = statusFlag,
-                StatusInformation = Interop.Crypto.GetX509VerifyCertErrorString(errorCode),
+                StatusInformation = Interop.Crypto.GetX509VerifyCertErrorString(errorCode.Code),
             };
 
             elementStatus.Add(chainStatus);

From 7f171bb20e0816cd2d5af57437553f1a31a886af Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Thu, 15 Apr 2021 08:06:27 -0700
Subject: [PATCH 07/11] OpenSSL3: Register legacy algorithms when needed

---
 .../Interop.LegacyAlgorithms.cs               | 31 +++++++++++++++++++
 .../openssl.c                                 | 10 ++++++
 .../openssl.h                                 |  2 ++
 .../opensslshim.h                             |  6 ++++
 .../osslcompat_30.h                           |  4 +++
 .../Cryptography/DesImplementation.Unix.cs    |  2 ++
 .../Cryptography/RC2Implementation.Unix.cs    |  2 ++
 ...em.Security.Cryptography.Algorithms.csproj |  3 ++
 8 files changed, 60 insertions(+)
 create mode 100644 src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.LegacyAlgorithms.cs

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.LegacyAlgorithms.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.LegacyAlgorithms.cs
new file mode 100644
index 000000000000..800b14b78825
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.LegacyAlgorithms.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+    internal static partial class Crypto
+    {
+        private static volatile bool s_loadedLegacy;
+        private static readonly object s_legacyLoadLock = new object();
+
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RegisterLegacyAlgorithms")]
+        private static extern void CryptoNative_RegisterLegacyAlgorithms();
+
+        internal static void EnsureLegacyAlgorithmsRegistered()
+        {
+            if (!s_loadedLegacy)
+            {
+                lock (s_legacyLoadLock)
+                {
+                    if (!s_loadedLegacy)
+                    {
+                        CryptoNative_RegisterLegacyAlgorithms();
+                        s_loadedLegacy = true;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
index 456741360d0e..6792bdb1a158 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
@@ -1117,6 +1117,16 @@ int64_t CryptoNative_OpenSslVersionNumber()
     return (int64_t)OpenSSL_version_num();
 }
 
+void CryptoNative_RegisterLegacyAlgorithms()
+{
+#if NEED_OPENSSL_3_0
+    if (API_EXISTS(OSSL_PROVIDER_try_load))
+    {
+        OSSL_PROVIDER_try_load(NULL, "legacy", 1);
+    }
+#endif
+}
+
 #ifdef NEED_OPENSSL_1_0
 // Lock used to make sure EnsureopenSslInitialized itself is thread safe
 static pthread_mutex_t g_initLock = PTHREAD_MUTEX_INITIALIZER;
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/openssl.h b/src/Native/Unix/System.Security.Cryptography.Native/openssl.h
index 1b4604024e32..7bf0da242629 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/openssl.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/openssl.h
@@ -73,3 +73,5 @@ DLLEXPORT int32_t CryptoNative_LookupFriendlyNameByOid(const char* oidValue, con
 DLLEXPORT int32_t CryptoNative_EnsureOpenSslInitialized(void);
 
 DLLEXPORT int64_t CryptoNative_OpenSslVersionNumber(void);
+
+DLLEXPORT void CryptoNative_RegisterLegacyAlgorithms(void);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index 1dc9a8c35c03..957860cae411 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -41,6 +41,10 @@
 #define OPENSSL_VERSION_1_1_0_RTM 0x10100000L
 #define OPENSSL_VERSION_1_0_2_RTM 0x10002000L
 
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
+#include <openssl/provider.h>
+#endif
+
 #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_1_RTM
 #define HAVE_OPENSSL_SET_CIPHERSUITES 1
 #else
@@ -374,6 +378,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     RENAMED_FUNCTION(OPENSSL_sk_push, sk_push) \
     RENAMED_FUNCTION(OPENSSL_sk_value, sk_value) \
     FALLBACK_FUNCTION(OpenSSL_version_num) \
+    LIGHTUP_FUNCTION(OSSL_PROVIDER_try_load) \
     REQUIRED_FUNCTION(PEM_read_bio_PKCS7) \
     REQUIRED_FUNCTION(PEM_read_bio_X509) \
     REQUIRED_FUNCTION(PEM_read_bio_X509_AUX) \
@@ -778,6 +783,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define OPENSSL_sk_push OPENSSL_sk_push_ptr
 #define OPENSSL_sk_value OPENSSL_sk_value_ptr
 #define OpenSSL_version_num OpenSSL_version_num_ptr
+#define OSSL_PROVIDER_try_load OSSL_PROVIDER_try_load_ptr
 #define PEM_read_bio_PKCS7 PEM_read_bio_PKCS7_ptr
 #define PEM_read_bio_X509 PEM_read_bio_X509_ptr
 #define PEM_read_bio_X509_AUX PEM_read_bio_X509_AUX_ptr
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
index 0fe57c91325a..b87b4e72500f 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
@@ -12,6 +12,9 @@
 #undef EVP_PKEY_CTX_set_rsa_pss_saltlen
 #undef EVP_PKEY_CTX_set_signature_md
 
+typedef struct ossl_provider_st OSSL_PROVIDER;
+typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
+
 void ERR_new(void);
 void ERR_set_debug(const char *file, int line, const char *func);
 void ERR_set_error(int lib, int reason, const char *fmt, ...);
@@ -20,4 +23,5 @@ int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
 int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
 int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen);
 int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
+OSSL_PROVIDER* OSSL_PROVIDER_try_load(OSSL_LIB_CTX* , const char* name, int retain_fallbacks);
 X509* SSL_get1_peer_certificate(const SSL* ssl);
diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs
index 721efeec6cfe..0416a86577c8 100644
--- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs
+++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs
@@ -31,6 +31,8 @@ private static ICryptoTransform CreateTransformCore(
                     throw new NotSupportedException();
             }
 
+            Interop.Crypto.EnsureLegacyAlgorithmsRegistered();
+
             BasicSymmetricCipher cipher = new OpenSslCipher(algorithm, cipherMode, blockSize, key, 0, iv, encrypting);
             return UniversalCryptoTransform.Create(paddingMode, cipher, encrypting);
         }
diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs
index 0c06cdbcf7bb..93e5e9a7138e 100644
--- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs
+++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs
@@ -33,6 +33,8 @@ private static ICryptoTransform CreateTransformCore(
                     throw new NotSupportedException();
             }
 
+            Interop.Crypto.EnsureLegacyAlgorithmsRegistered();
+
             BasicSymmetricCipher cipher = new OpenSslCipher(algorithm, cipherMode, blockSize, key, effectiveKeyLength, iv, encrypting);
             return UniversalCryptoTransform.Create(paddingMode, cipher, encrypting);
         }
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
index c6e8b5b69a17..cf5c6731c261 100644
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
@@ -519,6 +519,9 @@
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.LegacyAlgorithms.cs">
+      <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.LegacyAlgorithms.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.RAND.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.RAND.cs</Link>
     </Compile>

From 30e2e4cbb11a4fbdb7102133b19bfc990a2ba939 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Fri, 16 Apr 2021 09:38:47 -0700
Subject: [PATCH 08/11] Work around OpenSSL 3.0 ciphers not restoring original
 IV on reset.

---
 .../opensslshim.h                             |  2 ++
 .../osslcompat_30.h                           |  1 +
 .../pal_evp_cipher.c                          | 20 ++++++++++++++++++-
 3 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index 957860cae411..c5052c1ba5a8 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -271,6 +271,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     LEGACY_FUNCTION(EVP_CIPHER_CTX_cleanup) \
     REQUIRED_FUNCTION(EVP_CIPHER_CTX_ctrl) \
     FALLBACK_FUNCTION(EVP_CIPHER_CTX_free) \
+    LIGHTUP_FUNCTION(EVP_CIPHER_CTX_get_original_iv) \
     LEGACY_FUNCTION(EVP_CIPHER_CTX_init) \
     FALLBACK_FUNCTION(EVP_CIPHER_CTX_new) \
     FALLBACK_FUNCTION(EVP_CIPHER_CTX_reset) \
@@ -676,6 +677,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_CIPHER_CTX_cleanup EVP_CIPHER_CTX_cleanup_ptr
 #define EVP_CIPHER_CTX_ctrl EVP_CIPHER_CTX_ctrl_ptr
 #define EVP_CIPHER_CTX_free EVP_CIPHER_CTX_free_ptr
+#define EVP_CIPHER_CTX_get_original_iv EVP_CIPHER_CTX_get_original_iv_ptr
 #define EVP_CIPHER_CTX_init EVP_CIPHER_CTX_init_ptr
 #define EVP_CIPHER_CTX_new EVP_CIPHER_CTX_new_ptr
 #define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_reset_ptr
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
index b87b4e72500f..bb529df51ee0 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
@@ -18,6 +18,7 @@ typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
 void ERR_new(void);
 void ERR_set_debug(const char *file, int line, const char *func);
 void ERR_set_error(int lib, int reason, const char *fmt, ...);
+int EVP_CIPHER_CTX_get_original_iv(EVP_CIPHER_CTX *ctx, void *buf, size_t len);
 int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
 int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
 int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_cipher.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_cipher.c
index af2483fa0cf5..4d21294fa19f 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_cipher.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_cipher.c
@@ -127,8 +127,26 @@ int32_t CryptoNative_EvpCipherReset(EVP_CIPHER_CTX* ctx)
     //
     // But since we have a different object returned for CreateEncryptor
     // and CreateDecryptor we don't need to worry about that.
+    uint8_t* iv = NULL;
 
-    return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, KEEP_CURRENT_DIRECTION);
+#ifdef NEED_OPENSSL_3_0
+    // OpenSSL 3.0 alpha 13 does not properly reset the IV. Work around that by
+    // asking for the original IV, and giving it back.
+    uint8_t tmpIV[EVP_MAX_IV_LENGTH];
+
+    // If we're direct against 3.0, or we're portable and found 3.0
+    if (API_EXISTS(EVP_CIPHER_CTX_get_original_iv))
+    {
+        if (EVP_CIPHER_CTX_get_original_iv(ctx, tmpIV, sizeof(tmpIV)) != 1)
+        {
+            return 0;
+        }
+
+        iv = tmpIV;
+    }
+#endif
+
+    return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, KEEP_CURRENT_DIRECTION);
 }
 
 int32_t CryptoNative_EvpCipherCtxSetPadding(EVP_CIPHER_CTX* x, int32_t padding)

From b7700862a9a85e5bab302c158d5aa6ac1af7c5c1 Mon Sep 17 00:00:00 2001
From: Omair Majid <omajid@redhat.com>
Date: Mon, 7 Jun 2021 11:37:48 -0400
Subject: [PATCH 09/11] Use `1` instead of `true` for more portable code

---
 .../opensslshim.h                                  | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index c5052c1ba5a8..b0d1a71671c1 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -91,9 +91,9 @@ void ERR_put_error(int32_t lib, int32_t func, int32_t reason, const char* file,
 
 #ifdef FEATURE_DISTRO_AGNOSTIC_SSL
 
-#define NEED_OPENSSL_1_0 true
-#define NEED_OPENSSL_1_1 true
-#define NEED_OPENSSL_3_0 true
+#define NEED_OPENSSL_1_0 1
+#define NEED_OPENSSL_1_1 1
+#define NEED_OPENSSL_3_0 1
 
 #if !HAVE_OPENSSL_EC2M
 // In portable build, we need to support the following functions even if they were not present
@@ -991,14 +991,14 @@ FOR_ALL_OPENSSL_FUNCTIONS
 
 #else // FEATURE_DISTRO_AGNOSTIC_SSL
 
-#define API_EXISTS(fn) true
+#define API_EXISTS(fn) 1
 
 #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_3_0_RTM
-#define NEED_OPENSSL_3_0 true
+#define NEED_OPENSSL_3_0 1
 #elif OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_0_RTM
-#define NEED_OPENSSL_1_1 true
+#define NEED_OPENSSL_1_1 1
 #else
-#define NEED_OPENSSL_1_0 true
+#define NEED_OPENSSL_1_0 1
 #endif
 
 #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM

From c746b2a3bd8ae3b76740e2b4f2cf12646eedbb51 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Sat, 21 Aug 2021 05:05:19 -0700
Subject: [PATCH 10/11] Stop using ERR_GET_FUNC, since it has been removed in
 OSSL3 Beta2. (#57869)

---
 .../openssl.c                                 | 25 +++++++++++--------
 .../opensslshim.h                             |  2 ++
 2 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
index 6792bdb1a158..e55486dc8036 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/openssl.c
@@ -1064,27 +1064,30 @@ int32_t CryptoNative_LookupFriendlyNameByOid(const char* oidValue, const char**
         return -2;
     }
 
+    // First, check if oidValue parses as a dotted decimal OID. If not, we'll
+    // return not-found and let the system cache that.
+    int asnRet = a2d_ASN1_OBJECT(NULL, 0, oidValue, -1);
+
+    if (asnRet <= 0)
+    {
+        return 0;
+    }
+
     // Do a lookup with no_name set. The purpose of this function is to map only the
     // dotted decimal to the friendly name. "sha1" in should not result in "sha1" out.
     oid = OBJ_txt2obj(oidValue, 1);
 
-    if (!oid)
+    if (oid == NULL)
     {
-        unsigned long err = ERR_peek_last_error();
-
-        // If the most recent error pushed onto the error queue is NOT from OID parsing
-        // then signal for an exception to be thrown.
-        if (err != 0 && ERR_GET_FUNC(err) != ASN1_F_A2D_ASN1_OBJECT)
-        {
-            return -1;
-        }
-
-        return 0;
+        // We know that the OID parsed (unless it underwent concurrent modification,
+        // which is unsupported), so any error in this stage should be an exception.
+        return -1;
     }
 
     // Look in the predefined, and late-registered, OIDs list to get the lookup table
     // identifier for this OID.  The OBJ_txt2obj object will not have ln set.
     nid = OBJ_obj2nid(oid);
+    ASN1_OBJECT_free(oid);
 
     if (nid == NID_undef)
     {
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index b0d1a71671c1..c11285e7dd6c 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -148,6 +148,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
 // that needs to be added.
 
 #define FOR_ALL_OPENSSL_FUNCTIONS \
+    REQUIRED_FUNCTION(a2d_ASN1_OBJECT) \
     REQUIRED_FUNCTION(ASN1_BIT_STRING_free) \
     REQUIRED_FUNCTION(ASN1_d2i_bio) \
     REQUIRED_FUNCTION(ASN1_i2d_bio) \
@@ -554,6 +555,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 
 // Redefine all calls to OpenSSL functions as calls through pointers that are set
 // to the functions from the libssl.so selected by the shim.
+#define a2d_ASN1_OBJECT a2d_ASN1_OBJECT_ptr
 #define ASN1_BIT_STRING_free ASN1_BIT_STRING_free_ptr
 #define ASN1_GENERALIZEDTIME_free ASN1_GENERALIZEDTIME_free_ptr
 #define ASN1_d2i_bio ASN1_d2i_bio_ptr

From 05fb8ceb229d76ae32bd18e707b3682c8302490c Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Tue, 13 Jul 2021 01:38:33 -0700
Subject: [PATCH 11/11] Adjust crypto shim for functions renamed for OSSL3
 beta1

---
 .../opensslshim.h                                 | 15 +++++++++------
 .../osslcompat_30.h                               |  3 +++
 .../System.Security.Cryptography.Native/pal_evp.c |  2 +-
 .../pal_evp_pkey.c                                |  2 +-
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index c11285e7dd6c..b3386d381fad 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -292,7 +292,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_md5) \
     RENAMED_FUNCTION(EVP_MD_CTX_free, EVP_MD_CTX_destroy) \
     RENAMED_FUNCTION(EVP_MD_CTX_new, EVP_MD_CTX_create) \
-    REQUIRED_FUNCTION(EVP_MD_size) \
+    RENAMED_FUNCTION(EVP_MD_get_size, EVP_MD_size) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \
@@ -303,13 +303,14 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_padding) \
     FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_pss_saltlen) \
     FALLBACK_FUNCTION(EVP_PKEY_CTX_set_signature_md) \
-    REQUIRED_FUNCTION(EVP_PKEY_base_id) \
     REQUIRED_FUNCTION(EVP_PKEY_decrypt) \
     REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive) \
     REQUIRED_FUNCTION(EVP_PKEY_free) \
+    RENAMED_FUNCTION(EVP_PKEY_get_base_id, EVP_PKEY_base_id) \
+    RENAMED_FUNCTION(EVP_PKEY_get_size, EVP_PKEY_size) \
     FALLBACK_FUNCTION(EVP_PKEY_get0_RSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
@@ -322,7 +323,6 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \
     REQUIRED_FUNCTION(EVP_PKEY_sign) \
     REQUIRED_FUNCTION(EVP_PKEY_sign_init) \
-    REQUIRED_FUNCTION(EVP_PKEY_size) \
     FALLBACK_FUNCTION(EVP_PKEY_up_ref) \
     REQUIRED_FUNCTION(EVP_rc2_cbc) \
     REQUIRED_FUNCTION(EVP_rc2_ecb) \
@@ -699,7 +699,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_md5 EVP_md5_ptr
 #define EVP_MD_CTX_free EVP_MD_CTX_free_ptr
 #define EVP_MD_CTX_new EVP_MD_CTX_new_ptr
-#define EVP_MD_size EVP_MD_size_ptr
+#define EVP_MD_get_size EVP_MD_get_size_ptr
 #define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr
 #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr
 #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr
@@ -710,13 +710,14 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_CTX_set_rsa_padding EVP_PKEY_CTX_set_rsa_padding_ptr
 #define EVP_PKEY_CTX_set_rsa_pss_saltlen EVP_PKEY_CTX_set_rsa_pss_saltlen_ptr
 #define EVP_PKEY_CTX_set_signature_md EVP_PKEY_CTX_set_signature_md_ptr
-#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
 #define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr
 #define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr
 #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr
 #define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr
 #define EVP_PKEY_derive EVP_PKEY_derive_ptr
 #define EVP_PKEY_free EVP_PKEY_free_ptr
+#define EVP_PKEY_get_base_id EVP_PKEY_get_base_id_ptr
+#define EVP_PKEY_get_size EVP_PKEY_get_size_ptr
 #define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr
 #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
 #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
@@ -729,7 +730,6 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr
 #define EVP_PKEY_sign_init EVP_PKEY_sign_init_ptr
 #define EVP_PKEY_sign EVP_PKEY_sign_ptr
-#define EVP_PKEY_size EVP_PKEY_size_ptr
 #define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr
 #define EVP_rc2_cbc EVP_rc2_cbc_ptr
 #define EVP_rc2_ecb EVP_rc2_ecb_ptr
@@ -1006,6 +1006,9 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_RTM
 
 // Undo renames for renamed-in-3.0
+#define EVP_MD_get_size EVP_MD_size
+#define EVP_PKEY_get_base_id EVP_PKEY_base_id
+#define EVP_PKEY_get_size EVP_PKEY_size
 #define SSL_get1_peer_certificate SSL_get_peer_certificate
 
 #endif
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
index bb529df51ee0..dba69f1382d2 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/osslcompat_30.h
@@ -19,10 +19,13 @@ void ERR_new(void);
 void ERR_set_debug(const char *file, int line, const char *func);
 void ERR_set_error(int lib, int reason, const char *fmt, ...);
 int EVP_CIPHER_CTX_get_original_iv(EVP_CIPHER_CTX *ctx, void *buf, size_t len);
+int EVP_MD_get_size(const EVP_MD* md);
 int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
 int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
 int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
 int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen);
 int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
+int EVP_PKEY_get_base_id(const EVP_PKEY* pkey);
+int EVP_PKEY_get_size(const EVP_PKEY* pkey);
 OSSL_PROVIDER* OSSL_PROVIDER_try_load(OSSL_LIB_CTX* , const char* name, int retain_fallbacks);
 X509* SSL_get1_peer_certificate(const SSL* ssl);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp.c
index 9665ffe3fa76..5ec3c6312237 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp.c
@@ -59,7 +59,7 @@ int32_t CryptoNative_EvpDigestFinalEx(EVP_MD_CTX* ctx, uint8_t* md, uint32_t* s)
 
 int32_t CryptoNative_EvpMdSize(const EVP_MD* md)
 {
-    return EVP_MD_size(md);
+    return EVP_MD_get_size(md);
 }
 
 const EVP_MD* CryptoNative_EvpMd5()
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
index f232b382ead4..67410bc4e843 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c
@@ -21,7 +21,7 @@ void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey)
 int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey)
 {
     assert(pkey != NULL);
-    return EVP_PKEY_size(pkey);
+    return EVP_PKEY_get_size(pkey);
 }
 
 int32_t CryptoNative_UpRefEvpPkey(EVP_PKEY* pkey)