diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt index b09f9eafa6..c91c1262af 100644 --- a/src/libstrongswan/asn1/oid.txt +++ b/src/libstrongswan/asn1/oid.txt @@ -212,7 +212,7 @@ 0x03 "msSGC" 0x04 "msEncryptingFileSystem" 0x14 "msEnrollmentInfrastructure" - 0x02 "msCertificateTypeExtension" + 0x02 "msCertTypeExtension" OID_MS_CERT_TYPE_EXT 0x02 "msSmartcardLogon" OID_MS_SMARTCARD_LOGON 0x03 "msUPN" OID_USER_PRINCIPAL_NAME 0x15 "msCertSrvInfrastructure" diff --git a/src/libstrongswan/credentials/builder.c b/src/libstrongswan/credentials/builder.c index 196118f829..bb50e097f4 100644 --- a/src/libstrongswan/credentials/builder.c +++ b/src/libstrongswan/credentials/builder.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Martin Willi - * Copyright (C) 2016-2019 Andreas Steffen + * Copyright (C) 2016-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -59,6 +59,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END, "BUILD_REVOKED_ENUMERATOR", "BUILD_BASE_CRL", "BUILD_CHALLENGE_PWD", + "BUILD_CERT_TYPE_EXT", "BUILD_PKCS7_ATTRIBUTE", "BUILD_PKCS11_MODULE", "BUILD_PKCS11_SLOT", diff --git a/src/libstrongswan/credentials/builder.h b/src/libstrongswan/credentials/builder.h index f09c011460..6d143dd4fc 100644 --- a/src/libstrongswan/credentials/builder.h +++ b/src/libstrongswan/credentials/builder.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Martin Willi - * Copyright (C) 2016-2019 Andreas Steffen + * Copyright (C) 2016-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -127,6 +127,8 @@ enum builder_part_t { BUILD_BASE_CRL, /** PKCS#10 challenge password */ BUILD_CHALLENGE_PWD, + /** PKCS#10 certificate type extension */ + BUILD_CERT_TYPE_EXT, /** PKCS#7 attribute, int oid, chunk_t with ASN1 type encoded value */ BUILD_PKCS7_ATTRIBUTE, /** friendly name of a PKCS#11 module, null terminated char* */ diff --git a/src/libstrongswan/credentials/certificates/pkcs10.h b/src/libstrongswan/credentials/certificates/pkcs10.h index a6727bc3a2..ab5e3cdaad 100644 --- a/src/libstrongswan/credentials/certificates/pkcs10.h +++ b/src/libstrongswan/credentials/certificates/pkcs10.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Andreas Steffen + * Copyright (C) 2009-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -22,6 +22,8 @@ #ifndef PKCS10_H_ #define PKCS10_H_ +#include "x509.h" + #include #include @@ -47,8 +49,15 @@ struct pkcs10_t { */ chunk_t (*get_challengePassword)(pkcs10_t *this); + /** + * Get Extended Key Usage (EKU) flags + * + * @return EKU flags + */ + x509_flag_t (*get_flags)(pkcs10_t *this); + /** - * Get. + * Get subjectAltNames * * @return enumerator over subjectAltNames as identification_t* */ diff --git a/src/libstrongswan/plugins/x509/x509_pkcs10.c b/src/libstrongswan/plugins/x509/x509_pkcs10.c index f1d90abd0a..cee518b51a 100644 --- a/src/libstrongswan/plugins/x509/x509_pkcs10.c +++ b/src/libstrongswan/plugins/x509/x509_pkcs10.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2005 Jan Hutter, Martin Willi - * Copyright (C) 2009-2017 Andreas Steffen + * Copyright (C) 2009-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -72,6 +72,11 @@ struct private_x509_pkcs10_t { */ chunk_t challengePassword; + /** + * certificate type extension + */ + chunk_t certTypeExt; + /** * Signature scheme */ @@ -230,6 +235,35 @@ METHOD(pkcs10_t, get_challengePassword, chunk_t, return this->challengePassword; } +METHOD(pkcs10_t, get_flags, x509_flag_t, + private_x509_pkcs10_t *this) +{ + x509_flag_t flags = X509_NONE; + char *profile; + + profile = strndup(this->certTypeExt.ptr, this->certTypeExt.len); + + if (strcaseeq(profile, "server")) + { + flags |= X509_SERVER_AUTH; + } + else if (strcaseeq(profile, "client")) + { + flags |= X509_CLIENT_AUTH; + } + else if (strcaseeq(profile, "dual")) + { + flags |= (X509_SERVER_AUTH | X509_CLIENT_AUTH); + } + else if (strcaseeq(profile, "ocsp")) + { + flags |= X509_OCSP_SIGNER; + } + free(profile); + + return flags; +} + METHOD(pkcs10_t, create_subjectAltName_enumerator, enumerator_t*, private_x509_pkcs10_t *this) { @@ -240,12 +274,12 @@ METHOD(pkcs10_t, create_subjectAltName_enumerator, enumerator_t*, * ASN.1 definition of a PKCS#10 extension request */ static const asn1Object_t extensionRequestObjects[] = { - { 0, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ + { 0, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ { 1, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ - { 2, "extnID", ASN1_OID, ASN1_BODY }, /* 2 */ + { 2, "extnID", ASN1_OID, ASN1_BODY }, /* 2 */ { 2, "critical", ASN1_BOOLEAN, ASN1_DEF|ASN1_BODY }, /* 3 */ { 2, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ - { 1, "end loop", ASN1_EOC, ASN1_END }, /* 5 */ + { 0, "end loop", ASN1_EOC, ASN1_END }, /* 5 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define PKCS10_EXTN_ID 2 @@ -291,6 +325,14 @@ static bool parse_extension_request(private_x509_pkcs10_t *this, chunk_t blob, i goto end; } break; + case OID_MS_CERT_TYPE_EXT: + if (!asn1_parse_simple_object(&object, ASN1_UTF8STRING, + level, "certTypeExt")) + { + goto end; + } + this->certTypeExt = object; + break; default: break; } @@ -482,6 +524,7 @@ METHOD(certificate_t, destroy, void, { /* only parsed certificate requests point these fields to "encoded" */ chunk_free(&this->certificationRequestInfo); chunk_free(&this->challengePassword); + chunk_free(&this->certTypeExt); chunk_free(&this->signature); } free(this); @@ -513,6 +556,7 @@ static private_x509_pkcs10_t* create_empty(void) .destroy = _destroy, }, .get_challengePassword = _get_challengePassword, + .get_flags = _get_flags, .create_subjectAltName_enumerator = _create_subjectAltName_enumerator, }, }, @@ -530,7 +574,7 @@ 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; + chunk_t extensionRequest = chunk_empty, certTypeExt = chunk_empty; chunk_t challengePassword = chunk_empty, sig_scheme = chunk_empty; identification_t *subject; @@ -565,35 +609,44 @@ static bool generate(private_x509_pkcs10_t *cert, private_key_t *sign_key, /* encode subjectAltNames */ subjectAltNames = x509_build_subjectAltNames(cert->subjectAltNames); - if (subjectAltNames.ptr) + /* 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, "m", subjectAltNames) - )); + 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) { - asn1_t type = asn1_is_printablestring(cert->challengePassword) ? - ASN1_PRINTABLESTRING : ASN1_T61STRING; - challengePassword = asn1_wrap(ASN1_SEQUENCE, "mm", - asn1_build_known_oid(OID_CHALLENGE_PASSWORD), - asn1_wrap(ASN1_SET, "m", - asn1_simple_object(type, cert->challengePassword) - ) - ); + 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); - + 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)) { @@ -685,6 +738,9 @@ x509_pkcs10_t *x509_pkcs10_gen(certificate_type_t type, va_list args) case BUILD_CHALLENGE_PWD: cert->challengePassword = chunk_clone(va_arg(args, chunk_t)); continue; + case BUILD_CERT_TYPE_EXT: + cert->certTypeExt = chunk_clone(va_arg(args, chunk_t)); + continue; case BUILD_SIGNATURE_SCHEME: cert->scheme = va_arg(args, signature_params_t*); cert->scheme = signature_params_clone(cert->scheme); diff --git a/src/pki/commands/issue.c b/src/pki/commands/issue.c index 1b66548d40..023f0536ab 100644 --- a/src/pki/commands/issue.c +++ b/src/pki/commands/issue.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Martin Willi - * Copyright (C) 2015-2019 Andreas Steffen + * Copyright (C) 2015-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -480,9 +480,12 @@ static int issue() id = cert_req->get_subject(cert_req); id = id->clone(id); } + req = (pkcs10_t*)cert_req; + + /* Add Extended Key Usage (EKU) flags */ + flags |= req->get_flags(req); /* Add subjectAltNames from PKCS#10 certificate request */ - req = (pkcs10_t*)cert_req; enumerator = req->create_subjectAltName_enumerator(req); while (enumerator->enumerate(enumerator, &subjectAltName)) { diff --git a/src/pki/commands/req.c b/src/pki/commands/req.c index 44208771c0..b2f3545e64 100644 --- a/src/pki/commands/req.c +++ b/src/pki/commands/req.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Martin Willi - * Copyright (C) 2009-2017 Andreas Steffen + * Copyright (C) 2009-2022 Andreas Steffen * * Copyright (C) secunet Security Networks AG * @@ -39,6 +39,7 @@ static int req() linked_list_t *san; chunk_t encoding = chunk_empty; chunk_t challenge_password = chunk_empty; + chunk_t cert_type_ext = chunk_empty; char *arg; bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE, lib->ns); @@ -101,6 +102,9 @@ static int req() case 'a': san->insert_last(san, identification_create_from_string(arg)); continue; + case 'P': + cert_type_ext = chunk_create(arg, strlen(arg)); + continue; case 'p': challenge_password = chunk_create(arg, strlen(arg)); continue; @@ -180,6 +184,7 @@ static int req() 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) @@ -228,9 +233,9 @@ static void __attribute__ ((constructor))reg() req, 'r', "req", "create a PKCS#10 certificate request", {"[--in file|--keyid hex] [--type rsa|ecdsa|bliss|priv] --dn distinguished-name", - "[--san subjectAltName]+ [--password challengePassword]", + "[--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]", - "[--rsa-padding pkcs1|pss]", "[--outform der|pem]"}, { {"help", 'h', 0, "show usage information"}, @@ -239,6 +244,7 @@ static void __attribute__ ((constructor))reg() {"type", 't', 1, "type of input key, default: priv"}, {"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"}, diff --git a/src/pki/commands/scep.c b/src/pki/commands/scep.c index 37f5a94828..03703e76a3 100644 --- a/src/pki/commands/scep.c +++ b/src/pki/commands/scep.c @@ -46,6 +46,7 @@ static int scep() cred_encoding_type_t form = CERT_ASN1_DER; chunk_t scep_response = chunk_empty; chunk_t challenge_password = chunk_empty; + chunk_t cert_type = chunk_empty; chunk_t serialNumber = chunk_empty; chunk_t transID = chunk_empty; chunk_t pkcs10_encoding = chunk_empty; @@ -114,6 +115,9 @@ static int scep() case 'a': san->insert_last(san, identification_create_from_string(arg)); continue; + case 'P': + cert_type = chunk_create(arg, strlen(arg)); + continue; case 'p': challenge_password = chunk_create(arg, strlen(arg)); continue; @@ -351,6 +355,7 @@ static int scep() BUILD_SUBJECT, subject, BUILD_SUBJECT_ALTNAMES, san, BUILD_CHALLENGE_PWD, challenge_password, + BUILD_CERT_TYPE_EXT, cert_type, BUILD_SIGNATURE_SCHEME, scheme, BUILD_END); if (!pkcs10) @@ -682,8 +687,9 @@ static void __attribute__ ((constructor))reg() scep, 'S', "scep", "Enroll an X.509 certificate with a SCEP server", {"--url url [--in file] --dn distinguished-name [--san subjectAltName]+", - "[--password password] --cacert-enc file --cacert-sig file [--cacert file]+", - "[--oldcert file --oldkey file] [--cipher aes|des3]", + "[--profile profile] [--password password]", + " --cacert-enc file --cacert-sig file [--cacert file]+", + " --oldcert file --oldkey file] [--cipher aes|des3]", "[--digest sha256|sha384|sha512|sha224|sha1] [--rsa-padding pkcs1|pss]", "[--interval time] [--maxpolltime time] [--outform der|pem]"}, { @@ -692,6 +698,7 @@ static void __attribute__ ((constructor))reg() {"in", 'i', 1, "RSA private key input file, default: stdin"}, {"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"}, {"cacert-enc", 'e', 1, "CA certificate for encryption"}, {"cacert-sig", 's', 1, "CA certificate for signature verification"}, diff --git a/src/pki/man/pki---req.1.in b/src/pki/man/pki---req.1.in index 8f7de248ce..516088f3d2 100644 --- a/src/pki/man/pki---req.1.in +++ b/src/pki/man/pki---req.1.in @@ -1,4 +1,4 @@ -.TH "PKI \-\-REQ" 1 "2013-07-31" "@PACKAGE_VERSION@" "strongSwan" +.TH "PKI \-\-REQ" 1 "2022-08-11" "@PACKAGE_VERSION@" "strongSwan" . .SH "NAME" . @@ -13,6 +13,7 @@ pki \-\-req \- Create a PKCS#10 certificate request .OP \-\-type type .BI \-\-dn\~ distinguished-name .OP \-\-san subjectAltName +.OP \-\-profile profile .OP \-\-password password .OP \-\-digest digest .OP \-\-rsa\-padding padding @@ -29,7 +30,7 @@ pki \-\-req \- Create a PKCS#10 certificate request | .B \-\-help .YS -. +.q .SH "DESCRIPTION" . This sub-command of @@ -65,6 +66,15 @@ Subject distinguished name (DN). Required. .BI "\-a, \-\-san " subjectAltName subjectAltName extension to include in request. Can be used multiple times. .TP +.BI "\-P, \-\-profile " profile +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 +.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. +.TP .BI "\-p, \-\-password " password The challengePassword to include in the certificate request. .TP @@ -83,11 +93,12 @@ Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or . .SH "EXAMPLES" . -Generate a certificate request for an RSA key, with a subjectAltName extension: +Generate a certificate request for an RSA key, with a subjectAltName extension +and a TLS-server profile: .PP .EX pki \-\-req \-\-in key.der \-\-dn "C=CH, O=strongSwan, CN=moon" \\ - \-\-san moon@strongswan.org > req.der + \-\-san moon@strongswan.org \-\-profile server > req.der .EE .PP Generate a certificate request for an ECDSA key and a different digest: diff --git a/src/pki/man/pki---scep.1.in b/src/pki/man/pki---scep.1.in index 2422b54ca7..8817cffc11 100644 --- a/src/pki/man/pki---scep.1.in +++ b/src/pki/man/pki---scep.1.in @@ -11,6 +11,7 @@ pki \-\-scep \- Enroll an X.509 certificate with a SCEP server .OP \-\-in file .BI \-\-dn\~ distinguished-name .OP \-\-san subjectAltName +.OP \-\-profile profile .OP \-\-password password .BI \-\-ca-cert-enc\~ file .BI \-\-ca-cert-sig\~ file @@ -74,6 +75,14 @@ Subject distinguished name (DN). Required. .BI "\-a, \-\-san " subjectAltName subjectAltName extension to include in request. Can be used multiple times. .TP +.BI "\-P, \-\-profile " profile +Certificate profile name to be included in the certificate request. Can be any +UTF8 string. Supported e.g. by the +.B openxpki +SCEP server with profiles (\fIpc-client\fR, \fItls-server\fR, etc.) 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