pki: pki --req can use old certreq as template

When an X.509 certificate has to be renewed it is helpful to use
the old PKCS#10 certificate request as a template, so that the
distinguishedName (DN), the subjectAlternativeName (SAN) and
a certificate profile name don't have to be typed-in again.

The old public key in the existing certreq is replaced with the
new key and the signature is re-generated using the new private key.
This commit is contained in:
Andreas Steffen
2022-08-31 15:10:34 +02:00
parent 359b5739f4
commit 3fa3d2666a
4 changed files with 251 additions and 150 deletions
+123 -95
View File
@@ -270,6 +270,125 @@ METHOD(pkcs10_t, create_subjectAltName_enumerator, enumerator_t*,
return this->subjectAltNames->create_enumerator(this->subjectAltNames);
}
/**
* Generate and sign a new certificate request
*/
static bool generate(private_x509_pkcs10_t *cert, private_key_t *sign_key,
int digest_alg)
{
chunk_t key_info, subjectAltNames, attributes;
chunk_t extensionRequest = chunk_empty, certTypeExt = chunk_empty;
chunk_t challengePassword = chunk_empty, sig_scheme = chunk_empty;
identification_t *subject;
subject = cert->subject;
cert->public_key = sign_key->get_public_key(sign_key);
/* select signature scheme, if not already specified */
if (!cert->scheme)
{
INIT(cert->scheme,
.scheme = signature_scheme_from_oid(
hasher_signature_algorithm_to_oid(digest_alg,
sign_key->get_type(sign_key))),
);
}
if (cert->scheme->scheme == SIGN_UNKNOWN)
{
return FALSE;
}
if (!signature_params_build(cert->scheme, &sig_scheme))
{
return FALSE;
}
if (!cert->public_key->get_encoding(cert->public_key,
PUBKEY_SPKI_ASN1_DER, &key_info))
{
chunk_free(&sig_scheme);
return FALSE;
}
/* encode subjectAltNames */
subjectAltNames = x509_build_subjectAltNames(cert->subjectAltNames);
/* encode certTypeExt */
if (cert->certTypeExt.len > 0)
{
certTypeExt = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_MS_CERT_TYPE_EXT),
asn1_wrap(ASN1_OCTET_STRING, "m",
asn1_simple_object(ASN1_UTF8STRING, cert->certTypeExt)
));
}
/* encode extensionRequest attribute */
if (subjectAltNames.ptr || certTypeExt.ptr)
{
extensionRequest = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_EXTENSION_REQUEST),
asn1_wrap(ASN1_SET, "m",
asn1_wrap(ASN1_SEQUENCE, "mm", subjectAltNames, certTypeExt)
));
}
/* encode challengePassword attribute */
if (cert->challengePassword.len > 0)
{
challengePassword = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_CHALLENGE_PASSWORD),
asn1_wrap(ASN1_SET, "m",
asn1_simple_object(ASN1_UTF8STRING, cert->challengePassword)
));
}
attributes = asn1_wrap(ASN1_CONTEXT_C_0, "mm", extensionRequest,
challengePassword);
cert->certificationRequestInfo = asn1_wrap(ASN1_SEQUENCE, "ccmm",
ASN1_INTEGER_0,
subject->get_encoding(subject),
key_info,
attributes);
if (!sign_key->sign(sign_key, cert->scheme->scheme, cert->scheme->params,
cert->certificationRequestInfo, &cert->signature))
{
chunk_free(&sig_scheme);
return FALSE;
}
cert->encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
cert->certificationRequestInfo,
sig_scheme,
asn1_bitstring("c", cert->signature));
return TRUE;
}
METHOD(pkcs10_t, replace_key, certificate_t*,
private_x509_pkcs10_t *this, private_key_t *private,
signature_params_t *scheme, chunk_t password)
{
/* remove old public key and signature */
this->public_key->destroy(this->public_key);
this->signature = chunk_empty;
/* copy existing attributes from old certreq encoding */
this->certificationRequestInfo = chunk_empty;
this->certTypeExt = chunk_clone(this->certTypeExt);
this->challengePassword = chunk_clone((password.len > 0) ?
password : this->challengePassword);
chunk_free(&this->encoding);
signature_params_destroy(this->scheme);
this->scheme = signature_params_clone(scheme);
this->parsed = FALSE;
if (generate(this, private, HASH_SHA256))
{
return &this->public.interface.interface;
}
return NULL;
}
/**
* ASN.1 definition of a PKCS#10 extension request
*/
@@ -378,6 +497,8 @@ static bool parse_challengePassword(private_x509_pkcs10_t *this, chunk_t blob, i
}
DBG2(DBG_ASN, "L%d - challengePassword:", level);
DBG4(DBG_ASN, " '%.*s'", (int)blob.len, blob.ptr);
this->challengePassword = blob;
return TRUE;
}
@@ -558,6 +679,7 @@ static private_x509_pkcs10_t* create_empty(void)
.get_challengePassword = _get_challengePassword,
.get_flags = _get_flags,
.create_subjectAltName_enumerator = _create_subjectAltName_enumerator,
.replace_key = _replace_key,
},
},
.subjectAltNames = linked_list_create(),
@@ -567,100 +689,6 @@ static private_x509_pkcs10_t* create_empty(void)
return this;
}
/**
* Generate and sign a new certificate request
*/
static bool generate(private_x509_pkcs10_t *cert, private_key_t *sign_key,
int digest_alg)
{
chunk_t key_info, subjectAltNames, attributes;
chunk_t extensionRequest = chunk_empty, certTypeExt = chunk_empty;
chunk_t challengePassword = chunk_empty, sig_scheme = chunk_empty;
identification_t *subject;
subject = cert->subject;
cert->public_key = sign_key->get_public_key(sign_key);
/* select signature scheme, if not already specified */
if (!cert->scheme)
{
INIT(cert->scheme,
.scheme = signature_scheme_from_oid(
hasher_signature_algorithm_to_oid(digest_alg,
sign_key->get_type(sign_key))),
);
}
if (cert->scheme->scheme == SIGN_UNKNOWN)
{
return FALSE;
}
if (!signature_params_build(cert->scheme, &sig_scheme))
{
return FALSE;
}
if (!cert->public_key->get_encoding(cert->public_key,
PUBKEY_SPKI_ASN1_DER, &key_info))
{
chunk_free(&sig_scheme);
return FALSE;
}
/* encode subjectAltNames */
subjectAltNames = x509_build_subjectAltNames(cert->subjectAltNames);
/* encode certTypeExt */
if (cert->certTypeExt.len > 0)
{
certTypeExt = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_MS_CERT_TYPE_EXT),
asn1_wrap(ASN1_OCTET_STRING, "m",
asn1_simple_object(ASN1_UTF8STRING, cert->certTypeExt)
));
}
/* encode extensionRequest attribute */
if (subjectAltNames.ptr || certTypeExt.ptr)
{
extensionRequest = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_EXTENSION_REQUEST),
asn1_wrap(ASN1_SET, "m",
asn1_wrap(ASN1_SEQUENCE, "mm", subjectAltNames, certTypeExt)
));
}
/* encode challengePassword attribute */
if (cert->challengePassword.len > 0)
{
challengePassword = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_CHALLENGE_PASSWORD),
asn1_wrap(ASN1_SET, "m",
asn1_simple_object(ASN1_UTF8STRING, cert->challengePassword)
));
}
attributes = asn1_wrap(ASN1_CONTEXT_C_0, "mm", extensionRequest,
challengePassword);
cert->certificationRequestInfo = asn1_wrap(ASN1_SEQUENCE, "ccmm",
ASN1_INTEGER_0,
subject->get_encoding(subject),
key_info,
attributes);
if (!sign_key->sign(sign_key, cert->scheme->scheme, cert->scheme->params,
cert->certificationRequestInfo, &cert->signature))
{
chunk_free(&sig_scheme);
return FALSE;
}
cert->encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
cert->certificationRequestInfo,
sig_scheme,
asn1_bitstring("c", cert->signature));
return TRUE;
}
/**
* See header.
*/
@@ -705,7 +733,7 @@ x509_pkcs10_t *x509_pkcs10_gen(certificate_type_t type, va_list args)
{
private_x509_pkcs10_t *cert;
private_key_t *sign_key = NULL;
hash_algorithm_t digest_alg = HASH_SHA1;
hash_algorithm_t digest_alg = HASH_SHA256;
cert = create_empty();
while (TRUE)