* @return enumerator over subjectAltNames as identification_t*
*/
enumerator_t* (*create_subjectAltName_enumerator)(pkcs10_t *this);
+
+ /**
+ * Replace the public key and private key signature
+ *
+ * @param private new private key to be used
+ * @param scheme signature scheme
+ * @param password optionally set new password
+ */
+ certificate_t* (*replace_key)(pkcs10_t *this, private_key_t *private,
+ signature_params_t *scheme, chunk_t password);
};
#endif /** PKCS10_H_ @}*/
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
*/
}
DBG2(DBG_ASN, "L%d - challengePassword:", level);
DBG4(DBG_ASN, " '%.*s'", (int)blob.len, blob.ptr);
+ this->challengePassword = blob;
+
return TRUE;
}
.get_challengePassword = _get_challengePassword,
.get_flags = _get_flags,
.create_subjectAltName_enumerator = _create_subjectAltName_enumerator,
+ .replace_key = _replace_key,
},
},
.subjectAltNames = linked_list_create(),
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.
*/
{
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)
#include <collections/linked_list.h>
#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/pkcs10.h>
/**
* Create a self-signed PKCS#10 certificate request.
key_type_t type = KEY_ANY;
hash_algorithm_t digest = HASH_UNKNOWN;
signature_params_t *scheme = NULL;
- certificate_t *cert = NULL;
+ certificate_t *cert = NULL, *oldreq = NULL;
+ pkcs10_t *pkcs10;
private_key_t *private = NULL;
char *file = NULL, *keyid = NULL, *dn = NULL, *error = NULL;
+ char *oldreq_file = NULL;
identification_t *id = NULL;
linked_list_t *san;
chunk_t encoding = chunk_empty;
{
switch (command_getopt(&arg))
{
- case 'h':
+ case 'h': /* --help */
goto usage;
- case 't':
+ case 't': /* --type */
if (streq(arg, "rsa"))
{
type = KEY_RSA;
goto usage;
}
continue;
- case 'g':
+ case 'g': /* --digest */
if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
{
error = "invalid --digest type";
goto usage;
}
continue;
- case 'R':
+ case 'R': /* --rsa-padding */
if (streq(arg, "pss"))
{
+
pss = TRUE;
}
else if (!streq(arg, "pkcs1"))
goto usage;
}
continue;
- case 'i':
+ case 'i': /* --in */
file = arg;
continue;
- case 'd':
+ case 'd': /* --dn */
dn = arg;
continue;
- case 'a':
+ case 'a': /* --san */
san->insert_last(san, identification_create_from_string(arg));
continue;
- case 'P':
+ case 'P': /* --profile */
cert_type_ext = chunk_create(arg, strlen(arg));
continue;
- case 'p':
+ case 'p': /* --password */
challenge_password = chunk_create(arg, strlen(arg));
continue;
- case 'f':
+ case 'f': /* --outform */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
error = "invalid output format";
goto usage;
}
continue;
- case 'x':
+ case 'x': /* --keyid */
keyid = arg;
continue;
+ case 'o': /* --oldreq */
+ oldreq_file = arg;
+ continue;
case EOF:
break;
default:
break;
}
- if (!dn)
+ if (!dn && !oldreq_file)
{
- error = "--dn is required";
+ error = "--dn or --oldreq is required";
goto usage;
}
- id = identification_create_from_string(dn);
- if (id->get_type(id) != ID_DER_ASN1_DN)
- {
- error = "supplied --dn is not a distinguished name";
- goto end;
- }
+
if (file)
{
private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
goto end;
}
- cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST,
- BUILD_SIGNING_KEY, private,
- BUILD_SUBJECT, id,
- BUILD_SUBJECT_ALTNAMES, san,
- BUILD_CHALLENGE_PWD, challenge_password,
- BUILD_CERT_TYPE_EXT, cert_type_ext,
- BUILD_SIGNATURE_SCHEME, scheme,
- BUILD_END);
- if (!cert)
+ if (oldreq_file)
{
- error = "generating certificate request failed";
- goto end;
+ oldreq = lib->creds->create(lib->creds, CRED_CERTIFICATE,
+ CERT_PKCS10_REQUEST,
+ BUILD_FROM_FILE, oldreq_file, BUILD_END);
+ if (!oldreq)
+ {
+ error = "parsing certificate request failed";
+ goto end;
+ }
+ pkcs10 = (pkcs10_t*)oldreq;
+ cert = pkcs10->replace_key(pkcs10, private, scheme, challenge_password);
+ if (!cert)
+ {
+ error = "key replacement in certificate request failed";
+ oldreq->destroy(oldreq);
+ goto end;
+ }
+ }
+ else
+ {
+ id = identification_create_from_string(dn);
+ if (id->get_type(id) != ID_DER_ASN1_DN)
+ {
+ error = "supplied --dn is not a distinguished name";
+ goto end;
+ }
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST,
+ BUILD_SIGNING_KEY, private,
+ BUILD_SUBJECT, id,
+ BUILD_SUBJECT_ALTNAMES, san,
+ BUILD_CHALLENGE_PWD, challenge_password,
+ BUILD_CERT_TYPE_EXT, cert_type_ext,
+ BUILD_SIGNATURE_SCHEME, scheme,
+ BUILD_END);
+ if (!cert)
+ {
+ error = "generating certificate request failed";
+ goto end;
+ }
}
if (!cert->get_encoding(cert, form, &encoding))
{
command_register((command_t) {
req, 'r', "req",
"create a PKCS#10 certificate request",
- {"[--in file|--keyid hex] [--type rsa|ecdsa|bliss|priv] --dn distinguished-name",
- "[--san subjectAltName]+ [--profile server|client|dual|ocsp]",
- "[--password challengePassword] [--rsa-padding pkcs1|pss]",
- "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
- "[--outform der|pem]"},
+ {"[--in file|--keyid hex] [--type rsa|ecdsa|bliss|priv]",
+ " --oldreq file|--dn distinguished-name [--san subjectAltName]+",
+ "[--profile server|client|dual|ocsp] [--password challengePassword]",
+ "[--digest sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
+ "[--rsa-padding pkcs1|pss] [--outform der|pem]"},
{
- {"help", 'h', 0, "show usage information"},
- {"in", 'i', 1, "private key input file, default: stdin"},
- {"keyid", 'x', 1, "smartcard or TPM private key object handle"},
- {"type", 't', 1, "type of input key, default: priv"},
- {"dn", 'd', 1, "subject distinguished name"},
- {"san", 'a', 1, "subjectAltName to include in cert request"},
+ {"help", 'h', 0, "show usage information"},
+ {"in", 'i', 1, "private key input file, default: stdin"},
+ {"keyid", 'x', 1, "smartcard or TPM private key object handle"},
+ {"type", 't', 1, "type of input key, default: priv"},
+ {"oldreq", 'o', 1, "old certificate request to be used as a template"},
+ {"dn", 'd', 1, "subject distinguished name"},
+ {"san", 'a', 1, "subjectAltName to include in cert request"},
{"profile", 'P', 1, "certificate profile name to include in cert request"},
- {"password", 'p', 1, "challengePassword to include in cert request"},
- {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
- {"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
- {"outform", 'f', 1, "encoding of generated request, default: der"},
+ {"password", 'p', 1, "challengePassword to include in cert request"},
+ {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
+ {"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
+ {"outform", 'f', 1, "encoding of generated request, default: der"},
}
});
}
-.TH "PKI \-\-REQ" 1 "2022-08-11" "@PACKAGE_VERSION@" "strongSwan"
+.TH "PKI \-\-REQ" 1 "2022-08-30" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
.YS
.
.SY pki\ \-\-req
+.RB [ \-\-in
+.IR file | \fB\-\-keyid\fR
+.IR hex ]
+.OP \-\-type type
+.BI \-\-oldreq\~ file
+.OP \-\-password password
+.OP \-\-digest digest
+.OP \-\-rsa\-padding padding
+.OP \-\-outform encoding
+.OP \-\-debug level
+.YS
+.
+.SY pki\ \-\-req
.BI \-\-options\~ file
.YS
.
defaults to \fIpriv\fR.
.TP
.BI "\-d, \-\-dn " distinguished-name
-Subject distinguished name (DN). Required.
+Subject distinguished name (DN). Required if the
+.B \-\-dn
+option is not set.
.TP
.BI "\-a, \-\-san " subjectAltName
subjectAltName extension to include in request. Can be used multiple times.
Certificate profile name to be included in the certificate request. Can be any
UTF8 string. Supported e.g. by
.B openxpki
-with profiles (\fIpc-client\fR, \fItls-server\fR, etc.) or
+(with profiles \fIpc-client\fR, \fItls-server\fR, etc.) or
.B pki \-\-issue
-with (\fIserver\fR, \fIclient\fR, \fIdual\fR, or \fIocsp\fR) that are translated into
-corresponding Extended Key Usage (EKU) flags in the generated X.509 certificate.
+(with profiles \fIserver\fR, \fIclient\fR, \fIdual\fR, or \fIocsp\fR) that are
+translated into corresponding Extended Key Usage (EKU) flags in the generated
+X.509 certificate.
.TP
.BI "\-p, \-\-password " password
The challengePassword to include in the certificate request.
.TP
+.BI "\-o, \-\-oldreq " file
+Old certificate request to be used as a template. Required if the
+.B --dn
+option is not set. The public key in the old certificate request is replaced and
+a fresh signature is generated using the new private key. Optionally a new
+challengePassword may be set using the
+.B --password
+option.
+.TP
.BI "\-g, \-\-digest " digest
-Digest to use for signature creation. One of \fImd5\fR, \fIsha1\fR,
-\fIsha224\fR, \fIsha256\fR, \fIsha384\fR, or \fIsha512\fR. The default is
-determined based on the type and size of the signature key.
+Digest to use for signature creation. One of \fIsha1\fR, \fIsha224\fR,
+\fIsha256\fR, \fIsha384\fR, \fIsha512\fR, \fIsha3_224\fR, \fIsha3_256\fR,
+\fIsha3_384\fR, or \fIsha3_512\fR. The default is determined based on
+the type and size of the signature key.
.TP
.BI "\-R, \-\-rsa\-padding " padding
Padding to use for RSA signatures. Either \fIpkcs1\fR or \fIpss\fR, defaults
.PP
.EX
pki \-\-req \-\-in key.der \-\-dn "C=CH, O=strongSwan, CN=moon" \\
- \-\-san moon@strongswan.org \-\-profile server > req.der
+ \-\-san moon@strongswan.org \-\-profile server > req.der
+.EE
+.PP
+Generate a certificate request for a renewed key based on an existing template
+.PP
+.EX
+ pki \-\-req \-\-in myNewKey.der \-\-oldreq myReq.der > myNewReq.der
.EE
.PP
Generate a certificate request for an ECDSA key and a different digest: