static char *opt_ref = NULL;
static char *opt_secret = NULL;
static char *opt_cert = NULL;
+static char *opt_own_trusted = NULL;
static char *opt_key = NULL;
static char *opt_keypass = NULL;
static char *opt_digest = NULL;
OPT_IGNORE_KEYUSAGE, OPT_UNPROTECTED_ERRORS,
OPT_EXTRACERTSOUT, OPT_CACERTSOUT,
- OPT_REF, OPT_SECRET, OPT_CERT, OPT_KEY, OPT_KEYPASS,
+ OPT_REF, OPT_SECRET, OPT_CERT, OPT_OWN_TRUSTED, OPT_KEY, OPT_KEYPASS,
OPT_DIGEST, OPT_MAC, OPT_EXTRACERTS,
OPT_UNPROTECTED_REQUESTS,
"Client's current certificate (needed unless using -secret for PBM);"},
{OPT_MORE_STR, 0, 0,
"any further certs included are appended in extraCerts field"},
+ {"own_trusted", OPT_OWN_TRUSTED, 's',
+ "Optional certs to verify chain building for own CMP signer cert"},
{"key", OPT_KEY, 's', "Private key for the client's current certificate"},
{"keypass", OPT_KEYPASS, 's',
"Client private key (and cert and old cert file) pass phrase source"},
{(char **)&opt_ignore_keyusage}, {(char **)&opt_unprotected_errors},
{&opt_extracertsout}, {&opt_cacertsout},
- {&opt_ref}, {&opt_secret}, {&opt_cert}, {&opt_key}, {&opt_keypass},
+ {&opt_ref}, {&opt_secret},
+ {&opt_cert}, {&opt_own_trusted}, {&opt_key}, {&opt_keypass},
{&opt_digest}, {&opt_mac}, {&opt_extracerts},
{(char **)&opt_unprotected_requests},
if (opt_cert != NULL) {
X509 *cert;
STACK_OF(X509) *certs = NULL;
+ X509_STORE *own_trusted = NULL;
int ok = 0;
if (!load_cert_certs(opt_cert, &cert, &certs, 0, opt_keypass,
goto err;
ok = OSSL_CMP_CTX_set1_cert(ctx, cert);
X509_free(cert);
-
- if (ok) {
- /* add any remaining certs to the list of untrusted certs */
- STACK_OF(X509) *untrusted = OSSL_CMP_CTX_get0_untrusted_certs(ctx);
- ok = untrusted != NULL ?
- X509_add_certs(untrusted, certs,
- X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP)
- : OSSL_CMP_CTX_set1_untrusted_certs(ctx, certs);
+ if (!ok) {
+ CMP_err("out of memory");
+ } else {
+ if (opt_own_trusted != NULL) {
+ own_trusted = load_certstore(opt_own_trusted,
+ "trusted certs for verifying own CMP signer cert");
+ ok = own_trusted != NULL
+ && set1_store_parameters(own_trusted)
+ && truststore_set_host_etc(own_trusted, NULL);
+ }
+ ok = ok && OSSL_CMP_CTX_build_cert_chain(ctx, own_trusted, certs);
}
+ X509_STORE_free(own_trusted);
sk_X509_pop_free(certs, X509_free);
if (!ok)
goto err;
+ } else if (opt_own_trusted != NULL) {
+ CMP_warn("-own_trusted option is ignored without -cert");
}
if (!setup_certs(opt_extracerts, "extra certificates for CMP", ctx,
case OPT_CERT:
opt_cert = opt_str("cert");
break;
+ case OPT_OWN_TRUSTED:
+ opt_own_trusted = opt_str("own_trusted");
+ break;
case OPT_KEY:
opt_key = opt_str("key");
break;
*/
DEFINE_OSSL_CMP_CTX_set1_up_ref(cert, X509)
+int OSSL_CMP_CTX_build_cert_chain(OSSL_CMP_CTX *ctx, X509_STORE *own_trusted,
+ STACK_OF(X509) *candidates)
+{
+ STACK_OF(X509) *chain;
+
+ if (ctx == NULL) {
+ CMPerr(0, CMP_R_NULL_ARGUMENT);
+ return 0;
+ }
+
+ if (ctx->untrusted_certs != NULL ?
+ !X509_add_certs(ctx->untrusted_certs, candidates,
+ X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP) :
+ !OSSL_CMP_CTX_set1_untrusted_certs(ctx, candidates))
+ return 0;
+
+ ossl_cmp_debug(ctx, "trying to build chain for own CMP signer cert");
+ chain = ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq, own_trusted,
+ ctx->untrusted_certs, ctx->cert);
+ if (chain == NULL) {
+ CMPerr(0, CMP_R_FAILED_BUILDING_OWN_CHAIN);
+ return 0;
+ }
+ ossl_cmp_debug(ctx, "success building chain for own CMP signer cert");
+ sk_X509_pop_free(chain, X509_free); /* TODO(3.0) replace this by 'ctx->chain = chain;' when ctx->chain is available */
+ return 1;
+}
+
/*
* Set the old certificate that we are updating in KUR
* or the certificate to be revoked in RR, respectively.
"error validating protection"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_ERROR_VALIDATING_SIGNATURE),
"error validating signature"},
+ {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_BUILDING_OWN_CHAIN),
+ "failed building own chain"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_PUBKEY),
"failed extracting pubkey"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILURE_OBTAINING_RANDOM),
const unsigned char *bytes, int len);
STACK_OF(X509)
*ossl_cmp_build_cert_chain(OPENSSL_CTX *libctx, const char *propq,
+ X509_STORE *store,
STACK_OF(X509) *certs, X509 *cert);
/* from cmp_ctx.c */
ossl_cmp_debug(ctx,
"trying to build chain for own CMP signer cert");
- chain = ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq,
+ chain = ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq, NULL,
ctx->untrusted_certs, ctx->cert);
res = X509_add_certs(msg->extraCerts, chain,
X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP
}
/*-
- * Builds up the chain of intermediate CA certificates
- * starting from the given certificate <cert> as high up as possible using
- * the given list of candidate certificates, similarly to ssl_add_cert_chain().
+ * Builds a certificate chain starting from <cert>
+ * using the optional list of intermediate CA certificates <certs>.
+ * If <store> is NULL builds the chain as far down as possible, ignoring errors.
+ * Else the chain must reach a trust anchor contained in <store>.
*
- * Intended use of this function is to find all the certificates above the trust
- * anchor needed to verify an EE's own certificate. Those are supposed to be
- * included in the ExtraCerts field of every first CMP message of a transaction
- * when MSG_SIG_ALG is utilized.
+ * Returns NULL on error, else a pointer to a stack of (up_ref'ed) certificates
+ * starting with given EE certificate and followed by all available intermediate
+ * certificates down towards any trust anchor but without including the latter.
*
- * NOTE: This allocates a stack and increments the reference count of each cert,
- * so when not needed any more the stack and all its elements should be freed.
+ * NOTE: If a non-NULL stack is returned the caller is responsible for freeing.
* NOTE: In case there is more than one possibility for the chain,
* OpenSSL seems to take the first one; check X509_verify_cert() for details.
- *
- * returns a pointer to a stack of (up_ref'ed) X509 certificates containing:
- * - the EE certificate given in the function arguments (cert)
- * - all intermediate certificates up the chain toward the trust anchor
- * whereas the (self-signed) trust anchor is not included
- * returns NULL on error
*/
+/* TODO this should be of more general interest and thus be exported. */
STACK_OF(X509)
*ossl_cmp_build_cert_chain(OPENSSL_CTX *libctx, const char *propq,
+ X509_STORE *store,
STACK_OF(X509) *certs, X509 *cert)
{
STACK_OF(X509) *chain = NULL, *result = NULL;
- X509_STORE *store = X509_STORE_new();
+ X509_STORE *ts = store == NULL ? X509_STORE_new() : store;
X509_STORE_CTX *csc = NULL;
- if (certs == NULL || cert == NULL || store == NULL) {
+ if (ts == NULL || cert == NULL) {
CMPerr(0, CMP_R_NULL_ARGUMENT);
goto err;
}
- csc = X509_STORE_CTX_new_with_libctx(libctx, propq);
- if (csc == NULL)
+ if ((csc = X509_STORE_CTX_new_with_libctx(libctx, propq)) == NULL)
goto err;
-
- if (!ossl_cmp_X509_STORE_add1_certs(store, certs, 0)
- || !X509_STORE_CTX_init(csc, store, cert, NULL))
+ if (store == NULL && certs != NULL
+ && !ossl_cmp_X509_STORE_add1_certs(ts, certs, 0))
goto err;
+ if (!X509_STORE_CTX_init(csc, ts, cert,
+ store == NULL ? NULL : certs))
+ goto err;
+ /* disable any cert status/revocation checking etc. */
+ X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(csc),
+ ~(X509_V_FLAG_USE_CHECK_TIME
+ | X509_V_FLAG_NO_CHECK_TIME));
- (void)ERR_set_mark();
- /*
- * ignore return value as it would fail without trust anchor given in store
- */
- (void)X509_verify_cert(csc);
-
- /* don't leave any new errors in the queue */
- (void)ERR_pop_to_mark();
-
+ if (X509_verify_cert(csc) <= 0 && store != NULL)
+ goto err;
chain = X509_STORE_CTX_get0_chain(csc);
/* result list to store the up_ref'ed not self-signed certificates */
}
err:
- X509_STORE_free(store);
+ if (store == NULL)
+ X509_STORE_free(ts);
X509_STORE_CTX_free(csc);
return result;
}
CMP_R_ERROR_UNEXPECTED_CERTCONF:160:error unexpected certconf
CMP_R_ERROR_VALIDATING_PROTECTION:140:error validating protection
CMP_R_ERROR_VALIDATING_SIGNATURE:171:error validating signature
+CMP_R_FAILED_BUILDING_OWN_CHAIN:164:failed building own chain
CMP_R_FAILED_EXTRACTING_PUBKEY:141:failed extracting pubkey
CMP_R_FAILURE_OBTAINING_RANDOM:110:failure obtaining random
CMP_R_FAIL_INFO_OUT_OF_RANGE:129:fail info out of range
STACK_OF(X509)
*ossl_cmp_build_cert_chain(OPENSSL_CTX *libctx, const char *propq,
+ X509_STORE *store,
STACK_OF(X509) *certs, X509 *cert);
ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_MSG *msg);
=head1 DESCRIPTION
-ossl_cmp_build_cert_chain() builds up the chain of intermediate CA certificates
-starting from the given certificate I<cert> as high up as possible using
-the given list of candidate certificates, similarly to ssl_add_cert_chain().
+ossl_cmp_build_cert_chain() builds a certificate chain starting from I<cert>
+using the optional list of intermediate CA certificates I<certs>.
+If I<store> is NULL builds the chain as far down as possible, ignoring errors.
+Else the chain must reach a trust anchor contained in I<store>.
It internally uses a B<X509_STORE_CTX> structure associated with the library
context I<libctx> and property query string I<propq>, both of which may be NULL.
-Intended use of this function is to find all the certificates above the trust
-anchor needed to verify an EE's own certificate.
-Those are supposed to be included in the ExtraCerts field of every first
-CMP message of a transaction when MSG_SIG_ALG is utilized.
-This allocates a stack and increments the reference count of each cert,
-so when not needed any more the stack and all its elements should be freed.
+If a non-NULL stack is returned the caller is responsible for freeing it.
In case there is more than one possibility for the chain,
-OpenSSL seems to take the first one; check X509_verify_cert() for details.
+OpenSSL seems to take the first one; check L<X509_verify_cert(3)> for details.
ossl_cmp_calc_protection() calculates the protection for the given I<msg>
according to the algorithm and parameters in the message header's protectionAlg
it selects Signature and uses L<ossl_cmp_msg_add_extraCerts(3)>.
It also sets the protectionAlg field in the message header accordingly.
-ossl_cmp_msg_add_extraCerts() adds elements to the extraCerts field in the given
-message I<msg>. It tries to build the certificate chain of the client cert in
-the I<ctx> if present by using certificates in ctx->untrusted_certs;
-if no untrusted certs are set, it will at least add the client certificate.
+ossl_cmp_msg_add_extraCerts() adds elements to the extraCerts field in I<msg>.
+If signature-based message protection is used it adds first the CMP signer cert
+ctx->cert and then its chain ctx->chain. If this chain is not present in I<ctx>
+tries to build it using ctx->untrusted_certs and caches the result in ctx->chain.
In any case all the certificates explicitly specified to be sent out (i.e.,
I<ctx->extraCertsOut>) are added. Note that it will NOT add the root certificate
of the chain, i.e, the trust anchor (unless it is part of extraCertsOut).
ossl_cmp_build_cert_chain() returns NULL on error,
else a pointer to a stack of (up_ref'ed) certificates
-containing the EE certificate given in the function arguments (cert)
-and all intermediate certificates up the chain toward the trust anchor.
-The (self-signed) trust anchor is not included.
+starting with given EE certificate and followed by all available intermediate
+certificates down towards (but excluding) any trusted root certificate.
ossl_cmp_calc_protection() returns the protection on success, else NULL.
[B<-ref> I<value>]
[B<-secret> I<arg>]
[B<-cert> I<filename>]
+[B<-own_trusted> I<filenames>]
[B<-key> I<filename>]
[B<-keypass> I<arg>]
[B<-digest> I<name>]
=item B<-cert> I<filename>
-The client's current certificate.
+The client's current CMP signer certificate.
Requires the corresponding key to be given with B<-key>.
The subject of this certificate will be used as sender of outgoing CMP messages,
while the subject of B<-oldcert> or B<-subjectName> may provide fallback values.
the certificate to be updated if the B<-oldcert> option is not given.
If the file includes further certs, they are appended to the untrusted certs.
+=item B<-own_trusted> I<filenames>
+
+If this list of certificates is provided then the chain built for
+the CMP signer certificate given with the B<-cert> option is verified
+using the given certificates as trust anchors.
+
+Multiple filenames may be given, separated by commas and/or whitespace
+(where in the latter case the whole argument must be enclosed in "...").
+Each source may contain multiple certificates.
+
=item B<-key> I<filename>
The corresponding private key file for the client's current certificate given in
=item B<-otherpass> I<arg>
Pass phrase source for certificate given with the B<-trusted>, B<-untrusted>,
+B<-own_trusted>,
B<-out_trusted>, B<-extracerts>, B<-tls_extra>, or B<-tls_trusted> options.
If not given here, the password will be prompted for if needed.
OSSL_CMP_CTX_set1_untrusted_certs,
OSSL_CMP_CTX_get0_untrusted_certs,
OSSL_CMP_CTX_set1_cert,
+OSSL_CMP_CTX_build_cert_chain,
OSSL_CMP_CTX_set1_pkey,
OSSL_CMP_CTX_set1_referenceValue,
OSSL_CMP_CTX_set1_secretValue,
/* client authentication: */
int OSSL_CMP_CTX_set1_cert(OSSL_CMP_CTX *ctx, X509 *cert);
+ int OSSL_CMP_CTX_build_cert_chain(OSSL_CMP_CTX *ctx, X509_STORE *own_trusted,
+ STACK_OF(X509) *candidates);
int OSSL_CMP_CTX_set1_pkey(OSSL_CMP_CTX *ctx, EVP_PKEY *pkey);
int OSSL_CMP_CTX_set1_referenceValue(OSSL_CMP_CTX *ctx,
const unsigned char *ref, int len);
and any value set via B<OSSL_CMP_CTX_set1_subjectName()> are used as fallback.
The B<cert> argument may be NULL to clear the entry.
+OSSL_CMP_CTX_build_cert_chain() builds a certificate chain for the CMP signer
+certificate previously set in the B<ctx>. It adds the optional B<candidates>,
+a list of intermediate CA certs that may already constitute the targeted chain,
+to the untrusted certs that may already exist in the B<ctx>.
+Then the function uses this augumented set of certs for chain construction.
+If I<own_trusted> is NULL it builds the chain as far down as possible and
+ignores any verification errors. Else the CMP signer certificate must be
+verifiable where the chain reaches a trust anchor contained in I<own_trusted>.
+On success the function stores the resulting chain in B<ctx>
+for inclusion in the extraCerts field of signature-protected messages.
+Calling this function is optional; by default a chain construction
+is performed on demand that is equivalent to calling this function
+with the B<candidates> and I<own_trusted> arguments being NULL.
+
OSSL_CMP_CTX_set1_pkey() sets the private key corresponding to the
protection certificate B<cert> set via B<OSSL_CMP_CTX_set1_cert()>.
This key is used create signature-based protection (protectionAlg = MSG_SIG_ALG)
STACK_OF(X509) *OSSL_CMP_CTX_get0_untrusted_certs(const OSSL_CMP_CTX *ctx);
/* client authentication: */
int OSSL_CMP_CTX_set1_cert(OSSL_CMP_CTX *ctx, X509 *cert);
+int OSSL_CMP_CTX_build_cert_chain(OSSL_CMP_CTX *ctx, X509_STORE *own_trusted,
+ STACK_OF(X509) *candidates);
int OSSL_CMP_CTX_set1_pkey(OSSL_CMP_CTX *ctx, EVP_PKEY *pkey);
int OSSL_CMP_CTX_set1_referenceValue(OSSL_CMP_CTX *ctx,
const unsigned char *ref, int len);
/* certificate confirmation: */
typedef int (*OSSL_CMP_certConf_cb_t) (OSSL_CMP_CTX *ctx, X509 *cert,
int fail_info, const char **txt);
+int OSSL_CMP_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
+ const char **text);
int OSSL_CMP_CTX_set_certConf_cb(OSSL_CMP_CTX *ctx, OSSL_CMP_certConf_cb_t cb);
int OSSL_CMP_CTX_set_certConf_cb_arg(OSSL_CMP_CTX *ctx, void *arg);
void *OSSL_CMP_CTX_get_certConf_cb_arg(const OSSL_CMP_CTX *ctx);
OSSL_CMP_exec_certreq(ctx, OSSL_CMP_KUR, NULL)
int OSSL_CMP_try_certreq(OSSL_CMP_CTX *ctx, int req_type,
const OSSL_CRMF_MSG *crm, int *checkAfter);
-int OSSL_CMP_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info,
- const char **text);
X509 *OSSL_CMP_exec_RR_ses(OSSL_CMP_CTX *ctx);
STACK_OF(OSSL_CMP_ITAV) *OSSL_CMP_exec_GENM_ses(OSSL_CMP_CTX *ctx);
# define CMP_R_ERROR_UNEXPECTED_CERTCONF 160
# define CMP_R_ERROR_VALIDATING_PROTECTION 140
# define CMP_R_ERROR_VALIDATING_SIGNATURE 171
+# define CMP_R_FAILED_BUILDING_OWN_CHAIN 164
# define CMP_R_FAILED_EXTRACTING_PUBKEY 141
# define CMP_R_FAILURE_OBTAINING_RANDOM 110
# define CMP_R_FAIL_INFO_OUT_OF_RANGE 129
{
int ret = 0;
OSSL_CMP_CTX *ctx = fixture->cmp_ctx;
+ X509_STORE *store;
STACK_OF(X509) *chain =
- ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq,
+ ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq, NULL,
fixture->certs, fixture->cert);
if (TEST_ptr(chain)) {
ret = TEST_int_eq(0, STACK_OF_X509_cmp(chain, fixture->chain));
sk_X509_pop_free(chain, X509_free);
}
+ if (!ret)
+ return 0;
+
+ if (TEST_ptr(store = X509_STORE_new())
+ && TEST_true(X509_STORE_add_cert(store, root))) {
+ X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(store),
+ X509_V_FLAG_NO_CHECK_TIME);
+ chain = ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq,
+ store, fixture->certs, fixture->cert);
+ ret = TEST_int_eq(fixture->expected, chain != NULL);
+ if (ret && chain != NULL) {
+ /* Check whether chain built is equal to the expected one */
+ ret = TEST_int_eq(0, STACK_OF_X509_cmp(chain, fixture->chain));
+ sk_X509_pop_free(chain, X509_free);
+ }
+ }
+ X509_STORE_free(store);
return ret;
}
static int test_cmp_build_cert_chain(void)
{
SETUP_TEST_FIXTURE(CMP_PROTECT_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
fixture->cert = endentity2;
if (!TEST_ptr(fixture->certs = sk_X509_new_null())
|| !TEST_ptr(fixture->chain = sk_X509_new_null())
static int test_cmp_build_cert_chain_missing_intermediate(void)
{
SETUP_TEST_FIXTURE(CMP_PROTECT_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
fixture->cert = endentity2;
if (!TEST_ptr(fixture->certs = sk_X509_new_null())
|| !TEST_ptr(fixture->chain = sk_X509_new_null())
return result;
}
-static int test_cmp_build_cert_chain_missing_root(void)
+static int test_cmp_build_cert_chain_no_root(void)
{
SETUP_TEST_FIXTURE(CMP_PROTECT_TEST_FIXTURE, set_up);
+ fixture->expected = 1;
fixture->cert = endentity2;
if (!TEST_ptr(fixture->certs = sk_X509_new_null())
|| !TEST_ptr(fixture->chain = sk_X509_new_null())
static int test_cmp_build_cert_chain_no_certs(void)
{
SETUP_TEST_FIXTURE(CMP_PROTECT_TEST_FIXTURE, set_up);
+ fixture->expected = 0;
fixture->cert = endentity2;
if (!TEST_ptr(fixture->certs = sk_X509_new_null())
|| !TEST_ptr(fixture->chain = sk_X509_new_null())
#ifndef OPENSSL_NO_EC
ADD_TEST(test_cmp_build_cert_chain);
- ADD_TEST(test_cmp_build_cert_chain_missing_root);
+ ADD_TEST(test_cmp_build_cert_chain_no_root);
ADD_TEST(test_cmp_build_cert_chain_missing_intermediate);
ADD_TEST(test_cmp_build_cert_chain_no_certs);
#endif
OSSL_CMP_CTX_get0_untrusted_certs ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_cert ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_pkey ? 3_0_0 EXIST::FUNCTION:CMP
+OSSL_CMP_CTX_build_cert_chain ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_referenceValue ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_secretValue ? 3_0_0 EXIST::FUNCTION:CMP
OSSL_CMP_CTX_set1_recipient ? 3_0_0 EXIST::FUNCTION:CMP