Commit cc5dd4ff66 for openssl.org
commit cc5dd4ff66ff0cb000a15396620e9c00714d6f64
Author: Craig Lorentzen <crlorentzen@gmail.com>
Date: Fri Apr 24 17:25:29 2026 +0000
Map rsaesOaep SubjectPublicKeyInfo to RSA
TPM 1.2 Endorsement Key certificates use id-RSAES-OAEP
(NID_rsaesOaep) as their SubjectPublicKeyInfo algorithm
identifier per TCG Credential Profiles V1.2 section 3.2.7.
The underlying key is a standard RSAPublicKey. Without
this mapping, X509_get_pubkey() fails with a decode error
and X509_verify_cert() cannot validate these certificates.
Add NID_rsaesOaep handling to the three SPKI decode paths,
each of which points at the other two so future changes stay
in sync:
- x509_pubkey_decode(): remap the NID to NID_rsaEncryption
for the legacy ameth lookup. This path is reached via
d2i_RSA_PUBKEY()/ossl_d2i_PUBKEY_legacy(), which is in
turn invoked by the provider RSA decoder's rsa_d2i_PUBKEY,
so it is load-bearing even when the provider path is in
use.
- x509_pubkey_ex_d2i_ex(): use "RSA" as the decoder keytype
name so OSSL_DECODER_CTX_new_for_pkey() selects the RSA
provider decoder. The NID check precedes OBJ_obj2txt()
so the text conversion is skipped when unused.
- ossl_spki2typespki_der_decode(): same remap in the
SPKI-to-type-SPKI provider decoder chain. Flatten the
existing SM2 special case while here: the original code
relied on a dangling else across the #endif, which made
the rsaesOaep branch awkward to add. The new structure
initializes dataname to empty, applies each special case
in turn, and falls back to OBJ_obj2txt() only when no
override applied. strcpy() is replaced with
OPENSSL_strlcpy() for consistency with surrounding code.
The OAEP AlgorithmIdentifier parameters (which carry a
TCG-specific pSourceAlgorithm "TCPA" for TPM EKs) are
deliberately not interpreted; only the RSAPublicKey body is
consumed.
Add a test using a real TPM 1.2 EK certificate. The test
exercises both the provider decoder path (via X509_from_strings
+ X509_get0_pubkey) and, when deprecated APIs are available,
the legacy path (via d2i_RSA_PUBKEY), confirming the key
decodes to an RSA EVP_PKEY of the expected size.
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
MergeDate: Sun May 3 14:44:24 2026
(Merged from https://github.com/openssl/openssl/pull/30961)
diff --git a/CHANGES.md b/CHANGES.md
index 049c0e7288..e8c2167746 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -31,6 +31,15 @@ OpenSSL Releases
### Changes between 4.0 and 4.1 [xx XXX xxxx]
+ * SubjectPublicKeyInfo blobs whose AlgorithmIdentifier uses id-RSAES-OAEP
+ (NID_rsaesOaep, 1.2.840.113549.1.1.7) with a plain RSAPublicKey body
+ are now decoded as RSA keys. This is required for interoperability
+ with TPM 1.2 Endorsement Key certificates per TCG Credential Profiles
+ V1.2 section 3.2.7. The OAEP AlgorithmIdentifier parameters are not
+ interpreted.
+
+ *Craig Lorentzen*
+
* Added test framework for testing function memory allocation failures.
*Jakub Zelenka*
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index d085d74992..78eba3707a 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -199,8 +199,19 @@ static int x509_pubkey_ex_d2i_ex(ASN1_VALUE **pval,
}
p = in_saved;
- if (OBJ_obj2txt(txtoidname, sizeof(txtoidname),
- pubkey->algor->algorithm, 0)
+ /*
+ * TPM 1.2 Endorsement Key certificates use NID_rsaesOaep in the
+ * SPKI AlgorithmIdentifier with a plain RSAPublicKey body, per
+ * TCG Credential Profiles V1.2 section 3.2.7. Map the OID to
+ * "RSA" here so the provider decoder is selected; the OAEP
+ * AlgorithmIdentifier parameters are not interpreted. Keep
+ * this in sync with x509_pubkey_decode() and
+ * ossl_spki2typespki_der_decode().
+ */
+ if (OBJ_obj2nid(pubkey->algor->algorithm) == NID_rsaesOaep) {
+ OPENSSL_strlcpy(txtoidname, "RSA", sizeof(txtoidname));
+ } else if (OBJ_obj2txt(txtoidname, sizeof(txtoidname),
+ pubkey->algor->algorithm, 0)
<= 0) {
ERR_clear_last_mark();
goto end;
@@ -409,6 +420,16 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, const X509_PUBKEY *key)
if (!key->flag_force_legacy)
return 0;
+ /*
+ * NID_rsaesOaep uses the same underlying RSAPublicKey body as
+ * NID_rsaEncryption (TCG Credential Profiles V1.2 section 3.2.7).
+ * Remap so EVP_PKEY_set_type() below finds the RSA ameth. Keep
+ * this in sync with x509_pubkey_ex_d2i_ex() and
+ * ossl_spki2typespki_der_decode().
+ */
+ if (nid == NID_rsaesOaep)
+ nid = NID_rsaEncryption;
+
pkey = EVP_PKEY_new();
if (pkey == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_EVP_LIB);
diff --git a/providers/implementations/encode_decode/decode_spki2typespki.c b/providers/implementations/encode_decode/decode_spki2typespki.c
index ad1fd0ea3e..6fd680c23f 100644
--- a/providers/implementations/encode_decode/decode_spki2typespki.c
+++ b/providers/implementations/encode_decode/decode_spki2typespki.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -122,14 +122,29 @@ int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection,
goto end;
X509_ALGOR_get0(&oid, NULL, NULL, algor);
+ /*
+ * Resolve the SPKI AlgorithmIdentifier OID to the key type name expected
+ * by the downstream provider decoder. Most OIDs map one-to-one to a key
+ * type via OBJ_obj2txt(), but a few need special handling:
+ *
+ * - SM2 abuses id-ecPublicKey, so the EC parameters must be inspected
+ * to tell EC and SM2 apart.
+ * - TPM 1.2 Endorsement Key certificates use NID_rsaesOaep with a
+ * plain RSAPublicKey body per TCG Credential Profiles V1.2 section
+ * 3.2.7; the OAEP AlgorithmIdentifier parameters are not interpreted
+ * here. Keep this in sync with x509_pubkey_decode() and
+ * x509_pubkey_ex_d2i_ex() in crypto/x509/x_pubkey.c.
+ */
+ dataname[0] = '\0';
#ifndef OPENSSL_NO_EC
- /* SM2 abuses the EC oid, so this could actually be SM2 */
if (OBJ_obj2nid(oid) == NID_X9_62_id_ecPublicKey
&& ossl_x509_algor_is_sm2(algor))
- strcpy(dataname, "SM2");
- else
+ OPENSSL_strlcpy(dataname, "SM2", sizeof(dataname));
#endif
- if (OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0)
+ if (dataname[0] == '\0' && OBJ_obj2nid(oid) == NID_rsaesOaep)
+ OPENSSL_strlcpy(dataname, "RSA", sizeof(dataname));
+ if (dataname[0] == '\0'
+ && OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0)
goto end;
ossl_X509_PUBKEY_INTERNAL_free(xpub);
diff --git a/test/x509_test.c b/test/x509_test.c
index f5f5cc586a..5ce8a647c0 100644
--- a/test/x509_test.c
+++ b/test/x509_test.c
@@ -445,6 +445,96 @@ err:
return ret;
}
+/*
+ * TPM 1.2 Endorsement Key certificate with a NID_rsaesOaep
+ * SubjectPublicKeyInfo AlgorithmIdentifier (per TCG Credential
+ * Profiles V1.2 section 3.2.7). The AlgorithmIdentifier carries
+ * a TCG-specific pSourceAlgorithm ("TCPA") in its parameters,
+ * which we deliberately do not interpret. The key body itself
+ * is a standard RSAPublicKey.
+ */
+static const char *kRsaesOaepCert[] = {
+ "-----BEGIN CERTIFICATE-----\n",
+ "MIIDhDCCAmygAwIBAgIUBchBXcXPAWxNMJEsLXEXHv/eVZswDQYJKoZIhvcNAQEL\n",
+ "BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO\n",
+ "VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMjEw\n",
+ "OTA0MDAwMDAwWhcNMzEwOTA0MDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT\n",
+ "MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAxpd3DnecpD87acEsYp4J\n",
+ "stM2q5Ss3CkjAP2Ei8yGjbO6DG/6WBIZjTdI5RfIcInoqN4QMso94vm8VqijdRI+\n",
+ "Zo5hLTCPLKXYwa6UG5yIPZ3ENQdhgZWeEPWe+pp9VUwz8wi78Ifk+CCV6Xp/5kQi\n",
+ "DCsR+RYbOVb9QgR6kjq+cx1z8YFp5u+k3Pl9tMq9xgIp5E6hT2MaS12KnoN8+hYI\n",
+ "mfCYVnpzBeQaHDp1KUoyDK6xGt86VxB0QyRbniHI38qgQL6qhO7z96aQ0pNGoQde\n",
+ "QUxFf/sETurQ5zSf+3btnS8afjxdVBKzj3isv5BaQrt0mdB7+3XWD+ASda33SY12\n",
+ "6wIDAQABo4GLMIGIMB8GA1UdIwQYMBaAFFcfgGtHzOeb+jWUfO2IuNEAWuCeMEIG\n",
+ "A1UdIAQ7MDkwNwYEVR0gADAvMC0GCCsGAQUFBwIBFiFodHRwOi8vd3d3LnN0LmNv\n",
+ "bS9UUE0vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADATBgNVHSUBAf8ECTAHBgVn\n",
+ "gQUIATANBgkqhkiG9w0BAQsFAAOCAQEAMOhFPNcebyCRFOBztlWhmDb2DHTCD0nC\n",
+ "DVobH4WZJXGf4bkYNO3mOLyWtHEVzb36kiq7enh3f/eGhDPwKB8axlozpR5KAvER\n",
+ "szKNO8iLGOjuYzI2A4DazkttczFfzSB9QDgJrwTNEfIJtwRm2HQSiL0zzuEQOnaS\n",
+ "UWyt/iKn4/34BjEeaw4/Ld7+f06LXqSr18SUr0LTB2kk+Zzf0Och1C+G1CNLgJMM\n",
+ "MNQikAv0xdaOMX3HzA+phFlLbw/x8sboMlzmrbr92a/4Fp5WvmOSHH3ciwTtbAQn\n",
+ "A2TfExNOaKD2BG5FnB7c66puw2/yVxhveocQYgmT9XtMrNX00vEZJQ==\n",
+ "-----END CERTIFICATE-----\n",
+ NULL
+};
+
+/*
+ * Verify that a SubjectPublicKeyInfo with an id-RSAES-OAEP
+ * AlgorithmIdentifier decodes to an RSA EVP_PKEY via both the
+ * provider decoder path (exercised by X509_from_strings() +
+ * X509_get0_pubkey()) and the legacy type-specific path
+ * (exercised by d2i_RSA_PUBKEY() when available).
+ */
+static int test_rsaesoaep_spki(void)
+{
+ int ret = 0;
+ X509 *cert = NULL;
+ EVP_PKEY *pkey = NULL;
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ const X509_PUBKEY *xpk = NULL;
+ unsigned char *spki_der = NULL, *q;
+ const unsigned char *p;
+ int spki_len;
+ RSA *rsa = NULL;
+#endif
+
+ /* Provider / OSSL_DECODER path. */
+ if (!TEST_ptr(cert = X509_from_strings(kRsaesOaepCert))
+ || !TEST_ptr(pkey = X509_get0_pubkey(cert))
+ || !TEST_int_eq(EVP_PKEY_get_base_id(pkey), EVP_PKEY_RSA)
+ || !TEST_int_ge(EVP_PKEY_get_bits(pkey), 2048))
+ goto err;
+
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ /*
+ * Legacy path: d2i_RSA_PUBKEY() routes through
+ * ossl_d2i_PUBKEY_legacy() which sets flag_force_legacy=1,
+ * so this exercises the NID_rsaesOaep -> NID_rsaEncryption
+ * remap in x509_pubkey_decode().
+ */
+ if (!TEST_ptr(xpk = X509_get_X509_PUBKEY(cert))
+ || !TEST_int_gt((spki_len = i2d_X509_PUBKEY(xpk, NULL)), 0)
+ || !TEST_ptr(spki_der = OPENSSL_malloc(spki_len)))
+ goto err;
+ q = spki_der;
+ if (!TEST_int_eq(i2d_X509_PUBKEY(xpk, &q), spki_len))
+ goto err;
+ p = spki_der;
+ if (!TEST_ptr(rsa = d2i_RSA_PUBKEY(NULL, &p, spki_len))
+ || !TEST_int_ge(RSA_bits(rsa), 2048))
+ goto err;
+#endif
+
+ ret = 1;
+err:
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+ RSA_free(rsa);
+ OPENSSL_free(spki_der);
+#endif
+ X509_free(cert);
+ return ret;
+}
+
OPT_TEST_DECLARE_USAGE("<pss-self-signed-cert.pem>\n")
int setup_tests(void)
@@ -484,6 +574,7 @@ int setup_tests(void)
ADD_TEST(test_x509_revoked_delete_last_extension);
ADD_TEST(test_drop_empty_cert_keyids);
ADD_TEST(test_drop_empty_csr_keyids);
+ ADD_TEST(test_rsaesoaep_spki);
return 1;
}