Blob Blame History Raw
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 b2f4e33f0b..19dab3035d 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 0000000000..63b5531863
--- /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 0000000000..0f28900cb7
--- /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 0000000000..657cc969d2
--- /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 1a9ea04839..456741360d 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 @@ done:
 }
 #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 b085114a6b..edd7a6dd2d 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 4c15914d25..1dc9a8c35c 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 0000000000..2ee440c320
--- /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 0000000000..0a730cef89
--- /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 0000000000..0fe57c9132
--- /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 7764464bc8..c2e3fb2028 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)
-- 
2.31.1