Commit a0945b66bc for openssl.org
commit a0945b66bc0369fab5b5dd79f444799700d48a7f
Author: Bob Beck <beck@openssl.org>
Date: Fri Sep 4 14:27:54 2026 -0600
Remove the library context from X509
The X509_PUBKEY inside a certificate has a library context and
property query because it needs them to decode the public key. The
X509 carried a second copy, which the ASN.1 decoder cannot reach: it
seeds the X509_PUBKEY with the context it is given, for X509_new_ex()
+ d2i_X509() as well as for certificates embedded in a CMS, PKCS#7 or
CMP structure, but not the X509. ossl_x509_set0_libctx() existed only
to patch the copy afterwards, on refcounted certificates others may
share, and left the public key in the old context.
Drop the copy. The functions that fetch algorithms for a certificate
take the context from its public key, which is the value they were
seeded with, so their behaviour is unchanged. Remove
ossl_x509_set0_libctx() and its callers. X509_PUBKEY_set() gives a
replacement key the context of the one it replaces, so
X509_set_pubkey() keeps it.
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Merge-date: Mon Oct 26 19:04:41 2026
Merged-from: https://github.com/openssl/openssl/pull/32690
diff --git a/crypto/cmp/cmp_msg.c b/crypto/cmp/cmp_msg.c
index 044cf83979..02ef59caae 100644
--- a/crypto/cmp/cmp_msg.c
+++ b/crypto/cmp/cmp_msg.c
@@ -1178,8 +1178,6 @@ X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx, const OSSL_CMP_CE
}
if (crt == NULL)
ERR_raise(ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND);
- else
- (void)ossl_x509_set0_libctx(crt, ctx->libctx, ctx->propq);
return crt;
}
diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c
index bd66900861..659f94c04d 100644
--- a/crypto/cms/cms_env.c
+++ b/crypto/cms/cms_env.c
@@ -207,9 +207,6 @@ void ossl_cms_RecipientInfos_set_cmsctx(CMS_ContentInfo *cms)
break;
case CMS_RECIPINFO_TRANS:
ri->d.ktri->cms_ctx = ctx;
- ossl_x509_set0_libctx(ri->d.ktri->recip,
- ossl_cms_ctx_get0_libctx(ctx),
- ossl_cms_ctx_get0_propq(ctx));
break;
case CMS_RECIPINFO_KEK:
ri->d.kekri->cms_ctx = ctx;
diff --git a/crypto/cms/cms_lib.c b/crypto/cms/cms_lib.c
index 11aac8656e..72fcd72770 100644
--- a/crypto/cms/cms_lib.c
+++ b/crypto/cms/cms_lib.c
@@ -88,24 +88,8 @@ const char *ossl_cms_ctx_get0_propq(const CMS_CTX *ctx)
void ossl_cms_resolve_libctx(CMS_ContentInfo *ci)
{
- int i;
- CMS_CertificateChoices *cch;
- STACK_OF(CMS_CertificateChoices) **pcerts;
- const CMS_CTX *ctx = ossl_cms_get0_cmsctx(ci);
- OSSL_LIB_CTX *libctx = ossl_cms_ctx_get0_libctx(ctx);
- const char *propq = ossl_cms_ctx_get0_propq(ctx);
-
ossl_cms_SignerInfos_set_cmsctx(ci);
ossl_cms_RecipientInfos_set_cmsctx(ci);
-
- pcerts = cms_get0_certificate_choices(ci);
- if (pcerts != NULL) {
- for (i = 0; i < sk_CMS_CertificateChoices_num(*pcerts); i++) {
- cch = sk_CMS_CertificateChoices_value(*pcerts, i);
- if (cch->type == CMS_CERTCHOICE_CERT)
- ossl_x509_set0_libctx(cch->d.certificate, libctx, propq);
- }
- }
}
const ASN1_OBJECT *CMS_get0_type(const CMS_ContentInfo *cms)
diff --git a/crypto/ocsp/ocsp_cl.c b/crypto/ocsp/ocsp_cl.c
index 3e057caa61..14537be9d8 100644
--- a/crypto/ocsp/ocsp_cl.c
+++ b/crypto/ocsp/ocsp_cl.c
@@ -73,6 +73,9 @@ int OCSP_request_sign(OCSP_REQUEST *req,
const EVP_MD *dgst,
const STACK_OF(X509) *certs, unsigned long flags)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
if (!OCSP_request_set1_name(req, X509_get_subject_name(signer)))
goto err;
@@ -84,7 +87,8 @@ int OCSP_request_sign(OCSP_REQUEST *req,
OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
goto err;
}
- if (!OCSP_REQUEST_sign(req, key, dgst, signer->libctx, signer->propq))
+ ossl_x509_get0_libctx(signer, &libctx, &propq);
+ if (!OCSP_REQUEST_sign(req, key, dgst, libctx, propq))
goto err;
}
diff --git a/crypto/ocsp/ocsp_srv.c b/crypto/ocsp/ocsp_srv.c
index 9e77dcff74..1f996c98f1 100644
--- a/crypto/ocsp/ocsp_srv.c
+++ b/crypto/ocsp/ocsp_srv.c
@@ -216,13 +216,16 @@ int OCSP_basic_sign(OCSP_BASICRESP *brsp,
{
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
EVP_PKEY_CTX *pkctx = NULL;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
int i;
if (ctx == NULL)
return 0;
+ ossl_x509_get0_libctx(signer, &libctx, &propq);
if (!EVP_DigestSignInit_ex(ctx, &pkctx, EVP_MD_get0_name(dgst),
- signer->libctx, signer->propq, key, NULL)) {
+ libctx, propq, key, NULL)) {
EVP_MD_CTX_free(ctx);
return 0;
}
@@ -276,9 +279,13 @@ err:
int OCSP_RESPID_set_by_key(OCSP_RESPID *respid, X509 *cert)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
if (cert == NULL)
return 0;
- return OCSP_RESPID_set_by_key_ex(respid, cert, cert->libctx, cert->propq);
+ ossl_x509_get0_libctx(cert, &libctx, &propq);
+ return OCSP_RESPID_set_by_key_ex(respid, cert, libctx, propq);
}
int OCSP_RESPID_match_ex(OCSP_RESPID *respid, X509 *cert, OSSL_LIB_CTX *libctx,
@@ -321,7 +328,11 @@ err:
int OCSP_RESPID_match(OCSP_RESPID *respid, X509 *cert)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
if (cert == NULL)
return 0;
- return OCSP_RESPID_match_ex(respid, cert, cert->libctx, cert->propq);
+ ossl_x509_get0_libctx(cert, &libctx, &propq);
+ return OCSP_RESPID_match_ex(respid, cert, libctx, propq);
}
diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c
index 70e670cd63..e6dd9e4bb6 100644
--- a/crypto/ocsp/ocsp_vfy.c
+++ b/crypto/ocsp/ocsp_vfy.c
@@ -77,6 +77,8 @@ static int ocsp_verify(OCSP_REQUEST *req, OCSP_BASICRESP *bs,
X509 *signer, unsigned long flags)
{
EVP_PKEY *skey;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
int ret = 1;
if ((flags & OCSP_NOSIGS) == 0) {
@@ -84,10 +86,11 @@ static int ocsp_verify(OCSP_REQUEST *req, OCSP_BASICRESP *bs,
ERR_raise(ERR_LIB_OCSP, OCSP_R_NO_SIGNER_KEY);
return -1;
}
+ ossl_x509_get0_libctx(signer, &libctx, &propq);
if (req != NULL)
- ret = OCSP_REQUEST_verify(req, skey, signer->libctx, signer->propq);
+ ret = OCSP_REQUEST_verify(req, skey, libctx, propq);
else
- ret = OCSP_BASICRESP_verify(bs, skey, signer->libctx, signer->propq);
+ ret = OCSP_BASICRESP_verify(bs, skey, libctx, propq);
if (ret <= 0)
ERR_raise(ERR_LIB_OCSP, OCSP_R_SIGNATURE_FAILURE);
}
@@ -191,6 +194,8 @@ static X509 *ocsp_find_signer_sk(const STACK_OF(X509) *certs, OCSP_RESPID *id)
unsigned char tmphash[SHA_DIGEST_LENGTH], *keyhash;
EVP_MD *md;
X509 *x;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
/* Easy if lookup by name */
if (id->type == V_OCSP_RESPID_NAME)
@@ -205,7 +210,8 @@ static X509 *ocsp_find_signer_sk(const STACK_OF(X509) *certs, OCSP_RESPID *id)
/* Calculate hash of each key and compare */
for (i = 0; i < sk_X509_num(certs); i++) {
if ((x = sk_X509_value(certs, i)) != NULL) {
- if ((md = EVP_MD_fetch(x->libctx, SN_sha1, x->propq)) == NULL)
+ ossl_x509_get0_libctx(x, &libctx, &propq);
+ if ((md = EVP_MD_fetch(libctx, SN_sha1, propq)) == NULL)
break;
r = X509_pubkey_digest(x, md, tmphash, NULL);
EVP_MD_free(md);
diff --git a/crypto/pkcs12/p12_sbag.c b/crypto/pkcs12/p12_sbag.c
index 860553162f..1966412cbf 100644
--- a/crypto/pkcs12/p12_sbag.c
+++ b/crypto/pkcs12/p12_sbag.c
@@ -117,19 +117,12 @@ X509_CRL *PKCS12_SAFEBAG_get1_crl(const PKCS12_SAFEBAG *bag)
X509 *PKCS12_SAFEBAG_get1_cert_ex(const PKCS12_SAFEBAG *bag,
OSSL_LIB_CTX *libctx, const char *propq)
{
- X509 *ret = NULL;
-
if (PKCS12_SAFEBAG_get_nid(bag) != NID_certBag)
return NULL;
if (OBJ_obj2nid(bag->value.bag->type) != NID_x509Certificate)
return NULL;
- ret = ASN1_item_unpack_ex(bag->value.bag->value.octet,
+ return ASN1_item_unpack_ex(bag->value.bag->value.octet,
ASN1_ITEM_rptr(X509), libctx, propq);
- if (!ossl_x509_set0_libctx(ret, libctx, propq)) {
- X509_free(ret);
- return NULL;
- }
- return ret;
}
X509_CRL *PKCS12_SAFEBAG_get1_crl_ex(const PKCS12_SAFEBAG *bag,
diff --git a/crypto/pkcs7/pk7_lib.c b/crypto/pkcs7/pk7_lib.c
index 539bb1633b..bb23d074c2 100644
--- a/crypto/pkcs7/pk7_lib.c
+++ b/crypto/pkcs7/pk7_lib.c
@@ -432,46 +432,17 @@ STACK_OF(X509) *pkcs7_get0_certificates(const PKCS7 *p7)
return NULL;
}
-static STACK_OF(PKCS7_RECIP_INFO) *pkcs7_get_recipient_info(const PKCS7 *p7)
-{
- if (p7->d.ptr == NULL)
- return NULL;
- if (PKCS7_type_is_signedAndEnveloped(p7))
- return p7->d.signed_and_enveloped->recipientinfo;
- if (PKCS7_type_is_enveloped(p7))
- return p7->d.enveloped->recipientinfo;
- return NULL;
-}
-
-/*
- * Set up the library context into any loaded structure that needs it.
- * i.e loaded X509 objects.
- */
+/* Set up the library context into any loaded structure that needs it. */
void ossl_pkcs7_resolve_libctx(PKCS7 *p7)
{
int i;
const PKCS7_CTX *ctx = ossl_pkcs7_get0_ctx(p7);
- OSSL_LIB_CTX *libctx = ossl_pkcs7_ctx_get0_libctx(ctx);
- const char *propq = ossl_pkcs7_ctx_get0_propq(ctx);
- STACK_OF(PKCS7_RECIP_INFO) *rinfos;
STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
- STACK_OF(X509) *certs;
if (ctx == NULL || p7->d.ptr == NULL)
return;
- rinfos = pkcs7_get_recipient_info(p7);
sinfos = PKCS7_get_signer_info(p7);
- certs = pkcs7_get0_certificates(p7);
-
- for (i = 0; i < sk_X509_num(certs); i++)
- ossl_x509_set0_libctx(sk_X509_value(certs, i), libctx, propq);
-
- for (i = 0; i < sk_PKCS7_RECIP_INFO_num(rinfos); i++) {
- PKCS7_RECIP_INFO *ri = sk_PKCS7_RECIP_INFO_value(rinfos, i);
-
- ossl_x509_set0_libctx(ri->cert, libctx, propq);
- }
for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) {
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
diff --git a/crypto/x509/t_x509.c b/crypto/x509/t_x509.c
index 8e57d9b328..87157f80bb 100644
--- a/crypto/x509/t_x509.c
+++ b/crypto/x509/t_x509.c
@@ -203,6 +203,8 @@ int X509_ocspid_print(BIO *bp, const X509 *x)
const ASN1_BIT_STRING *keybstr;
const X509_NAME *subj;
EVP_MD *md = NULL;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
if (x == NULL || bp == NULL)
return 0;
@@ -220,7 +222,8 @@ int X509_ocspid_print(BIO *bp, const X509 *x)
if (i2d_X509_NAME(subj, &dertmp) < 0)
goto err;
- md = EVP_MD_fetch(x->libctx, SN_sha1, x->propq);
+ ossl_x509_get0_libctx(x, &libctx, &propq);
+ md = EVP_MD_fetch(libctx, SN_sha1, propq);
if (md == NULL)
goto err;
if (!EVP_Digest(der, derlen, SHA1md, NULL, md, NULL))
diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c
index 996ab10707..ba842d340d 100644
--- a/crypto/x509/x509_cmp.c
+++ b/crypto/x509/x509_cmp.c
@@ -41,13 +41,16 @@ unsigned long X509_issuer_and_serial_hash(const X509 *a)
unsigned char md[16];
char *f = NULL;
EVP_MD *digest = NULL;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
if (ctx == NULL)
goto err;
f = X509_NAME_oneline(a->cert_info.issuer, NULL, 0);
if (f == NULL)
goto err;
- digest = EVP_MD_fetch(a->libctx, SN_md5, a->propq);
+ ossl_x509_get0_libctx(a, &libctx, &propq);
+ digest = EVP_MD_fetch(libctx, SN_md5, propq);
if (digest == NULL)
goto err;
diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c
index 90f9b641f7..973897bdbc 100644
--- a/crypto/x509/x509_req.c
+++ b/crypto/x509/x509_req.c
@@ -25,8 +25,11 @@ X509_REQ *X509_to_X509_REQ(const X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
X509_REQ_INFO *ri;
int i;
EVP_PKEY *pktmp;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
- ret = X509_REQ_new_ex(x->libctx, x->propq);
+ ossl_x509_get0_libctx(x, &libctx, &propq);
+ ret = X509_REQ_new_ex(libctx, propq);
if (ret == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_ASN1_LIB);
goto err;
diff --git a/crypto/x509/x509_set.c b/crypto/x509/x509_set.c
index b1bd486889..ac156e458d 100644
--- a/crypto/x509/x509_set.c
+++ b/crypto/x509/x509_set.c
@@ -320,10 +320,14 @@ int ossl_x509_get_signature_info_ex(const X509 *x, int *mdnid, int *pknid,
int X509_get_signature_info(const X509 *x, int *mdnid, int *pknid, int *secbits,
uint32_t *flags)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
if (x == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ ossl_x509_get0_libctx(x, &libctx, &propq);
return ossl_x509_get_signature_info_ex(x, mdnid, pknid, secbits, flags,
- x->libctx, x->propq);
+ libctx, propq);
}
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
index 243ecc461c..f102a74989 100644
--- a/crypto/x509/x_all.c
+++ b/crypto/x509/x_all.c
@@ -143,7 +143,11 @@ int ossl_x509_verify_ex(const X509 *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
int X509_verify(const X509 *a, EVP_PKEY *r)
{
- return ossl_x509_verify_ex(a, r, a->libctx, a->propq);
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
+ ossl_x509_get0_libctx(a, &libctx, &propq);
+ return ossl_x509_verify_ex(a, r, libctx, propq);
}
int X509_REQ_verify_ex(X509_REQ *a, EVP_PKEY *r, OSSL_LIB_CTX *libctx,
@@ -198,6 +202,8 @@ static int bad_keyid_exts(const STACK_OF(X509_EXTENSION) *exts)
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
{
const STACK_OF(X509_EXTENSION) *exts;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
if (x == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
@@ -216,9 +222,10 @@ int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
* which exist below are the same.
*/
x->cert_info.enc.modified = 1;
+ ossl_x509_get0_libctx(x, &libctx, &propq);
return ASN1_item_sign_ex(ASN1_ITEM_rptr(X509_CINF), &x->cert_info.signature,
&x->sig_alg, &x->signature, &x->cert_info, NULL,
- pkey, md, x->libctx, x->propq);
+ pkey, md, libctx, propq);
}
int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx)
@@ -672,8 +679,12 @@ int ossl_x509_internal_fingerprint(const ASN1_ITEM *it, const void *val,
int X509_digest(const X509 *cert, const EVP_MD *md, unsigned char *data,
unsigned int *len)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
+ ossl_x509_get0_libctx(cert, &libctx, &propq);
return ossl_asn1_item_digest_ex(ASN1_ITEM_rptr(X509), md, (char *)cert,
- data, len, cert->libctx, cert->propq);
+ data, len, libctx, propq);
}
ASN1_OCTET_STRING *ossl_x509_digest_sig_ex(const X509 *cert,
@@ -769,6 +780,9 @@ err:
ASN1_OCTET_STRING *X509_digest_sig(const X509 *cert,
EVP_MD **md_used, int *md_is_fallback)
{
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
if (cert == NULL) {
if (md_used != NULL)
*md_used = NULL;
@@ -777,8 +791,8 @@ ASN1_OCTET_STRING *X509_digest_sig(const X509 *cert,
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
- return ossl_x509_digest_sig_ex(cert, md_used, md_is_fallback,
- cert->libctx, cert->propq);
+ ossl_x509_get0_libctx(cert, &libctx, &propq);
+ return ossl_x509_digest_sig_ex(cert, md_used, md_is_fallback, libctx, propq);
}
int X509_CRL_digest(const X509_CRL *data, const EVP_MD *type,
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index df4bccbeef..a697e52557 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -376,6 +376,10 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
goto error;
}
+ /* The replacement inherits the library context of the object it replaces */
+ if (*x != NULL && !x509_pubkey_set0_libctx(pk, (*x)->libctx, (*x)->propq))
+ goto error;
+
X509_PUBKEY_free(*x);
if (!EVP_PKEY_up_ref(pkey)) {
ERR_raise(ERR_LIB_X509, ERR_R_INTERNAL_ERROR);
diff --git a/crypto/x509/x_x509.c b/crypto/x509/x_x509.c
index 43a19a4d8f..0d1c694069 100644
--- a/crypto/x509/x_x509.c
+++ b/crypto/x509/x_x509.c
@@ -95,26 +95,15 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
ASIdentifiers_free(ret->rfc3779_asid);
#endif
ASN1_OCTET_STRING_free(ret->distinguishing_id);
- OPENSSL_free(ret->propq);
break;
- case ASN1_OP_DUP_POST: {
- X509 *old = exarg;
-
- if (!ossl_x509_set0_libctx(ret, old->libctx, old->propq))
- return 0;
- } break;
- case ASN1_OP_GET0_LIBCTX: {
- OSSL_LIB_CTX **libctx = exarg;
-
- *libctx = ret->libctx;
- } break;
-
- case ASN1_OP_GET0_PROPQ: {
- const char **propq = exarg;
+ case ASN1_OP_GET0_LIBCTX:
+ ossl_x509_get0_libctx(ret, exarg, NULL);
+ break;
- *propq = ret->propq;
- } break;
+ case ASN1_OP_GET0_PROPQ:
+ ossl_x509_get0_libctx(ret, NULL, exarg);
+ break;
default:
break;
@@ -132,36 +121,20 @@ ASN1_SEQUENCE_ref(X509, x509_cb) = {
IMPLEMENT_ASN1_FUNCTIONS(X509)
IMPLEMENT_ASN1_DUP_FUNCTION(X509)
-/*
- * This should only be used if the X509 object was embedded inside another
- * asn1 object and it needs a libctx to operate.
- * Use X509_new_ex() instead if possible.
- */
-int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq)
+void ossl_x509_get0_libctx(const X509 *x, OSSL_LIB_CTX **libctx,
+ const char **propq)
{
- if (x != NULL) {
- x->libctx = libctx;
- OPENSSL_free(x->propq);
- x->propq = NULL;
- if (propq != NULL) {
- x->propq = OPENSSL_strdup(propq);
- if (x->propq == NULL)
- return 0;
- }
- }
- return 1;
+ if (libctx != NULL)
+ *libctx = NULL;
+ if (propq != NULL)
+ *propq = NULL;
+ if (x->cert_info.key != NULL)
+ (void)ossl_x509_PUBKEY_get0_libctx(libctx, propq, x->cert_info.key);
}
X509 *X509_new_ex(OSSL_LIB_CTX *libctx, const char *propq)
{
- X509 *cert = NULL;
-
- cert = (X509 *)ASN1_item_new_ex(ASN1_ITEM_rptr(X509), libctx, propq);
- if (!ossl_x509_set0_libctx(cert, libctx, propq)) {
- X509_free(cert);
- cert = NULL;
- }
- return cert;
+ return (X509 *)ASN1_item_new_ex(ASN1_ITEM_rptr(X509), libctx, propq);
}
int X509_set_ex_data(X509 *r, int idx, void *arg)
diff --git a/doc/man3/X509_new.pod b/doc/man3/X509_new.pod
index 264767e834..882b5d6a1b 100644
--- a/doc/man3/X509_new.pod
+++ b/doc/man3/X509_new.pod
@@ -28,7 +28,7 @@ X509_new_ex() allocates and initializes a X509 structure with a
library context of I<libctx>, property query of I<propq> and a reference
count of B<1>. Many X509 functions such as X509_check_purpose(), and
X509_verify() use this library context to select which providers supply the
-fetched algorithms (SHA1 is used internally). This created X509 object can then
+fetched algorithms. This created X509 object can then
be used when loading binary data using d2i_X509().
X509_new() is similar to X509_new_ex() but sets the library context
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 7c9c1967ec..f58bd9e90e 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -227,9 +227,6 @@ struct x509_st {
/* Set on live certificates for authentication purposes */
ASN1_OCTET_STRING *distinguishing_id;
-
- OSSL_LIB_CTX *libctx;
- char *propq;
} /* X509 */;
/*
@@ -438,7 +435,19 @@ ASN1_OCTET_STRING *ossl_x509_digest_sig_ex(const X509 *cert,
EVP_MD **md_used, int *md_is_fallback,
OSSL_LIB_CTX *libctx, const char *propq);
-int ossl_x509_set0_libctx(X509 *x, OSSL_LIB_CTX *libctx, const char *propq);
+/**
+ * @brief Get the library context and property query of a certificate.
+ *
+ * A certificate has no library context of its own; the one given to
+ * X509_new_ex(), or to the decoder of the structure it is embedded in, is
+ * kept by its X509_PUBKEY, which needs it to decode the public key.
+ *
+ * @param x the certificate
+ * @param libctx where to store the library context, may be NULL
+ * @param propq where to store the property query, may be NULL
+ */
+void ossl_x509_get0_libctx(const X509 *x, OSSL_LIB_CTX **libctx,
+ const char **propq);
int ossl_x509_crl_set0_libctx(X509_CRL *x, OSSL_LIB_CTX *libctx,
const char *propq);
int ossl_x509_req_set0_libctx(X509_REQ *x, OSSL_LIB_CTX *libctx,