Merge branch 'pki-scep'

This commit is contained in:
Andreas Steffen
2022-08-26 12:19:17 +02:00
45 changed files with 3886 additions and 2264 deletions
+11 -1
View File
@@ -1,2 +1,12 @@
pki.load =
Plugins to load in ipsec pki tool.
Plugins to load in the pki tool.
pki.scep.http_bind
Source IP address to bind for HTTP operations.
pki.scep.http_timeout = 30s
Timeout for HTTP operations.
pki.scep.renewal_via_pkcs_req = no
Some SCEP servers (e.g. openxpki) are incorrectly doing certificate renewal
via messageType PKCSReq (19) instead of RenewalReq (17).
+38 -39
View File
@@ -1,6 +1,6 @@
#
# Copyright (C) 2007-2017 Tobias Brunner
# Copyright (C) 2006-2019 Andreas Steffen
# Copyright (C) 2006-2022 Andreas Steffen
# Copyright (C) 2006-2014 Martin Willi
#
# Copyright (C) secunet Security Networks AG
@@ -144,7 +144,7 @@ ARG_ENABL_SET([ctr], [enables the Counter Mode wrapper crypto plugin.
ARG_DISBL_SET([des], [disable DES/3DES software implementation plugin.])
ARG_DISBL_SET([drbg], [disable the NIST Deterministic Random Bit Generator plugin.])
ARG_DISBL_SET([fips-prf], [disable FIPS PRF software implementation plugin.])
ARG_ENABL_SET([gcm], [enables the GCM AEAD wrapper crypto plugin.])
ARG_DISBL_SET([gcm], [disable the GCM AEAD wrapper crypto plugin.])
ARG_ENABL_SET([gcrypt], [enables the libgcrypt plugin.])
ARG_DISBL_SET([gmp], [disable GNU MP (libgmp) based crypto implementation plugin.])
ARG_DISBL_SET([curve25519], [disable Curve25519 Diffie-Hellman plugin.])
@@ -302,7 +302,6 @@ ARG_ENABL_SET([medcli], [enable mediation client configuration database
ARG_ENABL_SET([medsrv], [enable mediation server web frontend and daemon plugin.])
ARG_ENABL_SET([nm], [enable NetworkManager backend.])
ARG_DISBL_SET([pki], [disable pki certificate utility.])
ARG_DISBL_SET([scepclient], [disable SCEP client tool.])
ARG_DISBL_SET([scripts], [disable additional utilities (found in directory scripts).])
ARG_ENABL_SET([svc], [enable charon Windows service.])
ARG_ENABL_SET([systemd], [enable systemd specific IKE daemon charon-systemd.])
@@ -453,7 +452,7 @@ if test x$tnc_imc = xtrue -o x$tnc_imv = xtrue -o x$tnccs_11 = xtrue -o x$tnccs_
tnc_tnccs=true;
fi
if test x$eap_tls = xtrue -o x$eap_ttls = xtrue -o x$eap_peap = xtrue -o x$tnc_tnccs = xtrue; then
if test x$eap_tls = xtrue -o x$eap_ttls = xtrue -o x$eap_peap = xtrue -o x$tnc_tnccs = xtrue -o x$pki = xtrue; then
tls=true;
fi
@@ -1483,7 +1482,6 @@ charon_plugins=
starter_plugins=
pool_plugins=
attest_plugins=
scepclient_plugins=
pki_plugins=
scripts_plugins=
fuzz_plugins=
@@ -1500,48 +1498,48 @@ s_plugins=
t_plugins=
p_plugins=
ADD_PLUGIN([test-vectors], [s charon scepclient pki])
ADD_PLUGIN([test-vectors], [s charon pki])
ADD_PLUGIN([unbound], [s charon scripts])
ADD_PLUGIN([ldap], [s charon scepclient scripts nm cmd])
ADD_PLUGIN([ldap], [s charon scripts nm cmd])
ADD_PLUGIN([pkcs11], [s charon pki nm cmd])
ADD_PLUGIN([tpm], [p charon pki nm cmd])
ADD_PLUGIN([aesni], [s charon scepclient pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([aes], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([des], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([blowfish], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([rc2], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([sha2], [s charon scepclient pki scripts medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([sha3], [s charon scepclient pki scripts medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([sha1], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([md4], [s charon scepclient pki nm cmd])
ADD_PLUGIN([md5], [s charon scepclient pki scripts attest nm cmd aikgen])
ADD_PLUGIN([mgf1], [s charon scepclient pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([rdrand], [s charon scepclient pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([random], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([aesni], [s charon pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([aes], [s charon pki scripts nm cmd])
ADD_PLUGIN([des], [s charon pki scripts nm cmd])
ADD_PLUGIN([blowfish], [s charon pki scripts nm cmd])
ADD_PLUGIN([rc2], [s charon pki scripts nm cmd])
ADD_PLUGIN([sha2], [s charon pki scripts medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([sha3], [s charon pki scripts medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([sha1], [s charon pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([md4], [s charon pki nm cmd])
ADD_PLUGIN([md5], [s charon pki scripts attest nm cmd aikgen])
ADD_PLUGIN([mgf1], [s charon pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([rdrand], [s charon pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([random], [s charon pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([nonce], [s charon nm cmd aikgen])
ADD_PLUGIN([x509], [s charon scepclient pki scripts attest nm cmd aikgen fuzz])
ADD_PLUGIN([x509], [s charon pki scripts attest nm cmd aikgen fuzz])
ADD_PLUGIN([revocation], [s charon pki nm cmd])
ADD_PLUGIN([constraints], [s charon nm cmd])
ADD_PLUGIN([acert], [s charon])
ADD_PLUGIN([pubkey], [s charon pki cmd aikgen])
ADD_PLUGIN([pkcs1], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([pkcs7], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd])
ADD_PLUGIN([pkcs1], [s charon pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([pkcs7], [s charon pki scripts nm cmd])
ADD_PLUGIN([pkcs12], [s charon pki scripts cmd])
ADD_PLUGIN([pgp], [s charon])
ADD_PLUGIN([dnskey], [s charon pki])
ADD_PLUGIN([sshkey], [s charon pki nm cmd])
ADD_PLUGIN([dnscert], [c charon])
ADD_PLUGIN([ipseckey], [c charon])
ADD_PLUGIN([pem], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([pem], [s charon pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([padlock], [s charon])
ADD_PLUGIN([openssl], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([wolfssl], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([gcrypt], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([botan], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([pkcs8], [s charon scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([af-alg], [s charon scepclient pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([openssl], [s charon pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([wolfssl], [s charon pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([gcrypt], [s charon pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([botan], [s charon pki scripts manager medsrv attest nm cmd aikgen])
ADD_PLUGIN([pkcs8], [s charon pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([af-alg], [s charon pki scripts medsrv attest nm cmd aikgen])
ADD_PLUGIN([fips-prf], [s charon nm cmd])
ADD_PLUGIN([gmp], [s charon scepclient pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([gmp], [s charon pki scripts manager medsrv attest nm cmd aikgen fuzz])
ADD_PLUGIN([curve25519], [s charon pki scripts nm cmd])
ADD_PLUGIN([agent], [s charon nm cmd])
ADD_PLUGIN([keychain], [s charon cmd])
@@ -1557,8 +1555,8 @@ ADD_PLUGIN([ntru], [s charon scripts nm cmd])
ADD_PLUGIN([drbg], [s charon pki scripts nm cmd])
ADD_PLUGIN([newhope], [s charon scripts nm cmd])
ADD_PLUGIN([bliss], [s charon pki scripts nm cmd])
ADD_PLUGIN([curl], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([files], [s charon scepclient pki scripts nm cmd])
ADD_PLUGIN([curl], [s charon pki scripts nm cmd])
ADD_PLUGIN([files], [s charon pki scripts nm cmd])
ADD_PLUGIN([winhttp], [s charon pki scripts])
ADD_PLUGIN([soup], [s charon pki scripts nm cmd])
ADD_PLUGIN([mysql], [s charon pool manager medsrv attest])
@@ -1838,11 +1836,10 @@ AM_CONDITIONAL(USE_ADNS, test x$adns = xtrue)
AM_CONDITIONAL(USE_CHARON, test x$charon = xtrue)
AM_CONDITIONAL(USE_NM, test x$nm = xtrue)
AM_CONDITIONAL(USE_PKI, test x$pki = xtrue)
AM_CONDITIONAL(USE_SCEPCLIENT, test x$scepclient = xtrue)
AM_CONDITIONAL(USE_SCRIPTS, test x$scripts = xtrue)
AM_CONDITIONAL(USE_FUZZING, test x$fuzzing = xtrue)
AM_CONDITIONAL(USE_CONFTEST, test x$conftest = xtrue)
AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$pki = xtrue -o x$scepclient = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$pki = xtrue -o x$conftest = xtrue -o x$fast = xtrue -o x$imcv = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$tls = xtrue -o x$tnc_tnccs = xtrue -o x$aikgen = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
AM_CONDITIONAL(USE_LIBCHARON, test x$charon = xtrue -o x$conftest = xtrue -o x$nm = xtrue -o x$tkm = xtrue -o x$cmd = xtrue -o x$svc = xtrue -o x$systemd = xtrue)
AM_CONDITIONAL(USE_LIBIPSEC, test x$libipsec = xtrue)
AM_CONDITIONAL(USE_LIBNTTFFT, test x$bliss = xtrue -o x$newhope = xtrue)
@@ -1851,7 +1848,7 @@ AM_CONDITIONAL(USE_LIBTNCCS, test x$tnc_tnccs = xtrue)
AM_CONDITIONAL(USE_LIBPTTLS, test x$tnc_tnccs = xtrue)
AM_CONDITIONAL(USE_LIBTPMTSS, test x$tss_trousers = xtrue -o x$tss_tss2 = xtrue -o x$tpm = xtrue -o x$aikgen = xtrue -o x$imcv = xtrue)
AM_CONDITIONAL(USE_FILE_CONFIG, test x$stroke = xtrue)
AM_CONDITIONAL(USE_IPSEC_SCRIPT, test x$stroke = xtrue -o x$scepclient = xtrue -o x$conftest = xtrue)
AM_CONDITIONAL(USE_IPSEC_SCRIPT, test x$stroke = xtrue -o x$conftest = xtrue)
AM_CONDITIONAL(USE_LIBCAP, test x$capabilities = xlibcap)
AM_CONDITIONAL(USE_VSTR, test x$printf_hooks = xvstr)
AM_CONDITIONAL(USE_BUILTIN_PRINTF, test x$printf_hooks = xbuiltin)
@@ -1927,7 +1924,6 @@ AM_COND_IF([USE_IMV_SWIMA], [strongswan_options=${strongswan_options}" sec-updat
AM_COND_IF([USE_LIBTNCCS], [strongswan_options=${strongswan_options}" tnc"])
AM_COND_IF([USE_MANAGER], [strongswan_options=${strongswan_options}" manager"])
AM_COND_IF([USE_MEDSRV], [strongswan_options=${strongswan_options}" medsrv"])
AM_COND_IF([USE_SCEPCLIENT], [strongswan_options=${strongswan_options}" scepclient"])
AM_COND_IF([USE_PKI], [strongswan_options=${strongswan_options}" pki"])
AM_COND_IF([USE_SWANCTL], [strongswan_options=${strongswan_options}" swanctl"])
AM_COND_IF([USE_SYSTEMD], [strongswan_options=${strongswan_options}" charon-systemd"])
@@ -2134,7 +2130,6 @@ AC_CONFIG_FILES([
src/starter/Makefile
src/starter/tests/Makefile
src/_updown/Makefile
src/scepclient/Makefile
src/aikgen/Makefile
src/tpm_extendpcr/Makefile
src/pki/Makefile
@@ -2167,6 +2162,8 @@ AC_CONFIG_FILES([
src/pki/man/pki.1
src/pki/man/pki---acert.1
src/pki/man/pki---dn.1
src/pki/man/pki---est.1
src/pki/man/pki---estca.1
src/pki/man/pki---gen.1
src/pki/man/pki---issue.1
src/pki/man/pki---keyid.1
@@ -2175,6 +2172,8 @@ AC_CONFIG_FILES([
src/pki/man/pki---print.1
src/pki/man/pki---pub.1
src/pki/man/pki---req.1
src/pki/man/pki---scep.1
src/pki/man/pki---scepca.1
src/pki/man/pki---self.1
src/pki/man/pki---signcrl.1
src/pki/man/pki---verify.1
+6 -6
View File
@@ -160,8 +160,8 @@ openssl*)
fi
;;
gcrypt)
CONFIG="--disable-defaults --enable-pki --enable-gcrypt --enable-pkcs1 --enable-pkcs8"
export TESTS_PLUGINS="test-vectors pkcs1 pkcs8 gcrypt!"
CONFIG="--disable-defaults --enable-pki --enable-gcrypt --enable-random --enable-pem --enable-pkcs1 --enable-pkcs8 --enable-gcm --enable-hmac --enable-kdf -enable-curve25519 --enable-x509 --enable-constraints"
export TESTS_PLUGINS="test-vectors random pem pkcs1 pkcs8 gcm hmac kdf curve25519 x509 constraints gcrypt!"
if [ "$ID" = "ubuntu" -a "$VERSION_ID" = "20.04" ]; then
DEPS="libgcrypt20-dev"
else
@@ -169,16 +169,16 @@ gcrypt)
fi
;;
botan)
CONFIG="--disable-defaults --enable-pki --enable-botan --enable-pem"
export TESTS_PLUGINS="test-vectors pem botan!"
CONFIG="--disable-defaults --enable-pki --enable-botan --enable-pem --enable-hmac --enable-x509 --enable-constraints"
export TESTS_PLUGINS="test-vectors pem hmac x509 constraints botan!"
DEPS=""
if test "$1" = "build-deps"; then
build_botan
fi
;;
wolfssl)
CONFIG="--disable-defaults --enable-pki --enable-wolfssl --enable-pem"
export TESTS_PLUGINS="test-vectors pem wolfssl!"
CONFIG="--disable-defaults --enable-pki --enable-wolfssl --enable-pem --enable-pkcs1 --enable-pkcs8 --enable-x509 --enable-constraints"
export TESTS_PLUGINS="test-vectors pem pkcs1 pkcs8 x509 constraints wolfssl!"
# build with custom options to enable all the features the plugin supports
DEPS=""
if test "$1" = "build-deps"; then
-4
View File
@@ -75,10 +75,6 @@ if USE_UPDOWN
SUBDIRS += _updown
endif
if USE_SCEPCLIENT
SUBDIRS += scepclient
endif
if USE_PKI
SUBDIRS += pki
endif
-4
View File
@@ -102,10 +102,6 @@ if USE_SYSTEMD
exes += $(DESTDIR)$(sbindir)/charon-systemd
endif
if USE_SCEPCLIENT
exes += $(DESTDIR)$(ipsecdir)/scepclient
endif
if USE_PKI
exes += $(DESTDIR)$(bindir)/pki
endif
+1 -1
View File
@@ -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"
+2 -1
View File
@@ -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",
+3 -1
View File
@@ -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* */
@@ -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 <collections/enumerator.h>
#include <credentials/certificates/certificate.h>
@@ -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*
*/
+79 -23
View File
@@ -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);
+1
View File
@@ -661,6 +661,7 @@ static char *whitelist[] = {
"botan_privkey_create",
"botan_privkey_load_ecdh",
"botan_privkey_load",
"botan_privkey_load_rsa_pkcs1",
"botan_kdf",
};
+8
View File
@@ -36,6 +36,14 @@ bool match(const char *pattern, const chunk_t *ch)
return ch->len == strlen(pattern) && strncmp(pattern, ch->ptr, ch->len) == 0;
}
/**
* compare string with chunk ignoring the case of the characters
*/
bool matchcase(const char *pattern, const chunk_t *ch)
{
return ch->len == strlen(pattern) && strncasecmp(pattern, ch->ptr, ch->len) == 0;
}
/**
* extracts a token ending with the first occurrence of a given termination symbol
*/
+6 -2
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2001-2008 Andreas Steffen
*
* Copyright (C) 2001-2022 Andreas Steffen
*
* Copyright (C) secunet Security Networks AG
*
@@ -35,6 +34,11 @@ bool eat_whitespace(chunk_t *src);
*/
bool match(const char *pattern, const chunk_t *ch);
/**
* Compare null-terminated pattern with chunk ignoring the case of the characters
*/
bool matchcase(const char *pattern, const chunk_t *ch);
/**
* Extracts a token ending with the first occurrence of a given termination symbol
*/
+174 -36
View File
@@ -112,9 +112,25 @@ static char rsa[] = {
};
/**
* ECDSA private key
* ECDSA256 private key
* pki --gen --type ecdsa --size 256
*/
static char ecdsa[] = {
static char ecdsa256[] = {
0x30,0x77,0x02,0x01,0x01,0x04,0x20,0x2d,0x01,0x7e,0x5b,0x4a,0x7d,0x78,0xe9,0x23,
0xeb,0xb2,0xac,0x4c,0xf1,0x28,0x3b,0xfa,0x1d,0xa9,0x08,0x5c,0xd0,0x60,0x2a,0xa6,
0x54,0xd3,0x94,0xd4,0x05,0xa1,0x04,0xa0,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,
0x03,0x01,0x07,0xa1,0x44,0x03,0x42,0x00,0x04,0x15,0x9c,0xbe,0xdb,0x54,0xa6,0xe7,
0x7f,0x76,0x05,0xa6,0x9d,0xf3,0x41,0x38,0x43,0x98,0xe9,0x0b,0x2b,0x8b,0x02,0xb4,
0x04,0x9b,0x61,0x84,0x65,0x63,0x3b,0x08,0xb2,0x4b,0x1e,0xd0,0x32,0x20,0xe9,0xfc,
0x62,0xa7,0xd0,0x71,0x9e,0xe9,0xf9,0x2d,0x91,0xb8,0xf2,0xa3,0x4d,0x8a,0x78,0xb2,
0x0b,0xfb,0x59,0x7c,0x40,0xbd,0xaf,0xa2,0x07
};
/**
* ECDSA384 private key
* pki --gen --type ecdsa --size 384
*/
static char ecdsa384[] = {
0x30,0x81,0xa4,0x02,0x01,0x01,0x04,0x30,0xc0,0x1f,0xfd,0x65,0xc6,0xc4,0x4c,0xb8,
0xff,0x56,0x08,0xb5,0xbd,0xb8,0xf5,0x93,0xf7,0x51,0x0e,0x92,0x1f,0x06,0xbf,0xa6,
0xd9,0x1d,0xae,0xa3,0x16,0x0d,0x0f,0xc9,0xd5,0x97,0x90,0x46,0xf1,0x98,0xa8,0x18,
@@ -128,6 +144,27 @@ static char ecdsa[] = {
0xb1,0x47,0xc8,0xf6,0x18,0xbb,0x97,
};
/**
* ECDSA521 private key
* pki --gen --type ecdsa --size 521
*/
static char ecdsa521[] = {
0X30,0x81,0xdc,0x02,0x01,0x01,0x04,0x42,0x01,0x88,0x0f,0x17,0x00,0x2c,0x62,0x5c,
0x3e,0xed,0xe6,0xc8,0x6a,0x12,0x8e,0x09,0x8e,0x4b,0x41,0x8f,0x1a,0xbc,0xf3,0xa4,
0xa6,0xcb,0xd4,0xa5,0x45,0x40,0xc8,0x29,0xc8,0x72,0x49,0x0a,0x04,0x9d,0xb2,0x02,
0xc7,0x6a,0x98,0x3c,0xc9,0x4d,0x87,0x30,0x8b,0x17,0xd8,0x94,0x3d,0x8b,0x88,0xc9,
0xe5,0x17,0x22,0x73,0x41,0x90,0x6d,0x52,0xee,0x11,0xa0,0x07,0x06,0x05,0x2b,0x81,
0X04,0x00,0x23,0xa1,0x81,0x89,0x03,0x81,0x86,0x00,0x04,0x01,0x9a,0x71,0x4e,0x04,
0X42,0xa7,0xdd,0x7c,0xe6,0xdb,0x0d,0x9d,0xe9,0xde,0x21,0x42,0x0b,0x56,0x90,0x7b,
0X5b,0xbc,0x33,0xdf,0x79,0x9a,0xb8,0xf0,0x79,0xad,0x78,0xe2,0x77,0xee,0x62,0x4b,
0Xc5,0x18,0xb8,0x7d,0x86,0x0a,0xb9,0xb4,0x24,0x3f,0x80,0xcf,0x34,0xfd,0x68,0xd0,
0X90,0xd0,0x66,0xe7,0x79,0x30,0x13,0xc7,0x55,0xb3,0x74,0xf7,0xd3,0x01,0x03,0x0c,
0X46,0x89,0xbf,0x7b,0xd6,0x26,0xe9,0xf6,0x50,0x35,0x7c,0x81,0x6f,0xb7,0xa5,0x62,
0Xa9,0xc9,0xba,0x45,0xd7,0xc2,0x09,0xfd,0xc5,0x0b,0x76,0x75,0xe7,0x47,0xa6,0x70,
0X09,0x16,0x14,0xc0,0x7e,0x09,0x3d,0xde,0xd4,0x79,0xa3,0xb6,0x95,0x2a,0xaa,0x5b,
0Xdc,0xd5,0xab,0xdc,0x8a,0xd9,0xf3,0x37,0x96,0xaa,0x84,0xfc,0xae,0x94,0xea
};
/**
* Ed25519 private key
* pki --gen --type ed25519
@@ -207,10 +244,41 @@ static char rsa_crt[] = {
};
/**
* TLS certificate for ECDSA key
* pki --self --in ecdsa.key --dn "C=CH, O=strongSwan, CN=tls-ecdsa" --san 127.0.0.1
* TLS certificate for ECDSA256 key
* pki --self --in ecdsa256.key --dn "C=CH, O=strongSwan, CN=tls-ecdsa" --san 127.0.0.1
*/
static char ecdsa_crt[] = {
static char ecdsa256_crt[] = {
0x30,0x82,0x01,0x74,0x30,0x82,0x01,0x1b,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x1e,
0x80,0xe3,0xbb,0xf4,0x6f,0xc5,0xab,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,
0x04,0x03,0x02,0x30,0x36,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,
0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,
0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
0x13,0x09,0x74,0x6c,0x73,0x2d,0x65,0x63,0x64,0x73,0x61,0x30,0x1e,0x17,0x0d,0x32,
0x32,0x30,0x38,0x32,0x33,0x30,0x39,0x31,0x33,0x35,0x34,0x5a,0x17,0x0d,0x32,0x35,
0x30,0x38,0x32,0x32,0x30,0x39,0x31,0x33,0x35,0x34,0x5a,0x30,0x36,0x31,0x0b,0x30,
0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,
0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,
0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,0x13,0x09,0x74,0x6c,0x73,0x2d,0x65,0x63,
0x64,0x73,0x61,0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,
0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0x15,0x9c,
0xbe,0xdb,0x54,0xa6,0xe7,0x7f,0x76,0x05,0xa6,0x9d,0xf3,0x41,0x38,0x43,0x98,0xe9,
0x0b,0x2b,0x8b,0x02,0xb4,0x04,0x9b,0x61,0x84,0x65,0x63,0x3b,0x08,0xb2,0x4b,0x1e,
0xd0,0x32,0x20,0xe9,0xfc,0x62,0xa7,0xd0,0x71,0x9e,0xe9,0xf9,0x2d,0x91,0xb8,0xf2,
0xa3,0x4d,0x8a,0x78,0xb2,0x0b,0xfb,0x59,0x7c,0x40,0xbd,0xaf,0xa2,0x07,0xa3,0x13,
0x30,0x11,0x30,0x0f,0x06,0x03,0x55,0x1d,0x11,0x04,0x08,0x30,0x06,0x87,0x04,0x7f,
0x00,0x00,0x01,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,
0x47,0x00,0x30,0x44,0x02,0x20,0x3d,0xa0,0x7e,0xff,0xfe,0x38,0xa4,0xfc,0x28,0x7b,
0x6a,0x63,0xea,0xb9,0x04,0x11,0x63,0x98,0x25,0x1f,0x7f,0xc6,0xbc,0xe7,0x2e,0x53,
0xbf,0x4a,0x7c,0x73,0xe9,0xe1,0x02,0x20,0x28,0xec,0x8b,0x84,0xa5,0xa3,0xd1,0xac,
0x92,0x0b,0x9d,0xdc,0xa5,0x59,0xe8,0x64,0xb9,0xd1,0x66,0xe9,0x23,0xca,0x3b,0xee,
0xc8,0x0e,0x08,0x4e,0x8f,0xc7,0xed,0x11
};
/**
* TLS certificate for ECDSA384 key
* pki --self --in ecdsa384.key --dn "C=CH, O=strongSwan, CN=tls-ecdsa" --san 127.0.0.1
*/
static char ecdsa384_crt[] = {
0x30,0x82,0x01,0xb1,0x30,0x82,0x01,0x38,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x77,
0x8f,0x61,0x26,0xa2,0xae,0xe8,0x6c,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,
0x04,0x03,0x03,0x30,0x36,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,
@@ -241,6 +309,46 @@ static char ecdsa_crt[] = {
0xac,0x36,0x08,0x14,0x29,
};
/**
* TLS certificate for ECDSA521 key
* pki --self --in ecdsa521.key --dn "C=CH, O=strongSwan, CN=tls-ecdsa" --san 127.0.0.1
*/
static char ecdsa521_crt[] = {
0x30,0x82,0x01,0xfd,0x30,0x82,0x01,0x5e,0xa0,0x03,0x02,0x01,0x02,0x02,0x08,0x6c,
0x72,0xcb,0x98,0xc7,0x4c,0x46,0xf7,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,
0x04,0x03,0x04,0x30,0x36,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,
0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,
0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,
0x13,0x09,0x74,0x6c,0x73,0x2d,0x65,0x63,0x64,0x73,0x61,0x30,0x1e,0x17,0x0d,0x32,
0x32,0x30,0x38,0x32,0x34,0x31,0x32,0x35,0x33,0x31,0x36,0x5a,0x17,0x0d,0x32,0x35,
0x30,0x38,0x32,0x33,0x31,0x32,0x35,0x33,0x31,0x36,0x5a,0x30,0x36,0x31,0x0b,0x30,
0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,
0x55,0x04,0x0a,0x13,0x0a,0x73,0x74,0x72,0x6f,0x6e,0x67,0x53,0x77,0x61,0x6e,0x31,
0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x03,0x13,0x09,0x74,0x6c,0x73,0x2d,0x65,0x63,
0x64,0x73,0x61,0x30,0x81,0x9b,0x30,0x10,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,
0x01,0x06,0x05,0x2b,0x81,0x04,0x00,0x23,0x03,0x81,0x86,0x00,0x04,0x01,0x9a,0x71,
0x4e,0x04,0x42,0xa7,0xdd,0x7c,0xe6,0xdb,0x0d,0x9d,0xe9,0xde,0x21,0x42,0x0b,0x56,
0x90,0x7b,0x5b,0xbc,0x33,0xdf,0x79,0x9a,0xb8,0xf0,0x79,0xad,0x78,0xe2,0x77,0xee,
0x62,0x4b,0xc5,0x18,0xb8,0x7d,0x86,0x0a,0xb9,0xb4,0x24,0x3f,0x80,0xcf,0x34,0xfd,
0x68,0xd0,0x90,0xd0,0x66,0xe7,0x79,0x30,0x13,0xc7,0x55,0xb3,0x74,0xf7,0xd3,0x01,
0x03,0x0c,0x46,0x89,0xbf,0x7b,0xd6,0x26,0xe9,0xf6,0x50,0x35,0x7c,0x81,0x6f,0xb7,
0xa5,0x62,0xa9,0xc9,0xba,0x45,0xd7,0xc2,0x09,0xfd,0xc5,0x0b,0x76,0x75,0xe7,0x47,
0xa6,0x70,0x09,0x16,0x14,0xc0,0x7e,0x09,0x3d,0xde,0xd4,0x79,0xa3,0xb6,0x95,0x2a,
0xaa,0x5b,0xdc,0xd5,0xab,0xdc,0x8a,0xd9,0xf3,0x37,0x96,0xaa,0x84,0xfc,0xae,0x94,
0xea,0xa3,0x13,0x30,0x11,0x30,0x0f,0x06,0x03,0x55,0x1d,0x11,0x04,0x08,0x30,0x06,
0x87,0x04,0x7f,0x00,0x00,0x01,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,
0x03,0x04,0x03,0x81,0x8c,0x00,0x30,0x81,0x88,0x02,0x42,0x01,0x1f,0x37,0x05,0xa6,
0x91,0x84,0x36,0x0f,0x63,0xf1,0x42,0x84,0xc2,0xfc,0xd2,0x4d,0x1e,0x7a,0xfe,0xe9,
0x22,0xc7,0xcf,0x12,0x37,0xdd,0xe7,0xc1,0xce,0xb7,0x92,0x5b,0x15,0xea,0xe5,0x81,
0x25,0x48,0x29,0x22,0xe2,0xe3,0x3f,0xbb,0xa7,0x3d,0xac,0xa7,0x29,0x0e,0xa6,0xcb,
0xf9,0x6a,0xa8,0x3a,0x33,0x2b,0xbd,0xaa,0x7b,0x81,0x7d,0x87,0x29,0x02,0x42,0x00,
0xcc,0x80,0xb7,0x7c,0xf3,0x04,0x1f,0x0c,0x6f,0xef,0xb3,0x4c,0x7b,0x2d,0x54,0x1f,
0x3d,0xb4,0xdd,0x6f,0x7c,0x2a,0xdb,0xfa,0x3e,0x47,0xa9,0x3a,0xe1,0x68,0x96,0xff,
0xc3,0x42,0xa1,0xd1,0xc3,0xe4,0x03,0xa7,0x33,0x82,0xb2,0x76,0x12,0xeb,0xaa,0xed,
0x00,0x3f,0x1f,0x4a,0xd5,0x1c,0x63,0x50,0xd0,0xae,0xa5,0x58,0xc2,0x16,0x56,0xcd,
0x9b
};
/**
* TLS certificate for Ed25519 key
* pki --self --in ed25519.key --dn "C=CH, O=strongSwan, CN=tls-ed25519" \
@@ -313,24 +421,13 @@ static void setup_credentials(chunk_t key_data, chunk_t cert_data)
lib->credmgr->add_set(lib->credmgr, &creds->set);
}
key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
BUILD_BLOB, chunk_from_thing(rsa), BUILD_END);
if (key)
{
creds->add_key(creds, key);
}
key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
BUILD_BLOB, key_data, BUILD_END);
if (key)
{
creds->add_key(creds, key);
}
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB, chunk_from_thing(rsa_crt), BUILD_END);
if (cert)
{
creds->add_cert(creds, TRUE, cert);
}
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB, cert_data, BUILD_END);
if (cert)
@@ -339,27 +436,31 @@ static void setup_credentials(chunk_t key_data, chunk_t cert_data)
}
}
START_SETUP(setup_creds)
START_SETUP(setup_rsa_creds)
{
setup_credentials(chunk_from_thing(ecdsa), chunk_from_thing(ecdsa_crt));
setup_credentials(chunk_from_thing(rsa), chunk_from_thing(rsa_crt));
}
END_SETUP
START_SETUP(setup_ed25519_creds)
{
setup_credentials(chunk_from_thing(rsa), chunk_from_thing(rsa_crt));
setup_credentials(chunk_from_thing(ed25519), chunk_from_thing(ed25519_crt));
}
END_SETUP
START_SETUP(setup_ed448_creds)
{
setup_credentials(chunk_from_thing(rsa), chunk_from_thing(rsa_crt));
setup_credentials(chunk_from_thing(ed448), chunk_from_thing(ed448_crt));
}
END_SETUP
START_SETUP(setup_all_creds)
{
setup_credentials(chunk_from_thing(ecdsa), chunk_from_thing(ecdsa_crt));
setup_credentials(chunk_from_thing(rsa), chunk_from_thing(rsa_crt));
setup_credentials(chunk_from_thing(ecdsa256), chunk_from_thing(ecdsa256_crt));
setup_credentials(chunk_from_thing(ed25519), chunk_from_thing(ed25519_crt));
setup_credentials(chunk_from_thing(ed448), chunk_from_thing(ed448_crt));
}
@@ -606,20 +707,57 @@ static void test_tls_ke_groups(tls_version_t version, uint16_t port, bool cauth,
static void test_tls_signature_schemes(tls_version_t version, uint16_t port,
bool cauth, u_int i)
{
chunk_t key_data = chunk_empty, cert_data = chunk_empty;
tls_signature_scheme_t *schemes;
char signature[128];
int count;
/* config used for both TLS server and client */
server_config = create_config(version, port, cauth);
/* start TLS server */
start_echo_server(server_config);
/* configure signature scheme */
count = tls_crypto_get_supported_signatures(version, &schemes);
ck_assert(i < count);
snprintf(signature, sizeof(signature), "%N", tls_signature_scheme_names,
schemes[i]);
lib->settings->set_str(lib->settings, "%s.tls.signature", signature, lib->ns);
/* depending on the signature scheme load a second set of credentials */
switch (schemes[i])
{
case TLS_SIG_ECDSA_SHA256:
case TLS_SIG_ECDSA_SHA1:
key_data = chunk_from_thing(ecdsa256);
cert_data = chunk_from_thing(ecdsa256_crt);
break;
case TLS_SIG_ECDSA_SHA384:
key_data = chunk_from_thing(ecdsa384);
cert_data = chunk_from_thing(ecdsa384_crt);
break;
case TLS_SIG_ECDSA_SHA512:
key_data = chunk_from_thing(ecdsa521);
cert_data = chunk_from_thing(ecdsa521_crt);
break;
case TLS_SIG_ED25519:
key_data = chunk_from_thing(ed25519);
cert_data = chunk_from_thing(ed25519_crt);
break;
case TLS_SIG_ED448:
key_data = chunk_from_thing(ed448);
cert_data = chunk_from_thing(ed448_crt);
break;
default:
break;
}
if (key_data.len > 0 || cert_data.len > 0)
{
setup_credentials(key_data, cert_data);
}
/* run TLS client */
run_echo_client(server_config);
free(schemes);
@@ -778,87 +916,87 @@ Suite *socket_suite_create()
s = suite_create("socket");
tc = tcase_create("TLS [1.0..1.3] client to TLS 1.3 server");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_versions_test(test_tls_13_server, TLS_1_0, TLS_1_3);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.3 client to TLS [1.0..1.3] server");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_versions_test(test_tls_13_client, TLS_1_0, TLS_1_3);
suite_add_tcase(s, tc);
tc = tcase_create("TLS [1.0..1.3] client to TLS 1.2 server");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_versions_test(test_tls_12_server, TLS_1_0, TLS_1_3);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.3/key exchange groups");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
tcase_add_loop_test(tc, test_tls13_ke_groups, 0,
tls_crypto_get_supported_groups(NULL));
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.3/signature schemes");
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_rsa_creds, teardown_creds);
tcase_add_loop_test(tc, test_tls13_signature_schemes, 0,
tls_crypto_get_supported_signatures(TLS_1_3, NULL));
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.2/signature schemes");
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_rsa_creds, teardown_creds);
tcase_add_loop_test(tc, test_tls12_signature_schemes, 0,
tls_crypto_get_supported_signatures(TLS_1_2, NULL));
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.1/signature schemes");
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_rsa_creds, teardown_creds);
tcase_add_loop_test(tc, test_tls11_signature_schemes, 0,
tls_crypto_get_supported_signatures(TLS_1_1, NULL));
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.0/signature schemes");
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_rsa_creds, teardown_creds);
tcase_add_loop_test(tc, test_tls10_signature_schemes, 0,
tls_crypto_get_supported_signatures(TLS_1_0, NULL));
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.3/anon");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls13, TLS_1_3);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.3/mutl");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls13_mutual, TLS_1_3);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.2/anon");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls12, TLS_1_2);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.2/mutl");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls12_mutual, TLS_1_2);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.1/anon");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls11, TLS_1_1);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.1/mutl");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls11_mutual, TLS_1_1);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.0/anon");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls10, TLS_1_0);
suite_add_tcase(s, tc);
tc = tcase_create("TLS 1.0/mutl");
tcase_add_checked_fixture(tc, setup_creds, teardown_creds);
tcase_add_checked_fixture(tc, setup_all_creds, teardown_creds);
add_tls_test(test_tls10_mutual, TLS_1_0);
suite_add_tcase(s, tc);
+56 -9
View File
@@ -1789,11 +1789,12 @@ METHOD(tls_crypto_t, sign, bool,
const chunk_t hashsig_def = chunk_from_chars(0x02, 0x01, 0x02, 0x03);
signature_params_t *params;
key_type_t type;
uint16_t scheme;
uint16_t scheme = 0, hashsig_scheme;
bio_reader_t *reader;
chunk_t sig;
bool done = FALSE;
if (this->tls->get_version_max(this->tls) >= TLS_1_3)
{
chunk_t transcript_hash;
@@ -1817,19 +1818,65 @@ METHOD(tls_crypto_t, sign, bool,
{ /* fallback if none given */
hashsig = hashsig_def;
}
/* Determine TLS signature scheme if unique */
type = key->get_type(key);
switch (type)
{
case KEY_ED448:
scheme = TLS_SIG_ED448;
break;
case KEY_ED25519:
scheme = TLS_SIG_ED25519;
break;
case KEY_ECDSA:
switch (key->get_keysize(key))
{
case 256:
scheme = TLS_SIG_ECDSA_SHA256;
break;
case 384:
scheme = TLS_SIG_ECDSA_SHA384;
break;
case 521:
scheme = TLS_SIG_ECDSA_SHA512;
break;
default:
DBG1(DBG_TLS, "%d bit ECDSA private key size not supported",
key->get_keysize(key));
return FALSE;
}
break;
case KEY_RSA:
/* Several TLS signature schemes possible, select later on */
break;
default:
DBG1(DBG_TLS, "%N private key type not supported",
key_type_names, type);
return FALSE;
}
reader = bio_reader_create(hashsig);
while (reader->remaining(reader) >= 2)
{
if (reader->read_uint16(reader, &scheme))
if (reader->read_uint16(reader, &hashsig_scheme))
{
params = params_for_scheme(scheme, TRUE);
if (params &&
type == key_type_from_signature_scheme(params->scheme) &&
key->sign(key, params->scheme, params->params, data, &sig))
params = params_for_scheme(hashsig_scheme, TRUE);
/**
* All key types except RSA have a single fixed signature scheme
* RSA signature schemes are tried until sign() is successful
*/
if (params && (scheme == hashsig_scheme ||
(!scheme &&
type == key_type_from_signature_scheme(params->scheme))))
{
done = TRUE;
break;
if (key->sign(key, params->scheme, params->params, data, &sig))
{
done = TRUE;
scheme = hashsig_scheme;
break;
}
}
}
}
@@ -1839,7 +1886,7 @@ METHOD(tls_crypto_t, sign, bool,
DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported");
return FALSE;
}
DBG2(DBG_TLS, "created signature with %N", tls_signature_scheme_names,
DBG1(DBG_TLS, "created signature with %N", tls_signature_scheme_names,
scheme);
writer->write_uint16(writer, scheme);
writer->write_data16(writer, sig);
+44 -28
View File
@@ -90,6 +90,11 @@ struct private_tls_peer_t {
*/
peer_state_t state;
/**
* Received a certificate request from server
*/
bool certreq_received;
/**
* TLS version we offered in hello
*/
@@ -933,7 +938,6 @@ static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader)
{
/* certificate request context as described in RFC 8446, section 4.3.2 */
reader->read_data8(reader, &context);
reader->read_data16(reader, &ext);
extensions = bio_reader_create(ext);
while (extensions->remaining(extensions))
@@ -982,6 +986,7 @@ static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader)
}
extensions->destroy(extensions);
}
this->certreq_received = TRUE;
this->state = STATE_CERTREQ_RECEIVED;
return NEED_MORE;
}
@@ -1478,35 +1483,39 @@ static status_t send_certificate(private_tls_peer_t *this,
version_min = this->tls->get_version_min(this->tls);
version_max = this->tls->get_version_max(this->tls);
if (!this->hashsig.len)
if (this->peer)
{
convert_cert_types(this);
}
enumerator = tls_create_private_key_enumerator(version_min, version_max,
this->hashsig, this->peer);
if (!enumerator || !enumerator->enumerate(enumerator, &key, &auth))
{
if (!enumerator)
if (!this->hashsig.len)
{
DBG1(DBG_TLS, "no common signature algorithms found");
convert_cert_types(this);
}
enumerator = tls_create_private_key_enumerator(version_min, version_max,
this->hashsig, this->peer);
if (!enumerator || !enumerator->enumerate(enumerator, &key, &auth))
{
if (!enumerator)
{
DBG1(DBG_TLS, "no common signature algorithms found");
}
else
{
DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'",
this->peer);
}
this->peer->destroy(this->peer);
this->peer = NULL;
}
else
{
DBG1(DBG_TLS, "no usable TLS client certificate found for '%Y'",
this->peer);
this->private = key->get_ref(key);
this->peer_auth->merge(this->peer_auth, auth, FALSE);
}
this->peer->destroy(this->peer);
this->peer = NULL;
DESTROY_IF(enumerator);
}
else
{
this->private = key->get_ref(key);
this->peer_auth->merge(this->peer_auth, auth, FALSE);
}
DESTROY_IF(enumerator);
/* certificate request context as described in RFC 8446, section 4.4.2 */
if (this->tls->get_version_max(this->tls) > TLS_1_2)
if (version_max > TLS_1_2)
{
writer->write_uint8(writer, 0);
}
@@ -1522,11 +1531,12 @@ static status_t send_certificate(private_tls_peer_t *this,
cert->get_subject(cert));
certs->write_data24(certs, data);
free(data.ptr);
}
/* extensions see RFC 8446, section 4.4.2 */
if (this->tls->get_version_max(this->tls) > TLS_1_2)
{
certs->write_uint16(certs, 0);
/* extensions see RFC 8446, section 4.4.2 */
if (version_max > TLS_1_2)
{
certs->write_uint16(certs, 0);
}
}
}
enumerator = this->peer_auth->create_enumerator(this->peer_auth);
@@ -1540,6 +1550,12 @@ static status_t send_certificate(private_tls_peer_t *this,
cert->get_subject(cert));
certs->write_data24(certs, data);
free(data.ptr);
/* extensions see RFC 8446, section 4.4.2 */
if (version_max > TLS_1_2)
{
certs->write_uint16(certs, 0);
}
}
}
}
@@ -1767,7 +1783,7 @@ METHOD(tls_handshake_t, build, status_t,
case STATE_INIT:
return send_client_hello(this, type, writer);
case STATE_HELLO_DONE:
if (this->peer)
if (this->peer || this->certreq_received)
{
return send_certificate(this, type, writer);
}
@@ -1804,7 +1820,7 @@ METHOD(tls_handshake_t, build, status_t,
return NEED_MORE;
}
this->crypto->change_cipher(this->crypto, TRUE);
if (this->peer)
if (this->peer || this->certreq_received)
{
return send_certificate(this, type, writer);
}
+8 -1
View File
@@ -176,14 +176,21 @@ public_key_t *tls_find_public_key(auth_cfg_t *peer_auth, identification_t *id)
{
public_key_t *public = NULL, *current;
certificate_t *cert, *found;
key_type_t key_type = KEY_ANY;
enumerator_t *enumerator;
auth_cfg_t *auth;
cert = peer_auth->get(peer_auth, AUTH_HELPER_SUBJECT_CERT);
if (cert)
{
public = cert->get_public_key(cert);
if (public)
{
key_type = public->get_type(public);
public->destroy(public);
}
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
KEY_ANY, id, peer_auth, TRUE);
key_type, id, peer_auth, TRUE);
while (enumerator->enumerate(enumerator, &current, &auth))
{
found = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
+10 -2
View File
@@ -2,9 +2,11 @@ SUBDIRS = man
bin_PROGRAMS = pki
pki_SOURCES = pki.c pki.h command.c command.h \
pki_SOURCES = pki.c pki.h pki_cert.c pki_cert.h command.c command.h \
commands/acert.c \
commands/dn.c \
commands/est.c \
commands/estca.c \
commands/gen.c \
commands/issue.c \
commands/keyid.c \
@@ -13,16 +15,22 @@ pki_SOURCES = pki.c pki.h command.c command.h \
commands/print.c \
commands/pub.c \
commands/req.c \
commands/scep.c \
commands/scepca.c \
commands/self.c \
commands/signcrl.c \
commands/verify.c
commands/verify.c \
est/est_tls.h est/est_tls.c \
scep/scep.h scep/scep.c
pki_LDADD = \
$(top_builddir)/src/libstrongswan/libstrongswan.la \
$(top_builddir)/src/libtls/libtls.la \
$(PTHREADLIB) $(ATOMICLIB) $(DLLIB)
pki.o : $(top_builddir)/config.status
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libtls \
-DPLUGINS=\""${pki_plugins}\""
+1 -1
View File
@@ -25,7 +25,7 @@
/**
* Maximum number of commands (+1).
*/
#define MAX_COMMANDS 14
#define MAX_COMMANDS 18
/**
* Maximum number of options in a command (+3)
+375
View File
@@ -0,0 +1,375 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <errno.h>
#include <unistd.h>
#include "pki.h"
#include "pki_cert.h"
#include "est/est_tls.h"
#include <credentials/certificates/certificate.h>
#include <credentials/sets/mem_cred.h>
/* default polling time interval in EST manual mode */
#define DEFAULT_POLL_INTERVAL 60 /* seconds */
/**
* Enroll an X.509 certificate with an EST server (RFC 7030)
*/
static int est()
{
char *arg, *url = NULL, *file = NULL, *error = NULL;
char *client_cert_file = NULL, *client_key_file = NULL;
char *keyid = NULL, *certid = NULL, *user_pass = NULL;
cred_encoding_type_t form = CERT_ASN1_DER;
chunk_t pkcs10_encoding = chunk_empty, est_response = chunk_empty;
certificate_t *pkcs10 = NULL, *client_cert = NULL, *cacert = NULL;
mem_cred_t *creds = NULL, *client_creds = NULL;
private_key_t *client_key = NULL;
est_op_t est_op = EST_SIMPLE_ENROLL;
est_tls_t *est_tls;
u_int poll_interval = DEFAULT_POLL_INTERVAL;
u_int max_poll_time = 0, poll_start = 0;
u_int http_code = 0, retry_after = 0;
int status = 1;
/* initialize CA certificate storage */
creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &creds->set);
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h': /* --help */
goto usage;
case 'u': /* --url */
url = arg;
continue;
case 'i': /* --in */
file = arg;
continue;
case 'C': /* --cacert */
cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509, BUILD_FROM_FILE, arg, BUILD_END);
if (!cacert)
{
DBG1(DBG_APP, "could not load cacert file '%s'", arg);
goto err;
}
creds->add_cert(creds, TRUE, cacert);
continue;
case 'c': /* --cert */
client_cert_file = arg;
continue;
case 'X': /* --certid */
certid = arg;
continue;
case 'k': /* --key */
client_key_file = arg;
continue;
case 'x': /* --keyid */
keyid = arg;
continue;
case 'p': /* --userpass */
user_pass = arg;
continue;
case 't': /* --pollinterval */
poll_interval = atoi(arg);
if (poll_interval <= 0)
{
error = "invalid interval specified";
goto usage;
}
continue;
case 'm': /* --maxpolltime */
max_poll_time = atoi(arg);
continue;
case 'f': /* --force */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
error = "invalid certificate output format";
goto usage;
}
continue;
case EOF:
break;
default:
error = "invalid --est option";
goto usage;
}
break;
}
if (!url)
{
error = "--url is required";
goto usage;
}
if ((client_cert_file || certid) && !(client_key_file || keyid))
{
error = "--key or --keyid is required if --cert or --certid is set";
goto usage;
}
if (!(client_cert_file || certid) && (client_key_file || keyid))
{
error = "--cert or --certid is required if --key or --keyid is set";
goto usage;
}
if (client_key_file && keyid)
{
error = "only one of --key or --keyid can be set";
goto usage;
}
if (client_cert_file && certid)
{
error = "only one of --cert or --certid can be set";
goto usage;
}
/* load PKCS#10 certificate request from file or stdin */
if (file)
{
pkcs10 = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_PKCS10_REQUEST,
BUILD_FROM_FILE, file, BUILD_END);
}
else
{
chunk_t chunk;
set_file_mode(stdin, CERT_ASN1_DER);
if (!chunk_from_fd(0, &chunk))
{
DBG1(DBG_APP, "reading PKCS#10 certificate request failed: %s\n",
strerror(errno));
goto err;
}
pkcs10 = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_PKCS10_REQUEST,
BUILD_BLOB, chunk, BUILD_END);
free(chunk.ptr);
}
if (!pkcs10)
{
DBG1(DBG_APP, "parsing certificate request failed");
goto err;
}
/* generate PKCS#10 encoding */
if (!pkcs10->get_encoding(pkcs10, CERT_ASN1_DER, &pkcs10_encoding))
{
DBG1(DBG_APP, "encoding certificate request failed");
pkcs10->destroy(pkcs10);
goto err;
}
pkcs10->destroy(pkcs10);
/* create a separate set for the old client credentials */
client_creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &client_creds->set);
/* re-enrollment with existing client certificate */
if (client_cert_file || certid)
{
chunk_t handle;
if (client_cert_file) /* load old certificate file */
{
client_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509,
BUILD_FROM_FILE, client_cert_file,
BUILD_END);
if (!client_cert)
{
DBG1(DBG_APP, "loading client cert '%s' failed",
client_cert_file);
goto end;
}
}
else /* attach old certificate object */
{
handle = chunk_from_hex(chunk_create(certid, strlen(certid)), NULL);
client_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509,
BUILD_PKCS11_KEYID, handle,
BUILD_END);
chunk_free(&handle);
if (!client_cert)
{
DBG1(DBG_APP, "attaching to certificate handle %s failed",
certid);
goto end;
}
}
client_creds->add_cert(client_creds, FALSE, client_cert);
if (client_key_file) /* load old client private key file */
{
client_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
KEY_ANY,
BUILD_FROM_FILE, client_key_file,
BUILD_END);
if (!client_key)
{
DBG1(DBG_APP, "loading client private key '%s' failed",
client_key_file);
goto end;
}
}
else /* attach old client private key object */
{
handle = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
client_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
KEY_ANY,
BUILD_PKCS11_KEYID, handle,
BUILD_END);
chunk_free(&handle);
if (!client_key)
{
DBG1(DBG_APP, "attaching to private key handle %s failed",
keyid);
goto end;
}
}
client_creds->add_key(client_creds, client_key);
est_op = EST_SIMPLE_REENROLL;
}
est_tls = est_tls_create(url, client_cert, user_pass);
if (!est_tls)
{
DBG1(DBG_APP, "TLS connection to EST server was not established");
goto end;
}
if (!est_tls->request(est_tls, est_op, pkcs10_encoding, &est_response,
&http_code, &retry_after))
{
est_tls->destroy(est_tls);
DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
goto end;
}
/* in case of manual mode, we are going into a polling loop */
if (http_code == EST_HTTP_CODE_ACCEPTED)
{
if (retry_after > 0 && poll_interval < retry_after)
{
poll_interval = retry_after;
}
if (max_poll_time > 0)
{
DBG1(DBG_APP, " EST request pending, polling every %d seconds"
" up to %d seconds", poll_interval, max_poll_time);
}
else
{
DBG1(DBG_APP, " EST request pending, polling indefinitely"
" every %d seconds", poll_interval);
}
poll_start = time_monotonic(NULL);
}
while (http_code == EST_HTTP_CODE_ACCEPTED)
{
chunk_free(&est_response);
est_tls->destroy(est_tls);
if (max_poll_time > 0 &&
(time_monotonic(NULL) - poll_start) >= max_poll_time)
{
DBG1(DBG_APP, "maximum poll time reached: %d seconds", max_poll_time);
goto end;
}
DBG1(DBG_APP, " going to sleep for %d seconds", poll_interval);
sleep(poll_interval);
est_tls = est_tls_create(url, client_cert, user_pass);
if (!est_tls)
{
DBG1(DBG_APP, "TLS connection to EST server was not established");
goto end;
}
if (!est_tls->request(est_tls, est_op, pkcs10_encoding, &est_response,
&http_code, &retry_after))
{
DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
est_tls->destroy(est_tls);
goto end;
}
}
est_tls->destroy(est_tls);
end:
/* remove the old client certificate before extracting the new one */
lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
client_creds->destroy(client_creds);
if (http_code == EST_HTTP_CODE_OK)
{
status = pki_cert_extract_cert(est_response, form) ? 0 : 1;
}
err:
/* cleanup */
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
chunk_free(&pkcs10_encoding);
chunk_free(&est_response);
return status;
usage:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
return command_usage(error);
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
est, 'E', "est",
"Enroll an X.509 certificate with an EST server",
{"--url url [--in file] [--cacert file]+ [-userpass username:password]",
"[--cert file|--certid hex --key file|--keyid hex] [--interval time]",
"[--maxpolltime time] [--outform der|pem]"},
{
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the EST server"},
{"in", 'i', 1, "PKCS#10 input file, default: stdin"},
{"cacert", 'C', 1, "CA certificate"},
{"cert", 'c', 1, "old certificate about to be renewed"},
{"certid", 'X', 1, "smartcard or TPM certificate object handle" },
{"key", 'k', 1, "old private key about to be replaced"},
{"keyid", 'x', 1, "smartcard or TPM private key object handle"},
{"userpass", 'p', 1, "username:password for http basic auth"},
{"interval", 't', 1, "poll interval, default: 60s"},
{"maxpolltime", 'm', 1, "maximum poll time, default: 0 (no limit)"},
{"outform", 'f', 1, "encoding of stored certificates, default: der"},
}
});
}
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "pki.h"
#include "pki_cert.h"
#include "est/est_tls.h"
#include <credentials/containers/pkcs7.h>
#include <credentials/certificates/certificate.h>
#include <credentials/sets/mem_cred.h>
/**
* Get CA certificate[s] from an EST server (RFC 7030)
*/
static int estca()
{
cred_encoding_type_t form = CERT_ASN1_DER;
chunk_t est_response = chunk_empty;
certificate_t *cacert;
mem_cred_t *creds = NULL;
est_tls_t *est_tls;
char *arg, *error = NULL, *url = NULL, *caout = NULL;
bool force = FALSE, success;
u_int http_code = 0;
status_t status = 1;
/* initialize CA certificate storage */
creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &creds->set);
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h': /* --help */
goto usage;
case 'u': /* --url */
url = arg;
continue;
case 'C': /* --cacert */
cacert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_X509, BUILD_FROM_FILE, arg, BUILD_END);
if (!cacert)
{
DBG1(DBG_APP, "could not load cacert file '%s'", arg);
goto err;
}
creds->add_cert(creds, TRUE, cacert);
continue;
case 'c': /* --caout */
caout = arg;
continue;
case 'f': /* --outform */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
error ="invalid certificate output format";
goto usage;
}
continue;
case 'F': /* --force */
force = TRUE;
continue;
case EOF:
break;
default:
error ="invalid --estca option";
goto usage;
}
break;
}
if (!url)
{
return command_usage("--url is required");
}
est_tls = est_tls_create(url, NULL, NULL);
if (!est_tls)
{
DBG1(DBG_APP, "TLS connection to EST server was not established");
goto err;
}
success = est_tls->request(est_tls, EST_CACERTS, chunk_empty, &est_response,
&http_code, NULL);
est_tls->destroy(est_tls);
if (!success)
{
DBG1(DBG_APP, "EST request failed: HTTP %u", http_code);
goto err;
}
if (pki_cert_extract_cacerts(est_response, caout, NULL, TRUE, form, force))
{
status = 0;
}
err:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
chunk_free(&est_response);
return status;
usage:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
return command_usage(error);
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
estca, 'e', "estca",
"get CA certificate[s] from a EST server",
{"--url url [--cacert file]+ [--caout file] [--outform der|pem] [--force]"},
{
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the SCEP server"},
{"cacert", 'C', 1, "TLS CA certificate"},
{"caout", 'c', 1, "CA certificate [template]"},
{"outform", 'f', 1, "encoding of stored certificates, default: der"},
{"force", 'F', 0, "force overwrite of existing files"},
}
});
}
+5 -2
View File
@@ -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))
{
+9 -3
View File
@@ -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"},
+658
View File
@@ -0,0 +1,658 @@
/*
* Copyright (C) 2005 Jan Hutter, Martin Willi
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include "pki.h"
#include "pki_cert.h"
#include "scep/scep.h"
#include <credentials/certificates/certificate.h>
#include <credentials/certificates/x509.h>
#include <credentials/sets/mem_cred.h>
#include <asn1/asn1.h>
/* default polling time interval in SCEP manual mode */
#define DEFAULT_POLL_INTERVAL 60 /* seconds */
/**
* Enroll an X.509 certificate with a SCEP server (RFC 8894)
*/
static int scep()
{
char *arg, *url = NULL, *file = NULL, *dn = NULL, *error = NULL;
char *ca_enc_file = NULL, *ca_sig_file = NULL;
char *client_cert_file = NULL, *client_key_file = NULL;
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;
chunk_t pkcs7_req = chunk_empty;
chunk_t certPoll = chunk_empty;
chunk_t issuerAndSubject = chunk_empty;
chunk_t data = chunk_empty;
hash_algorithm_t digest_alg = HASH_SHA256;
encryption_algorithm_t cipher = ENCR_AES_CBC;
uint16_t key_size = 128;
signature_params_t *scheme = NULL;
private_key_t *private = NULL, *priv_signer = NULL;
public_key_t *public = NULL;
certificate_t *pkcs10 = NULL, *x509_signer = NULL, *cert = NULL;
certificate_t *x509_ca_sig = NULL, *x509_ca_enc = NULL;
identification_t *subject = NULL, *issuer = NULL;
container_t *container = NULL;
mem_cred_t *creds = NULL, *client_creds = NULL;
scep_msg_t scep_msg_type;
scep_attributes_t attrs = empty_scep_attributes;
uint32_t caps_flags;
u_int poll_interval = DEFAULT_POLL_INTERVAL;
u_int max_poll_time = 0, poll_start = 0;
u_int http_code = 0;
time_t notBefore, notAfter;
linked_list_t *san;
int status = 1;
bool ok, http_post = FALSE;
bool pss = lib->settings->get_bool(lib->settings,
"%s.rsa_pss", FALSE, lib->ns);
bool renewal_via_pkcs_req = lib->settings->get_bool(lib->settings,
"%s.scep.renewal_via_pkcs_req", FALSE, lib->ns);
/* initialize certificate validity */
notBefore = time(NULL);
notAfter = notBefore + 365 * 24 * 60 * 60;
/* initialize list of subjectAltNames */
san = linked_list_create();
/* initialize CA certificate storage */
creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &creds->set);
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h': /* --help */
goto usage;
case 'u': /* --url */
url = arg;
continue;
case 'i': /* --in */
file = arg;
continue;
case 'd': /* --dn */
dn = arg;
continue;
case 'a': /* --san */
san->insert_last(san, identification_create_from_string(arg));
continue;
case 'P': /* --profile */
cert_type = chunk_create(arg, strlen(arg));
continue;
case 'p': /* --password */
challenge_password = chunk_create(arg, strlen(arg));
continue;
case 'e': /* --cacert-enc */
ca_enc_file = arg;
continue;
case 's': /* --cacert-sig */
ca_sig_file = arg;
continue;
case 'C': /* --cacert */
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, arg, BUILD_END);
if (!cert)
{
DBG1(DBG_APP, "could not load cacert file '%s'", arg);
goto err;
}
creds->add_cert(creds, TRUE, cert);
continue;
case 'c': /* --cert */
client_cert_file = arg;
continue;
case 'k': /* --key */
client_key_file = arg;
continue;
case 'E': /* --cipher */
if (strcaseeq(arg, "des3"))
{
cipher = ENCR_3DES;
key_size = 0;
}
else if (strcaseeq(arg, "aes"))
{
cipher = ENCR_AES_CBC;
key_size = 128;
}
else
{
error = "invalid --cipher type";
goto usage;
}
continue;
case 'g': /* --digest */
if (!enum_from_name(hash_algorithm_short_names, arg, &digest_alg))
{
error = "invalid --digest type";
goto usage;
}
continue;
case 'R': /* --rsa-padding */
if (streq(arg, "pss"))
{
pss = TRUE;
}
if (streq(arg, "pkcs1"))
{
pss = FALSE;
}
else {
error = "invalid RSA padding";
goto usage;
}
continue;
case 't': /* --pollinterval */
poll_interval = atoi(optarg);
if (poll_interval <= 0)
{
error = "invalid interval specified";
goto usage;
}
continue;
case 'm': /* --maxpolltime */
max_poll_time = atoi(optarg);
continue;
case 'f': /* --form */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
error = "invalid certificate output format";
goto usage;
}
continue;
case EOF:
break;
default:
error = "invalid --scep option";
goto usage;
}
break;
}
if (!url)
{
error = "--url is required";
goto usage;
}
if (!ca_enc_file)
{
error = "--cacert-enc is required";
goto usage;
}
if (!ca_sig_file)
{
error = "--cacert-sig is required";
goto usage;
}
if (client_cert_file && !client_key_file)
{
error = "--oldkey is required if --oldcert is set";
goto usage;
}
if (!dn)
{
error = "--dn is required";
goto usage;
}
subject = identification_create_from_string(dn);
if (subject->get_type(subject) != ID_DER_ASN1_DN)
{
DBG1(DBG_APP, "supplied --dn is not a distinguished name");
goto err;
}
/* load RSA private key from file or stdin */
if (file)
{
private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
BUILD_FROM_FILE, file, BUILD_END);
}
else
{
chunk_t chunk;
set_file_mode(stdin, CERT_ASN1_DER);
if (!chunk_from_fd(0, &chunk))
{
DBG1(DBG_APP, "reading private key failed: %s", strerror(errno));
goto err;
}
private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
BUILD_BLOB, chunk, BUILD_END);
free(chunk.ptr);
}
if (!private)
{
DBG1(DBG_APP, "parsing private key failed");
goto err;
}
public = private->get_public_key(private);
/* Request capabilities from SCEP server */
if (!scep_http_request(url, SCEP_GET_CA_CAPS, FALSE, chunk_empty,
&scep_response, &http_code))
{
DBG1(DBG_APP, "did not receive a valid scep response: HTTP %u", http_code);
goto err;
}
caps_flags = scep_parse_caps(scep_response);
chunk_free(&scep_response);
/* check support of selected digest algorithm */
switch (digest_alg)
{
case HASH_SHA256:
ok = (caps_flags & SCEP_CAPS_SHA256) ||
(caps_flags & SCEP_CAPS_SCEPSTANDARD);
break;
case HASH_SHA384:
ok = (caps_flags & SCEP_CAPS_SHA384);
break;
case HASH_SHA512:
ok = (caps_flags & SCEP_CAPS_SHA512);
break;
case HASH_SHA224:
ok = (caps_flags & SCEP_CAPS_SHA224);
break;
case HASH_SHA1:
ok = (caps_flags & SCEP_CAPS_SHA1);
break;
default:
ok = FALSE;
}
if (!ok)
{
DBG1(DBG_APP, "%N digest algorithm not supported by CA",
hash_algorithm_short_names, digest_alg);
goto err;
}
/* check support of selected encryption algorithm */
switch (cipher)
{
case ENCR_AES_CBC:
ok = (caps_flags & SCEP_CAPS_AES) ||
(caps_flags & SCEP_CAPS_SCEPSTANDARD);
break;
case ENCR_3DES:
ok = (caps_flags & SCEP_CAPS_DES3);
break;
default:
ok = FALSE;
}
if (!ok)
{
DBG1(DBG_APP, "%N encryption algorithm not supported by CA",
encryption_algorithm_names, cipher);
goto err;
}
DBG2(DBG_APP, "%N digest and %N encryption algorithm supported by CA",
hash_algorithm_short_names, digest_alg,
encryption_algorithm_names, cipher);
/* check support of HTTP POST operation */
if ((caps_flags & SCEP_CAPS_POSTPKIOPERATION) ||
(caps_flags & SCEP_CAPS_SCEPSTANDARD))
{
http_post = TRUE;
}
DBG2(DBG_APP, "HTTP POST %ssupported", http_post ? "" : "not ");
scheme = get_signature_scheme(private, digest_alg, pss);
if (!scheme)
{
DBG1(DBG_APP, "no signature scheme found");
goto err;
}
/* generate PKCS#10 certificate request */
pkcs10 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_PKCS10_REQUEST,
BUILD_SIGNING_KEY, private,
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)
{
DBG1(DBG_APP, "generating certificate request failed");
goto err;
}
/* generate PKCS#10 encoding */
if (!pkcs10->get_encoding(pkcs10, CERT_ASN1_DER, &pkcs10_encoding))
{
DBG1(DBG_APP, "encoding certificate request failed");
pkcs10->destroy(pkcs10);
goto err;
}
pkcs10->destroy(pkcs10);
if (!scep_generate_transaction_id(public, &transID, &serialNumber))
{
DBG1(DBG_APP, "generating transaction ID failed");
goto err;
}
DBG1(DBG_APP, "transaction ID: %.*s", (int)transID.len, transID.ptr);
if (client_cert_file)
{
/* check support of Renewal Operation */
if (!(caps_flags & SCEP_CAPS_RENEWAL))
{
DBG1(DBG_APP, "Renewal operation not supported by SCEP server");
goto err;
}
DBG2(DBG_APP, "SCEP Renewal operation supported");
/* set message type for SCEP renewal request */
scep_msg_type = renewal_via_pkcs_req ? SCEP_PKCSReq_MSG :
SCEP_RenewalReq_MSG;
/* load old client certificate */
x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, client_cert_file, BUILD_END);
if (!x509_signer)
{
DBG1(DBG_APP, "loading client cert file '%s' failed",
client_cert_file);
goto err;
}
/* load old RSA private key */
priv_signer = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
BUILD_FROM_FILE, client_key_file, BUILD_END);
if (!priv_signer)
{
DBG1(DBG_APP, "loading client private key file '%s' failed",
client_key_file);
goto err;
}
}
else
{
/* create self-signed X.509 certificate */
x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_SIGNING_KEY, private,
BUILD_PUBLIC_KEY, public,
BUILD_SUBJECT, subject,
BUILD_NOT_BEFORE_TIME, notBefore,
BUILD_NOT_AFTER_TIME, notAfter,
BUILD_SERIAL, serialNumber,
BUILD_SUBJECT_ALTNAMES, san,
BUILD_SIGNATURE_SCHEME, scheme,
BUILD_END);
if (!x509_signer)
{
DBG1(DBG_APP, "generating self-signed certificate failed");
goto err;
}
/* the signing key is identical to the client key */
priv_signer = private->get_ref(private);
/* set message type for SCEP request */
scep_msg_type = SCEP_PKCSReq_MSG;
}
/* create a separate set for the self-signed or old client credentials */
client_creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &client_creds->set);
client_creds->add_cert(client_creds, FALSE, x509_signer);
client_creds->add_key(client_creds, priv_signer);
/* load CA or RA certificate used for encryption */
x509_ca_enc = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, ca_enc_file, BUILD_END);
if (!x509_ca_enc)
{
DBG1(DBG_APP, "could not load encryption cacert file '%s'", ca_enc_file);
goto end;
}
/* load CA certificate used for signature verification */
x509_ca_sig = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, ca_sig_file, BUILD_END);
if (!x509_ca_sig)
{
DBG1(DBG_APP, "could not load signature cacert file '%s'", ca_sig_file);
goto end;
}
x509_ca_sig = creds->add_cert_ref(creds, TRUE, x509_ca_sig);
/* build pkcs7 request */
pkcs7_req = scep_build_request(pkcs10_encoding, transID, scep_msg_type,
x509_ca_enc, cipher, key_size, x509_signer,
digest_alg, priv_signer);
if (!pkcs7_req.ptr)
{
DBG1(DBG_APP, "failed to build SCEP request");
goto end;
}
if (!scep_http_request(url, SCEP_PKI_OPERATION, http_post, pkcs7_req,
&scep_response, &http_code))
{
DBG1(DBG_APP, "did not receive a valid SCEP response: HTTP %u", http_code);
goto end;
}
if (!scep_parse_response(scep_response, transID, &container, &attrs))
{
goto end;
}
/* in case of manual mode, we are going into a polling loop */
if (attrs.pkiStatus == SCEP_PENDING)
{
issuer = x509_ca_sig->get_subject(x509_ca_sig);
issuerAndSubject = asn1_wrap(ASN1_SEQUENCE, "cc",
issuer->get_encoding(issuer),
subject->get_encoding(subject));
if (max_poll_time > 0)
{
DBG1(DBG_APP, " SCEP request pending, polling every %d seconds"
" up to %d seconds", poll_interval, max_poll_time);
}
else
{
DBG1(DBG_APP, " SCEP request pending, polling indefinitely"
" every %d seconds", poll_interval);
}
poll_start = time_monotonic(NULL);
}
while (attrs.pkiStatus == SCEP_PENDING)
{
if (max_poll_time > 0 &&
(time_monotonic(NULL) - poll_start) >= max_poll_time)
{
DBG1(DBG_APP, "maximum poll time reached: %d seconds", max_poll_time);
goto end;
}
DBG1(DBG_APP, " going to sleep for %d seconds", poll_interval);
sleep(poll_interval);
chunk_free(&certPoll);
chunk_free(&scep_response);
chunk_free(&attrs.transID);
chunk_free(&attrs.recipientNonce);
container->destroy(container);
container = NULL;
DBG1(DBG_APP, "transaction ID: %.*s", (int)transID.len, transID.ptr);
certPoll = scep_build_request(issuerAndSubject, transID, SCEP_CertPoll_MSG,
x509_ca_enc, cipher, key_size, x509_signer,
digest_alg, priv_signer);
if (!certPoll.ptr)
{
DBG1(DBG_APP, "failed to build SCEP certPoll request");
goto end;
}
if (!scep_http_request(url, SCEP_PKI_OPERATION, http_post, certPoll,
&scep_response, &http_code))
{
DBG1(DBG_APP, "did not receive a valid SCEP response: HTTP %u",
http_code);
goto end;
}
if (!scep_parse_response(scep_response, transID, &container, &attrs))
{
goto end;
}
}
if (attrs.pkiStatus != SCEP_SUCCESS)
{
DBG1(DBG_APP, "reply status is not 'SUCCESS'");
goto end;
}
if (!container->get_data(container, &data))
{
DBG1(DBG_APP, "extracting enveloped-data failed");
goto end;
}
container->destroy(container);
/* decrypt enveloped-data container */
container = lib->creds->create(lib->creds,
CRED_CONTAINER, CONTAINER_PKCS7,
BUILD_BLOB_ASN1_DER, data,
BUILD_END);
chunk_free(&data);
if (!container)
{
DBG1(DBG_APP, "could not decrypt envelopedData");
goto end;
}
if (!container->get_data(container, &data))
{
DBG1(DBG_APP, "extracting encrypted-data failed");
goto end;
}
container->destroy(container);
container = NULL;
status = 0;
end:
/* remove the old client certificate before extracting the new one */
lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
client_creds->destroy(client_creds);
if (status == 0)
{
status = pki_cert_extract_cert(data, form) ? 0 : 1;
chunk_free(&data);
}
err:
/* cleanup */
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
san->destroy_offset(san, offsetof(identification_t, destroy));
signature_params_destroy(scheme);
DESTROY_IF(subject);
DESTROY_IF(private);
DESTROY_IF(public);
DESTROY_IF(x509_ca_enc);
DESTROY_IF(x509_ca_sig);
DESTROY_IF(container);
chunk_free(&scep_response);
chunk_free(&serialNumber);
chunk_free(&transID);
chunk_free(&pkcs10_encoding);
chunk_free(&pkcs7_req);
chunk_free(&certPoll);
chunk_free(&issuerAndSubject);
chunk_free(&attrs.transID);
chunk_free(&attrs.recipientNonce);
return status;
usage:
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
san->destroy_offset(san, offsetof(identification_t, destroy));
return command_usage(error);
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
scep, 'S', "scep",
"Enroll an X.509 certificate with a SCEP server",
{"--url url [--in file] --dn distinguished-name [--san subjectAltName]+",
"[--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]"},
{
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the SCEP server"},
{"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"},
{"cacert", 'C', 1, "Additional CA certificates"},
{"cert", 'c', 1, "Old certificate about to be renewed"},
{"key", 'k', 1, "Old RSA private key about to be replaced"},
{"cipher", 'E', 1, "encryption cipher, default: aes"},
{"digest", 'g', 1, "digest for signature creation, default: sha256"},
{"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
{"interval", 't', 1, "poll interval, default: 60s"},
{"maxpolltime", 'm', 1, "maximum poll time, default: 0 (no limit)"},
{"outform", 'f', 1, "encoding of stored certificates, default: der"},
}
});
}
+105
View File
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2005 Jan Hutter, Martin Willi
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "pki.h"
#include "pki_cert.h"
#include "scep/scep.h"
#include <credentials/certificates/certificate.h>
/**
* Get CA certificate[s] from a SCEP server (RFC 8894)
*/
static int scepca()
{
cred_encoding_type_t form = CERT_ASN1_DER;
chunk_t scep_response = chunk_empty;
char *arg, *url = NULL, *caout = NULL, *raout = NULL;
bool force = FALSE, success;
u_int http_code = 0;
while (TRUE)
{
switch (command_getopt(&arg))
{
case 'h': /* --help */
return command_usage(NULL);
case 'u': /* --url */
url = arg;
continue;
case 'c': /* --caout */
caout = arg;
continue;
case 'r': /* --raout */
raout = arg;
continue;
case 'f': /* --form */
if (!get_form(arg, &form, CRED_CERTIFICATE))
{
return command_usage("invalid certificate output format");
}
continue;
case 'F': /* --force */
force = TRUE;
continue;
case EOF:
break;
default:
return command_usage("invalid --scepca option");
}
break;
}
if (!url)
{
return command_usage("--url is required");
}
if (!scep_http_request(url, SCEP_GET_CA_CERT, FALSE, chunk_empty,
&scep_response, &http_code))
{
DBG1(DBG_APP, "did not receive a valid SCEP response: HTTP %u", http_code);
return 1;
}
success = pki_cert_extract_cacerts(scep_response, caout, raout, TRUE, form,
force);
chunk_free(&scep_response);
return success ? 0 : 1;
}
/**
* Register the command.
*/
static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
scepca, 'C', "scepca",
"get CA [and RA] certificate[s] from a SCEP server",
{"--url url [--caout file] [--raout file] [--outform der|pem] [--force]"},
{
{"help", 'h', 0, "show usage information"},
{"url", 'u', 1, "URL of the SCEP server"},
{"caout", 'c', 1, "CA certificate [template]"},
{"raout", 'r', 1, "RA certificate [template]"},
{"outform", 'f', 1, "encoding of stored certificates, default: der"},
{"force", 'F', 0, "force overwrite of existing files"},
}
});
}
+434
View File
@@ -0,0 +1,434 @@
/*
* Copyright (C) 2013-2022 Andreas Steffen
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#define _GNU_SOURCE /* for asprintf() */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "est_tls.h"
#include <utils/debug.h>
#include <utils/lexparser.h>
#include <tls_socket.h>
static const char *operations[] = {
"cacerts",
"simpleenroll",
"simplereenroll",
"fullcmc",
"serverkeygen",
"csrattrs"
};
static const char *request_types[] = {
"",
"application/pkcs10",
"application/pkcs10",
"application/pkcs7-mime",
"application/pkcs10",
""
};
typedef struct private_est_tls_t private_est_tls_t;
/**
* Private data of an est_tls_t object.
*/
struct private_est_tls_t {
/**
* Public est_tls_t interface.
*/
est_tls_t public;
/**
* EST Server (IP address and port)
*/
host_t *host;
/**
* File descriptor for secure TCP socket
*/
int fd;
/**
* TLS socket
*/
tls_socket_t *tls;
/**
* Host string of the form <hostname:port> used for http requests
*/
char *http_host;
/**
* Path string used for http requests
*/
char *http_path;
/**
* Optional <username:password> for http basic authentication
*/
char *user_pass;
};
static chunk_t build_http_request(private_est_tls_t *this, est_op_t op, chunk_t in)
{
char *http_header, http_auth[256];
chunk_t request = chunk_empty, data;
int len;
/* Use Basic Authentication? */
if (this->user_pass)
{
snprintf(http_auth, sizeof(http_auth), "Authorization: Basic %s\r\n",
this->user_pass);
}
else
{
*http_auth = '\0';
}
if (strlen(request_types[op]) > 0) /* create HTTP POST request */
{
data = chunk_to_base64(in, NULL);
len = asprintf(&http_header,
"POST %s/.well-known/est/%s HTTP/1.1\r\n"
"Host: %s\r\n"
"%s"
"Content-Type: %s\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-Length: %d\r\n"
"\r\n",
this->http_path, operations[op], this->http_host, http_auth,
request_types[op], (int)data.len);
if (len > 0)
{
request = chunk_cat("mm", chunk_create(http_header, len), data);
}
else
{
chunk_free(&data);
}
}
else /* create HTTP GET request */
{
len = asprintf(&http_header,
"GET %s/.well-known/est/%s HTTP/1.1\r\n"
"Host: %s\r\n"
"%s"
"\r\n",
this->http_path, operations[op], this->http_host, http_auth);
if (len > 0)
{
request = chunk_create(http_header, len);
}
}
return request;
}
static bool parse_http_header(chunk_t *in, u_int *http_code, u_int *content_len,
bool *base64, u_int *retry_after)
{
chunk_t line, version, parameter;
u_int len;
/*initialize output parameters */
*http_code = 0;
*content_len = 0;
*base64 = FALSE;
if (retry_after)
{
*retry_after = 0;
}
/* Process HTTP protocol version and HTTP status code */
if (!fetchline(in, &line) || !extract_token(&version, ' ', &line) ||
!match("HTTP/1.1", &version) || sscanf(line.ptr, "%d", http_code) != 1)
{
DBG1(DBG_APP, "malformed http response header");
return FALSE;
}
/* Process HTTP header line by line until the HTTP body is reached */
while (fetchline(in, &line))
{
if (line.len == 0)
{
break;
}
if (extract_token(&parameter, ':', &line) && eat_whitespace(&line))
{
if (matchcase("Content-Length", &parameter))
{
if (sscanf(line.ptr, "%u", &len) == 1)
{
*content_len = len;
}
}
else if (matchcase("Content-Transfer-Encoding", &parameter) &&
matchcase("Base64", &line))
{
*base64 = TRUE;
}
else if (matchcase("Retry-After", &parameter))
{
if (sscanf(line.ptr, "%u", &len) == 1 && retry_after)
{
*retry_after = len;
}
}
}
}
return (*http_code < 300);
}
METHOD(est_tls_t, request, bool,
private_est_tls_t *this, est_op_t op, chunk_t in, chunk_t *out,
u_int *http_code, u_int *retry_after)
{
chunk_t http = chunk_empty, data = chunk_empty, response;
u_int content_len;
char buf[1024];
bool base64;
int len;
/* initialize output variables */
*out = chunk_empty;
*http_code = 0;
if (retry_after)
{
*retry_after = 0;
}
http = build_http_request(this, op, in);
if (http.len == 0)
{
return FALSE;
}
DBG2(DBG_APP, "http request: %B", &http);
/* send https request */
if (this->tls->write(this->tls, http.ptr, http.len) != http.len)
{
DBG1(DBG_APP, "TLS socket write failed");
chunk_free(&http);
return FALSE;
}
chunk_free(&http);
/* receive first part of https response */
len = this->tls->read(this->tls, buf, sizeof(buf), TRUE);
if (len <= 0)
{
DBG1(DBG_APP, "TLS socket first read failed");
return FALSE;
}
response = chunk_create(buf, len);
DBG2(DBG_APP, "http response: %B", &response);
if (!parse_http_header(&response, http_code, &content_len, &base64,
retry_after))
{
return FALSE;
}
if (*http_code == EST_HTTP_CODE_OK)
{
if (content_len == 0)
{
DBG1(DBG_APP, "no content-length defined in http header");
return FALSE;
}
if (response.len > content_len)
{
DBG1(DBG_APP, "http body is larger than content-length");
return FALSE;
}
data = chunk_alloc(content_len);
memcpy(data.ptr, response.ptr, response.len);
if (data.len > response.len)
{
/* read remaining part of https response */
len = this->tls->read(this->tls, data.ptr + response.len,
data.len - response.len, TRUE);
if (len < data.len - response.len)
{
DBG1(DBG_APP, "TLS socket second read failed");
chunk_free(&data);
return FALSE;
}
}
if (base64)
{
*out = chunk_from_base64(data, NULL);
chunk_free(&data);
}
else
{
*out = data;
}
}
return TRUE;
}
METHOD(est_tls_t, destroy, void,
private_est_tls_t *this)
{
DESTROY_IF(this->tls);
DESTROY_IF(this->host);
if (this->fd != -1)
{
close(this->fd);
}
free(this->http_host);
free(this->http_path);
free(this->user_pass);
free(this);
}
static bool est_tls_init(private_est_tls_t *this, char *uri,
certificate_t *client_cert)
{
identification_t *client_id = NULL, *server_id = NULL;
char *host_str, *port_str, *path_str;
int port = 443;
bool success = FALSE;
/* check for "https://" prefix and remove it */
if (strlen(uri) < 8 || !strncaseeq(uri, "https://", 8))
{
DBG1(DBG_APP, "'%s' is not an https URI", uri);
return FALSE;
}
uri += 8;
/* any trailing path or command? */
path_str = strchr(uri, '/');
this->http_path =
strdup( (path_str == NULL || path_str[1] == '\0') ? "" : path_str );
if (path_str)
{
/* NUL-terminate host_str */
*path_str = '\0';
}
/* duplicate <hostname:port> string since we are going to manipulate it */
host_str = strdup(uri);
/* another duplicate for http requests */
this->http_host = strdup(host_str);
/* extract hostname and port from URI */
port_str = strchr(host_str, ':');
if (port_str)
{
/* NUL-terminate hostname */
*port_str++ = '\0';
/* extract port */
if (sscanf(port_str, "%d", &port) != 1)
{
DBG1(DBG_APP, "parsing server port %s failed", port_str);
goto end;
}
}
/* open TCP socket and connect to EST server */
this->host = host_create_from_dns(host_str, 0, port);
if (!this->host)
{
DBG1(DBG_APP, "resolving hostname %s failed", host_str);
goto end;
}
this->fd = socket(this->host->get_family(this->host), SOCK_STREAM, 0);
if (this->fd == -1)
{
DBG1(DBG_APP, "opening socket failed: %s", strerror(errno));
goto end;
}
if (connect(this->fd, this->host->get_sockaddr(this->host),
*this->host->get_sockaddr_len(this->host)) == -1)
{
DBG1(DBG_APP, "connecting to %#H failed: %s",
this->host, strerror(errno));
goto end;
}
if (client_cert)
{
client_id = client_cert->get_subject(client_cert);
}
server_id = identification_create_from_string(host_str);
/* open TLS socket */
this->tls = tls_socket_create(FALSE, server_id, client_id, this->fd,
NULL, TLS_UNSPEC, TLS_UNSPEC, 0);
server_id->destroy(server_id);
if (!this->tls)
{
DBG1(DBG_APP, "creating TLS socket failed");
goto end;
}
success = TRUE;
end:
free(host_str);
return success;
}
/**
* See header
*/
est_tls_t *est_tls_create(char *uri, certificate_t *client_cert, char *user_pass)
{
private_est_tls_t *this;
INIT(this,
.public = {
.request = _request,
.destroy = _destroy,
},
);
if (user_pass)
{
this->user_pass = strdup(user_pass);
}
if (!est_tls_init(this, uri, client_cert))
{
destroy(this);
return NULL;
}
return &this->public;
}
+79
View File
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 Andreas Steffen
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup est_tls est_tls
* @{ @ingroup pki
*/
#ifndef EST_TLS_H_
#define EST_TLS_H_
#include <library.h>
#include <credentials/certificates/certificate.h>
#define EST_HTTP_CODE_OK 200
#define EST_HTTP_CODE_ACCEPTED 202
typedef struct est_tls_t est_tls_t;
/**
* EST (RFC 7030) Operations
*/
typedef enum {
EST_CACERTS,
EST_SIMPLE_ENROLL,
EST_SIMPLE_REENROLL,
EST_FULL_CMC,
EST_SERVER_KEYGEN,
EST_CSR_ATTRS
} est_op_t;
/**
* TLS Interface for sending and receiving HTTPS messages
*/
struct est_tls_t {
/**
* Send a https request and get a response back
*
* @param option EST operation
* @param in HTTP POST input data
* @param out HTTP response
* @param http_code HTTP status code
* @param retry_after Retry time in seconds
* @result TRUE if successful
*/
bool (*request)(est_tls_t *this, est_op_t op, chunk_t in, chunk_t *out,
u_int *http_code, u_int *retry_after);
/**
* Destroy an est_tls_t object.
*/
void (*destroy)(est_tls_t *this);
};
/**
* Create a est_tls instance.
*
* @param uri URI (https://...)
* @param client_cert Optional client certificate
* @param user_pass Optional username:password for HTTP Basic Authentication
*/
est_tls_t *est_tls_create(char *uri, certificate_t *client_cert,
char *user_pass);
#endif /** EST_TLS_H_ @}*/
+4
View File
@@ -2,6 +2,8 @@ man1_MANS = \
pki.1 \
pki---acert.1 \
pki---dn.1 \
pki---est.1 \
pki---estca.1 \
pki---gen.1 \
pki---issue.1 \
pki---keyid.1 \
@@ -9,6 +11,8 @@ man1_MANS = \
pki---print.1 \
pki---pub.1 \
pki---req.1 \
pki---scep.1 \
pki---scepca.1 \
pki---self.1 \
pki---signcrl.1 \
pki---verify.1
+183
View File
@@ -0,0 +1,183 @@
.TH "PKI \-\-EST" 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
pki \-\-est \- Enroll an X.509 certificate with an EST server
.
.SH "SYNOPSIS"
.
.SY pki\ \-\-est
.BI\-\-\-url\~ url
.OP \-\-in file
.BI \-\-cacert\~ file
.RB [ \-\-cert
.IR file | \fB\-\-certid\fR
.IR hex ]
.RB [ \-\-key
.IR file | \fB\-\-keyid\fR
.IR hex ]
.OP \-\-userpass username:password
.OP \-\-interval time
.OP \-\-maxpolltime time
.OP \-\-outform encoding
.OP \-\-debug level
.YS
.
.SY pki\ \-\-est
.BI \-\-options\~ file
.YS
.
.SY "pki \-\-est"
.B \-h
|
.B \-\-help
.YS
.
.SH "DESCRIPTION"
.
This sub-command of
.BR pki (1)
sends a PKCS#10 certificate request via HTTPS to a server using the Enrollment
over Secure Transport (EST) Protocol (RFC 7030). After successful authorization
which with manual authentication requires periodic polling by the enrollment
client, the EST server returns an X.509 certificate signed by the CA.
Before the expiry of the current certificate, a new client certificate based on
a fresh private key can be requested, using the old certificate and the old
key for automatic TLS client authentication with the EST server.
.
.SH "OPTIONS"
.
.TP
.B "\-h, \-\-help"
Print usage information with a summary of the available options.
.TP
.BI "\-v, \-\-debug " level
Set debug level, default: 1.
.TP
.BI "\-+, \-\-options " file
Read command line options from \fIfile\fR.
.TP
.BI "\-u, \-\-url " url
URL of the EST server.
.TP
.BI "\-i, \-\-in " file
PKCS#10 certificate request. If not given, the certificate request is read from
\fISTDIN\fR.
.TP
.BI "\-C, \-\-cacert " file
CA certificate in the trust chain used for EST TLS server signature verification
or in the trust chain to verify the client certificate issued by the CA.
Can be used multiple times.
.TP
.BI "\-c, \-\-cert " file
Client certificate to be renewed.
.TP
.BI "\-X, \-\-certid " hex
Smartcard or TPM 2.0 client certficate object handle.
.TP
.BI "\-k, \-\-key " file
Client private key to be replaced.
.TP
.BI "\-x, \-\-keyid " hex
Smartcard or TPM 2.0 client private key object handle.
.TP
.BI "\-p, \-\-userpass " username:password
Optional username:password that may be used for HTTP basic authentication.
.TP
.BI "\-t, \-\-interval " time
Poll interval in seconds, defaults to \fI60s\fR. This value might get overridden
by the
.B retry-after
header in the HTTP 202 reply from the EST server.
.TP
.BI "\-m, \-\-maxpolltime " time
Maximum poll time in seconds, defaults to \fI0\fR which means unlimited polling.
.TP
.BI "\-f, \-\-outform " encoding
Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or
\fIpem\fR (Base64 PEM), defaults to \fIder\fR.
.
.SH "EXAMPLES"
.
To save some typing work the following command line options are stored in a
\fIest.opt\fR file:
.PP
.EX
\-\-url https://pki.strongswan.org:8443
\-\-cacert tlsca.crt
\-\-cacert tlsca-1.crt
\-\-cacert myca.crt
\-\-cacert myca-1.crt
.EE
.PP
.B NOTE:
For a successful HTTPS connection, trust must be established into the EST server
certificate. The TLS trust chain including the root CA certificate and
optionally intermediate CA certificates must be given using [multiple]
.B --cacert*
options.
.P
The
.B --cacert
option must also be used to be able to verify the received client certificate
issued by the CA. This second trust chain might be identical to the TLS trust
chain (if the EST server is using a TLS server certificate issued by its own CA)
or might be totally different, e.g. if a Let's Encrypt EST server certificate is
used.
.P
With the following command, an X.509 certificate signed by the intermediate CA is
requested from an EST server based on a PKCS#10 certificate request:
.PP
.EX
pki \-\-options est.opt --in moonReq.der > moonCert.der
negotiated TLS 1.3 using suite TLS_AES_256_GCM_SHA384
received TLS server certificate 'C=CH, O=strongSwan Project, CN=pki.strongswan.org'
using certificate "C=CH, O=strongSwan Project, CN=pki.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
EST request pending, polling indefinitely every 300 seconds
going to sleep for 300 seconds
...
Issued certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
serial: 1a:ff:de:66:d9:38:ea:d5:b6:da
using certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
Issued certificate is trusted, valid from Aug 22 15:19:43 2022 until Aug 22 15:19:43 2023 (currently valid)
.EE
.PP
This certificate can be renewed some time before it expires with the command:
.PP
.EX
pki \-\-options est.opt --in moonReqNew.der --cert moonCert.der --key moonKey.der > moonCertNew.der
negotiated TLS 1.3 using suite TLS_AES_256_GCM_SHA384
received TLS server certificate 'C=CH, O=strongSwan Project, CN=pki.strongswan.org'
using certificate "C=CH, O=strongSwan Project, CN=pki.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
sending TLS client certificate 'C=CH, O=strongSwan Project, CN=moon.strongswan.org'
sending TLS intermediate certificate 'C=CH, O=strongSwan Project, CN=strongSwan Issuing CA'
Issued certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
serial: 1b:ff:ad:dc:2f:50:c4:cb:a1:44
using certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
Issued certificate is trusted, valid from Jul 20 12:21:00 2023 until Jul 20 12:21:00 2024 (currently valid)
.EE
.PP
If the private key and the certificate of the client is stored in a TPM 2.0, the
renewal can be done wtih the following options:
.PP
.EX
pki \-\-options est.opt --in moonReqNew.der --certid 0x01800004 --keyid 0x81010004 > moonCertNew.der
.SH "SEE ALSO"
.
.BR pki (1)
+139
View File
@@ -0,0 +1,139 @@
.TH "PKI \-\-ESTCA" 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
pki \-\-estca \- Get CA certificate[s] from an EST server
.
.SH "SYNOPSIS"
.
.SY pki\ \-\-estca
.BI\-\-\-url\~ url
.BI\-\-\-cacert\~ file
.OP \-\-caout file
.OP \-\-outform encoding
.OP \-\-force
.OP \-\-debug level
.YS
.
.SY pki\ \-\-estca
.BI \-\-options\~ file
.YS
.
.SY "pki \-\-estca"
.B \-h
|
.B \-\-help
.YS
.
.SH "DESCRIPTION"
.
This sub-command of
.BR pki (1)
gets CA certificates via https from an EST server using the \fI/cacerts\fR
operation of the Enrollment over Secure Transport protocol (RFC 7030).
.
.SH "OPTIONS"
.
.TP
.B "\-h, \-\-help"
Print usage information with a summary of the available options.
.TP
.BI "\-v, \-\-debug " level
Set debug level, default: 1.
.TP
.BI "\-+, \-\-options " file
Read command line options from \fIfile\fR.
.TP
.BI "\-u, \-\-url " url
URL of the SCEP server.
.TP
.BI "\-C, \-\-cacert " file
CA certificate in the trust chain used for EST TLS server signature verification.
Can be used multiple times.
.TP
.BI "\-c, \-\-caout " file
If present, path where the fetched root CA certificate file is stored to.
If several CA certificates are downloaded, then the value of
.B \-\-caout
is used as a template to derive unique filenames (*-1, *-2, etc.) for the
intermediate or sub CA certificates.
If a file suffix is missing, then depending on the value of
.B \-\-outform
either .\fIder\fR (the default) or .\fIpem\fR is automatically appended.
If the
.B \-\-caout
option is missing and
.B \-\-outform
is set to \fIpem\fR then a PEM-encoded CA certificate bundle is written to
\fIstdout\fR.
.TP
.BI "\-f, \-\-outform " encoding
Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or
\fIpem\fR (Base64 PEM), defaults to \fIder\fR.
.TP
.B "\-F, \-\-force"
Force overwrite of existing files.
.
.SH "EXAMPLES"
.
To save some typing work the following command line options are stored in a
\fIest.opt\fR file:
.PP
.EX
\-\-url https://pki.strongswan.org:8443
\-\-cacert tlsca.crt
\-\-cacert tlsca-1.crt
.EE
.PP
.B NOTE:
For a successful HTTPS connection, trust must be established into the EST server
certificate. The TLS trust chain including the root CA certificate and optionally
intermediate CA certificates must be given using [multiple]
.B --cacert
options.
.P
An EST server sends a root CA and an intermediate CA certificate:
.PP
.EX
pki \-\-estca \-\-options est.opt \-\-caout myca.crt
Root CA cert "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
serial: 65:31:00:ca:79:da:16:6b:aa:ac:89:e2:a8:f9:49:c3:10:ab:64:54
SHA256: 96:70:50:51:cd:b9:e7:94:6b:04:f6:15:45:80:fc:90:85:01:71:2a:f6:4f:d1:1b:2d:a1:7e:eb:bf:dd:be:86
SHA1 : 8e:f3:78:b0:34:a6:c1:6a:7b:c6:f5:91:eb:e5:46:9b:0d:0a:a7:ba (jvN4sDSmwWp7xvWR6+VGmw0Kp7o)
Root CA equals trusted TLS Root CA
Root CA cert is untrusted, valid until Aug 12 15:51:34 2032, 'myca.crt'
Sub CA cert "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
serial: 74:f9:7e:72:7d:b8:fd:f2:c6:e5:1b:fa:37:f9:cb:87:bf:9c:ea:e2
SHA256: a3:5b:4b:12:d5:8f:68:7b:05:11:08:27:f5:42:62:b8:b5:01:1b:19:37:9c:28:78:5d:37:08:69:6a:8c:07:bf
SHA1 : 8c:e6:67:67:c2:23:89:7b:d0:bc:b1:50:d2:1c:bc:8d:8d:69:15:11 (jOZnZ8IjiXvQvLFQ0hy8jY1pFRE)
using certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 0
Sub CA cert is trusted, valid until Aug 12 15:51:34 2027, 'mycacert-1.crt'
.EE
.PP
.B NOTE:
The trusthworthiness of the root CA certificate is either verified automatically
if the Root CA certificate of the TLS trust chain is the same as that of the
Issuing CA. Otherwise trust has to be established manually by verifying the SHA256
or SHA1 fingerprint of the DER-encoded certificate that is e.g. listed on the
official PKI website or by some other means.
.P
The stored certificate files in DER format can be overwritten by PEM-encoded
versions with:
.PP
.EX
pki \-\-estca \-\-options est.opt \-\-caout myca.crt \-\-outform pem \-\-force
.EE
.PP
A CA certificate bundle in PEM format is written to \fIstdout\fR:
.PP
.EX
pki \-\-estca \-\-options est.opt \-\-outform pem > cacerts.pem
.EE
.PP
.
.SH "SEE ALSO"
.
.BR pki (1)
+15 -4
View File
@@ -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 [email protected] > req.der
\-\-san [email protected] \-\-profile server > req.der
.EE
.PP
Generate a certificate request for an ECDSA key and a different digest:
+185
View File
@@ -0,0 +1,185 @@
.TH "PKI \-\-SCEP" 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
pki \-\-scep \- Enroll an X.509 certificate with a SCEP server
.
.SH "SYNOPSIS"
.
.SY pki\ \-\-scep
.BI\-\-\-url\~ url
.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
.OP \-\-cacert file
.BI [\-\-cert\~ file
.BI \-\-key\~ file ]
.OP \-\-cipher cipher
.OP \-\-digest digest
.OP \-\-rsa-padding padding
.OP \-\-interval time
.OP \-\-maxpolltime time
.OP \-\-outform encoding
.OP \-\-debug level
.YS
.
.SY pki\ \-\-scep
.BI \-\-options\~ file
.YS
.
.SY "pki \-\-scep"
.B \-h
|
.B \-\-help
.YS
.
.SH "DESCRIPTION"
.
This sub-command of
.BR pki (1)
sends a PKCS#10 certificate request in an encrypted and signed PKCS#7 container
via HTTP to a SCEP server using the Simple Certificate Enrollment Protocol
(RFC 8894). After successful authorization which with manual authentication
requires periodic polling by the enrollment client, the SCEP server returns an
X.509 certificate signed by the CA.
Before the expiry of the current certificate, a new client certificate based on
a fresh RSA private key can be requested, using the old certificate and the old
key for automatic authentication with the SCEP server.
.
.SH "OPTIONS"
.
.TP
.B "\-h, \-\-help"
Print usage information with a summary of the available options.
.TP
.BI "\-v, \-\-debug " level
Set debug level, default: 1.
.TP
.BI "\-+, \-\-options " file
Read command line options from \fIfile\fR.
.TP
.BI "\-u, \-\-url " url
URL of the SCEP server.
.TP
.BI "\-i, \-\-in " file
RSA private key. If not given the key is read from \fISTDIN\fR.
.TP
.BI "\-d, \-\-dn " distinguished-name
Subject distinguished name (DN). Required.
.TP
.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
.BI "\-e, \-\-cacert-enc " file
CA or RA certificate for encryption
.TP
.BI "\-s, \-\-cacert-sig " file
CA certificate for signature verification
.TP
.BI "\-C, \-\-cacert " file
Additional CA certificate in the trust chain used for signature verification.
Can be used multiple times.
.TP
.BI "\-c, \-\-cert " file
Client certificate to be renewed.
.TP
.BI "\-k, \-\-key " file
Client RSA private key to be replaced.
.TP
.BI "\-E, \-\-cipher " cipher
Cipher used for symmetric encryption. Either \fIaes\fR (the default) or \fIdes3\fR.
.TP
.BI "\-g, \-\-digest " digest
Digest to use for signature creation. One of \fIsha256\fR (the default),
\fIsha384\fR, \fIsha512\fR, or \fIsha1\fR.
.TP
.BI "\-R, \-\-rsa\-padding " padding
Padding to use for RSA signatures. Either \fIpkcs1\fR (the default) or \fIpss\fR.
.TP
.BI "\-t, \-\-interval " time
Poll interval in seconds, defaults to \fI60s\fR.
.TP
.BI "\-m, \-\-maxpolltime " time
Maximum poll time in seconds, defaults to \fI0\fR which means unlimited polling.
.TP
.BI "\-f, \-\-outform " encoding
Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or
\fIpem\fR (Base64 PEM), defaults to \fIder\fR.
.
.SH "EXAMPLES"
.
To save some typing work the following command line options are stored in a
\fIscep.opt\fR file:
.PP
.EX
\-\-url http://pki.strongswan.org:8080/scep
\-\-cacert-enc myra.crt
\-\-cacert-sig myca-1.crt
\-\-cacert myca.crt
.EE
.PP
With the following command, an X.509 certificate signed by the intermediate CA is
requested from a SCEP server:
.PP
.EX
pki \-\-options scep.opt \-\-in moonKey.der \-\-san "moon.strongswan.org" \\
\-\-dn "C=CH, O=strongSec GmbH, CN=moon.strongswan.org" > moonCert.der
transaction ID: 4DFCF31CB18A9B5333CCEC6F99CF230E4524E334
using certificate "C=CH, O=strongSwan Project, CN=SCEP RA"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
SCEP request pending, polling indefinitely every 60 seconds
going to sleep for 60 seconds
transaction ID: 4DFCF31CB18A9B5333CCEC6F99CF230E4524E334
...
going to sleep for 60 seconds
Issued certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
serial: 1e:ff:22:7b:6e:d7:4c:c1:8a:06
using certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
Issued certificate is trusted, valid from Aug 22 18:56:23 2022 until Aug 22 18:56:23 2023 (currently valid)
.EE
.PP
A certificate about to expire can be renewed with the command:
.PP
.EX
pki \-\-options scep.opt \-\-in moonNewKey.der \-\-san "moon.strongswan.org" \\
\-\-dn "C=CH, O=strongSec GmbH, CN=moon.strongswan.org" \\
\-\-cert moonCert.der \-\-key moonKey.der > moonNewCert.der
transaction ID: A9A63D028CC439F68452D125C4DBA025E67DBA95
using certificate "C=CH, O=strongSwan Project, CN=SCEP RA"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
Issued certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
serial: 1f:ff:b2:78:43:a2:9d:85:00:38
using certificate "C=CH, O=strongSwan Project, CN=moon.strongswan.org"
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
Issued certificate is trusted, valid from Jul 20 15:05:33 2023 until Jul 20 15:05:33 2024 (currently valid)
.
.SH "SEE ALSO"
.
.BR pki (1)
+161
View File
@@ -0,0 +1,161 @@
.TH "PKI \-\-SCEPCA" 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
pki \-\-scepca \- Get CA [and RA] certificate[s] from a SCEP server
.
.SH "SYNOPSIS"
.
.SY pki\ \-\-scepca
.BI\-\-\-url\~ url
.OP \-\-caout file
.OP \-\-raout file
.OP \-\-outform encoding
.OP \-\-force
.OP \-\-debug level
.YS
.
.SY pki\ \-\-scepca
.BI \-\-options\~ file
.YS
.
.SY "pki \-\-scepca"
.B \-h
|
.B \-\-help
.YS
.
.SH "DESCRIPTION"
.
This sub-command of
.BR pki (1)
gets CA and RA certificates via http from a SCEP server using the \fIGetCACert\fR
command of the Simple Certificate Enrollment Protocol (RFC 8894).
.
.SH "OPTIONS"
.
.TP
.B "\-h, \-\-help"
Print usage information with a summary of the available options.
.TP
.BI "\-v, \-\-debug " level
Set debug level, default: 1.
.TP
.BI "\-+, \-\-options " file
Read command line options from \fIfile\fR.
.TP
.BI "\-u, \-\-url " url
URL of the SCEP server.
.TP
.BI "\-c, \-\-caout " file
If present, path where the fetched root CA certificate file is stored to.
If several CA certificates are downloaded, then the value of
.B \-\-caout
is used as a template to derive unique filenames (*-1, *-2, etc.) for the
intermediate or sub CA certificates.
If a file suffix is missing, then depending on the value of
.B \-\-outform
either .\fIder\fR (the default) or .\fIpem\fR is automatically appended.
If the
.B \-\-caout
option is missing and
.B \-\-outform
is set to \fIpem\fR then a PEM-encoded CA certificate bundle is written to
\fIstdout\fR.
.TP
.BI "\-r, \-\-raout " file
If present, path where the fetched RA certificate file is stored to.
If multiple RA certificates are available, then the value of
.B \-\-raout
is used as a template to derive unique filenames (*-2, etc.). If the
.B \-\-raout
option is missing, then the value of
.B \-\-caout
is used as a template to derive unique filenames (*-ra, *-ra-2, etc.) for the RA
certificates. If a file suffix is missing, then depending on the value of
.B \-\-outform
either .\fIder\fR (the default) or .\fIpem\fR is automatically appended.
.TP
.BI "\-f, \-\-outform " encoding
Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or
\fIpem\fR (Base64 PEM), defaults to \fIder\fR.
.TP
.B "\-F, \-\-force"
Force overwrite of existing files.
.
.SH "EXAMPLES"
.
A SCEP server sends a root CA and an intermediate CA certificate as well as an
RA certificate:
.PP
.EX
pki \-\-scepca \-\-url http://pki.strongswan.org:8080/scep \-\-caout myca.crt \-\-raout myra.crt
Root CA cert "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
serial: 65:31:00:ca:79:da:16:6b:aa:ac:89:e2:a8:f9:49:c3:10:ab:64:54
SHA256: 96:70:50:51:cd:b9:e7:94:6b:04:f6:15:45:80:fc:90:85:01:71:2a:f6:4f:d1:1b:2d:a1:7e:eb:bf:dd:be:86
SHA1 : 8e:f3:78:b0:34:a6:c1:6a:7b:c6:f5:91:eb:e5:46:9b:0d:0a:a7:ba (jvN4sDSmwWp7xvWR6+VGmw0Kp7o)
Root CA cert is untrusted, valid until Aug 12 15:51:34 2032, 'myca.crt'
Sub CA cert "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
serial: 74:f9:7e:72:7d:b8:fd:f2:c6:e5:1b:fa:37:f9:cb:87:bf:9c:ea:e2
SHA256: a3:5b:4b:12:d5:8f:68:7b:05:11:08:27:f5:42:62:b8:b5:01:1b:19:37:9c:28:78:5d:37:08:69:6a:8c:07:bf
SHA1 : 8c:e6:67:67:c2:23:89:7b:d0:bc:b1:50:d2:1c:bc:8d:8d:69:15:11 (jOZnZ8IjiXvQvLFQ0hy8jY1pFRE)
using certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 0
Sub CA cert is trusted, valid until Aug 12 15:51:34 2027, 'mycacert-1.crt'
RA cert "C=CH, O=strongSwan Project, CN=SCEP RA"
serial: 74:f9:7e:72:7d:b8:fd:f2:c6:e5:1b:fa:37:f9:cb:87:bf:9c:ea:e3
SHA256: 57:22:f3:13:69:2f:24:82:12:59:8e:05:63:0b:f5:a8:fb:4e:78:87:8d:68:d1:4c:c1:c4:b5:85:db:bb:64:df
SHA1 : bc:d1:46:76:55:7f:8c:d1:c5:22:31:b9:d7:b1:49:b5:95:a4:f3:ea (vNFGdlV/jNHFIjG517FJtZWk8+o)
using certificate "C=CH, O=strongSwan Project, CN=SCEP RA"
using untrusted intermediate certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
reached self-signed root ca with a path length of 1
RA cert is trusted, valid until Aug 10 15:51:34 2023, 'myra.crt'
.EE
.PP
The trusthworthiness of the root CA certificate has to be established manually by
verifying the SHA256 or SHA1 fingerprint of the DER-encoded certificate that is
e.g. listed on the official PKI website or by some other means.
.P
The stored certificate files in DER format can be overwritten by PEM-encoded
versions with:
.PP
.EX
pki \-\-scepca \-\-url http://pki.strongswan.org:8080/scep \-\-caout myca.crt \-\-raout myra.crt \\
\-\-outform pem \-\-force
.EE
.PP
If the
.B \-\-raout
option is omitted and the
.B \-\-caout
template doesn't have a file suffix, then with
.B \-\-outform
\fIpem\fR the following filenames are derived:
.PP
.EX
pki \-\-scepca \-\-url http://pki.strongswan.org:8080/scep \-\-caout scep/myca \-\-outform pem
Root CA cert "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
...
Root CA cert is untrusted, valid until Aug 12 15:51:34 2032, written to 'scep/myca.pem'
Sub CA cert "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA"
...
Sub CA cert is trusted, valid until Aug 12 15:51:34 2027, 'mycacert-1.crt'
RA cert "C=CH, O=strongSwan Project, CN=SCEP RA"
...
RA cert is trusted, valid until Aug 10 15:51:34 2023, 'myca-ra.crt'
.EE
.PP
A CA certificate bundle in PEM format is written to \fIstdout\fR:
.PP
.EX
pki \-\-scepca \-\-url http://pki.strongswan.org:8080/scep --raout myra.crt \-\-outform pem > cacerts.pem
.EE
.PP
.
.SH "SEE ALSO"
.
.BR pki (1)
+28 -2
View File
@@ -1,4 +1,4 @@
.TH PKI 1 "2015-08-06" "@PACKAGE_VERSION@" "strongSwan"
.TH PKI 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
.
.SH "NAME"
.
@@ -30,6 +30,16 @@ private key of a CA and containing subjectAltNames, CRL distribution points
and URIs of OCSP servers. You can also extract raw public keys from private
keys, certificate requests and certificates and compute two kinds of SHA-1-based
key IDs.
.P
The
.B pki
command now supports certificate enrollment via the
.B Simple Certificate Enrollment Protocol
(SCEP) as defined by RFC 8894, replacing the obsoleted
.B ipsec scepclient
tool. Additionally the
.B Enrollment over Secure Transport
(EST) protocol (RFC 7030) is supported, too.
.
.SH "COMMANDS"
.
@@ -72,6 +82,18 @@ Extract a public key from a private key or certificate.
.TP
.B "\-v, \-\-verify"
Verify a certificate using a CA certificate.
.TP
.B "\-S, \-\-scep"
Enroll an X.509 certificate with a SCEP server.
.TP
.B "\-C, \-\-scepca"
Get CA [and RA] certificate[s] from a SCEP server.
.TP
.B "\-E, \-\-est"
Enroll an X.509 certificate with an EST server.
.TP
.B "\-e, \-\-estca"
Get CA certificate[s] from an EST server.
.
.SH "EXAMPLES"
.
@@ -161,4 +183,8 @@ certificates with the \-\-crl option.
.BR pki\ \-\-print (1),
.BR pki\ \-\-dn (1),
.BR pki\ \-\-pub (1),
.BR pki\ \-\-verify (1)
.BR pki\ \-\-verify (1),
.BR pki\ \-\-scep (1)
.BR pki\ \-\-scepca (1)
.BR pki\ \-\-est (1)
.BR pki\ \-\-estca (1)
+522
View File
@@ -0,0 +1,522 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include "pki.h"
#include "pki_cert.h"
#include <credentials/certificates/certificate.h>
#include <credentials/certificates/x509.h>
#include <credentials/containers/pkcs7.h>
/*
* Certificate types
*/
typedef enum {
CERT_TYPE_ROOT_CA,
CERT_TYPE_SUB_CA,
CERT_TYPE_RA
} pki_cert_type_t;
static char *cert_type_label[] = { "Root CA", "Sub CA", "RA" };
/**
* Determine certificate type based on X.509 certificate flags
*/
static pki_cert_type_t get_pki_cert_type(certificate_t *cert)
{
x509_t *x509;
x509_flag_t flags;
x509 = (x509_t*)cert;
flags = x509->get_flags(x509);
if (flags & X509_CA)
{
if (flags & X509_SELF_SIGNED)
{
return CERT_TYPE_ROOT_CA;
}
else
{
return CERT_TYPE_SUB_CA;
}
}
else
{
return CERT_TYPE_RA;
}
}
/**
* Output cert type, subject as well as SHA256 and SHA1 fingerprints
*/
static bool print_cert_info(certificate_t *cert, pki_cert_type_t cert_type)
{
hasher_t *hasher = NULL;
char digest_buf[HASH_SIZE_SHA256];
char base64_buf[HASH_SIZE_SHA256];
chunk_t cert_digest = {digest_buf, HASH_SIZE_SHA256};
chunk_t cert_id, serial, encoding = chunk_empty;
x509_t *x509;
bool success = FALSE;
DBG1(DBG_APP, "%s cert \"%Y\"", cert_type_label[cert_type],
cert->get_subject(cert));
x509 = (x509_t*)cert;
serial = x509->get_serial(x509);
DBG1(DBG_APP, " serial: %#B", &serial);
if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
{
DBG1(DBG_APP, "could not get certificate encoding");
return FALSE;
}
/* SHA256 certificate digest */
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA256);
if (!hasher)
{
DBG1(DBG_APP, "could not create SHA256 hasher");
goto end;
}
if (!hasher->get_hash(hasher, encoding, digest_buf))
{
DBG1(DBG_APP, "could not compute SHA256 hash");
goto end;
}
hasher->destroy(hasher);
DBG1(DBG_APP, " SHA256: %#B", &cert_digest);
/* SHA1 certificate digest */
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher)
{
DBG1(DBG_APP, "could not create SHA1 hasher");
goto end;
}
if (!hasher->get_hash(hasher, encoding, digest_buf))
{
DBG1(DBG_APP, "could not compute SHA1 hash");
goto end;
}
cert_digest.len = HASH_SIZE_SHA1;
cert_id = chunk_to_base64(cert_digest, base64_buf);
DBG1(DBG_APP, " SHA1 : %#B (%.*s)", &cert_digest,
cert_id.len-1, cert_id.ptr);
success = TRUE;
end:
DESTROY_IF(hasher);
chunk_free(&encoding);
return success;
}
/**
* Build a CA or RA pathname
*/
static bool build_pathname(char **path, pki_cert_type_t cert_type,
int *cert_type_count, char *caout, char *raout,
cred_encoding_type_t form)
{
char *basename, *extension, *dot, *suffix;
int count, len;
bool number;
basename = caout;
extension = "";
suffix = (form == CERT_ASN1_DER) ? "der" : "pem";
count = cert_type_count[cert_type];
number = count > 1;
switch (cert_type)
{
default:
case CERT_TYPE_ROOT_CA:
if (count > 1)
{
extension = "-root";
}
break;
case CERT_TYPE_SUB_CA:
number = TRUE;
break;
case CERT_TYPE_RA:
if (raout)
{
basename = raout;
}
else
{
extension = "-ra";
}
break;
}
/* skip if no path is defined */
if (!basename)
{
*path = NULL;
return TRUE;
}
/* check for a file suffix */
dot = strrchr(basename, '.');
len = dot ? (dot - basename) : strlen(basename);
if (dot && (dot[1] != '\0'))
{
suffix = dot + 1;
}
if (number)
{
return asprintf(path, "%.*s%s-%d.%s", len, basename, extension,
count, suffix) > 0;
}
else
{
return asprintf(path, "%.*s%s.%s", len, basename, extension, suffix) > 0;
}
}
/**
* Write CA/RA certificate to file in DER or PEM format
*/
static bool write_cert(certificate_t *cert, pki_cert_type_t cert_type,
bool trusted, char *path, cred_encoding_type_t form,
bool force)
{
chunk_t encoding = chunk_empty;
time_t until;
bool written, valid;
if (path)
{
if (!cert->get_encoding(cert, form, &encoding))
{
DBG1(DBG_APP, "could not get certificate encoding");
return FALSE;
}
written = chunk_write(encoding, path, 0022, force);
chunk_free(&encoding);
if (!written)
{
DBG1(DBG_APP, "could not write cert file '%s': %s",
path, strerror(errno));
return FALSE;
}
}
else if (form == CERT_PEM)
{
if (!cert->get_encoding(cert, form, &encoding))
{
DBG1(DBG_APP, "could not get certificate encoding");
return FALSE;
}
printf("%.*s", encoding.len, encoding.ptr);
chunk_free(&encoding);
path = "stdout";
}
valid = cert->get_validity(cert, NULL, NULL, &until);
DBG1(DBG_APP, "%s cert is %strusted, %s %T, %s'%s'",
cert_type_label[cert_type], trusted ? "" : "un",
valid ? "valid until" : "invalid since", &until, FALSE,
path ? "written to " : "", path ? path : "not written");
return TRUE;
}
/**
* Extract X.509 CA [and SCEP RA] certificates from PKCS#7 container,
* check trust as well as validity and write to files
*/
bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
bool is_scep, cred_encoding_type_t form,
bool force)
{
container_t *container;
mem_cred_t *creds = NULL;
certificate_t *cert;
pki_cert_type_t cert_type;
bool written = FALSE, success = FALSE;
char *path;
int cert_type_count[] = { 0, 0, 0 };
creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &creds->set);
container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
BUILD_BLOB_ASN1_DER, data, BUILD_END);
if (!container)
{
if (is_scep)
{
/* no PKCS#7 encoded certificates, assume single root CA cert */
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_BLOB, data, BUILD_END);
if (!cert)
{
DBG1(DBG_APP, "could not parse single CA certificate");
goto end;
}
cert_type = get_pki_cert_type(cert);
cert_type_count[cert_type]++;
if (print_cert_info(cert, cert_type) &&
build_pathname(&path, cert_type, cert_type_count, caout, raout,
form))
{
written = write_cert(cert, cert_type, FALSE, path, form, force);
}
}
else
{
DBG1(DBG_APP, "did not receive a valid pkcs7 container");
goto end;
}
}
else
{
enumerator_t *enumerator;
pkcs7_t *pkcs7 = (pkcs7_t*)container;
certificate_t *cert_found;
enumerator_t *certs;
bool trusted;
enumerator = pkcs7->create_cert_enumerator(pkcs7);
while (enumerator->enumerate(enumerator, &cert))
{
trusted = FALSE;
cert_type = get_pki_cert_type(cert);
if (cert_type == CERT_TYPE_ROOT_CA)
{
if (!print_cert_info(cert, cert_type))
{
goto end;
}
/* same root CA as trusted TLS root CA already in cred set? */
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
KEY_ANY, cert->get_subject(cert), FALSE);
while (certs->enumerate(certs, &cert_found, NULL))
{
if (cert->equals(cert, cert_found))
{
DBG1(DBG_APP, "Root CA equals trusted TLS Root CA");
trusted = TRUE;
break;
}
else
{
DBG1(DBG_APP, "non-matching TLS Root CA of same name");
}
}
certs->destroy(certs);
/* otherwise trust in root CA has to be established manuallly */
if (!trusted)
{
creds->add_cert(creds, TRUE, cert->get_ref(cert));
trusted = FALSE;
}
cert_type_count[cert_type]++;
if (build_pathname(&path, cert_type, cert_type_count, caout,
raout, form))
{
written = write_cert(cert, cert_type, trusted, path, form,
force);
free(path);
}
if (!written)
{
break;
}
}
else
{
/* trust relative to root CA will be established in round 2 */
creds->add_cert(creds, FALSE, cert->get_ref(cert));
}
}
enumerator->destroy(enumerator);
if (!written)
{
goto end;
}
enumerator = pkcs7->create_cert_enumerator(pkcs7);
while (enumerator->enumerate(enumerator, &cert))
{
written = FALSE;
trusted = FALSE;
cert_type = get_pki_cert_type(cert);
if (cert_type != CERT_TYPE_ROOT_CA)
{
if (!print_cert_info(cert, cert_type))
{
break;
}
/* establish trust relative to root CA */
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
KEY_ANY, cert->get_subject(cert), FALSE);
while (certs->enumerate(certs, &cert_found, NULL))
{
if (cert->equals(cert, cert_found))
{
trusted = TRUE;
break;
}
else
{
DBG1(DBG_APP, "non-matching TLS Sub CA of same name");
}
}
certs->destroy(certs);
cert_type_count[cert_type]++;
if (build_pathname(&path, cert_type, cert_type_count, caout,
raout, form))
{
written = write_cert(cert, cert_type, trusted, path, form,
force);
free(path);
}
if (!written)
{
break;
}
}
}
enumerator->destroy(enumerator);
}
success = TRUE;
end:
/* cleanup */
lib->credmgr->remove_set(lib->credmgr, &creds->set);
creds->destroy(creds);
DESTROY_IF(container);
return success;
}
/**
* Extract an X.509 client certificates from PKCS#7 container
* check trust as well as validity and write to stdout
*/
bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form)
{
pkcs7_t *pkcs7;
container_t *container;
certificate_t *cert;
mem_cred_t *client_creds;
chunk_t cert_encoding = chunk_empty;
enumerator_t *enumerator;
bool stored = FALSE;
/* parse pkcs7 signed-data container */
container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
BUILD_BLOB_ASN1_DER, data, BUILD_END);
if (!container)
{
DBG1(DBG_APP, "could not parse pkcs7 signed-data container");
return FALSE;
}
lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
client_creds = mem_cred_create();
lib->credmgr->add_set(lib->credmgr, &client_creds->set);
/* store the end entity certificate */
pkcs7 = (pkcs7_t*)container;
enumerator = pkcs7->create_cert_enumerator(pkcs7);
while (enumerator->enumerate(enumerator, &cert))
{
x509_t *x509 = (x509_t*)cert;
certificate_t *cert_found = NULL;
enumerator_t *certs;
chunk_t serial;
time_t from, until;
bool trusted, valid;
if (!(x509->get_flags(x509) & X509_CA))
{
DBG1(DBG_APP, "Issued certificate \"%Y\"", cert->get_subject(cert));
serial = x509->get_serial(x509);
DBG1(DBG_APP, " serial: %#B", &serial);
if (stored)
{
DBG1(DBG_APP, "multiple certs received, only first stored");
continue;
}
/* establish trust relative to root CA */
client_creds->add_cert(client_creds, FALSE, cert->get_ref(cert));
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
KEY_ANY, cert->get_subject(cert), FALSE);
trusted = certs->enumerate(certs, &cert_found, NULL) &&
(cert_found == cert);
certs->destroy(certs);
valid = cert->get_validity(cert, NULL, &from, &until);
DBG1(DBG_APP, "Issued certificate is %strusted, "
"valid from %T until %T (currently %svalid)",
trusted ? "" : "not ", &from, FALSE, &until, FALSE,
valid ? "" : "not ");
if (!cert->get_encoding(cert, form, &cert_encoding))
{
DBG1(DBG_APP, "encoding certificate failed");
break;
}
set_file_mode(stdout, form);
stored = fwrite(cert_encoding.ptr, cert_encoding.len, 1, stdout) == 1;
chunk_free(&cert_encoding);
if (!stored)
{
DBG1(DBG_APP, "writing certificate failed");
break;
}
}
}
enumerator->destroy(enumerator);
container->destroy(container);
lib->credmgr->remove_set(lib->credmgr, &client_creds->set);
client_creds->destroy(client_creds);
return stored;
}
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup pki_cert pki_cert
* @{ @ingroup pki
*/
#ifndef _PKI_CERT
#define _PKI_CERT
#include <library.h>
#include <credentials/sets/mem_cred.h>
/**
* Extract X.509 CA [and SCEP RA] certificates from PKCS#7 container
* check trust as well as validity and write to files
*/
bool pki_cert_extract_cacerts(chunk_t data, char *caout, char *raout,
bool is_scep, cred_encoding_type_t form,
bool force);
/**
* Extract an X.509 client certificates from PKCS#7 container
* check trust as well as validity and write to stdout
*/
bool pki_cert_extract_cert(chunk_t data, cred_encoding_type_t form);
#endif /** PKI_CERT_H_ @}*/
+203 -175
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2005 Jan Hutter, Martin Willi
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
@@ -20,6 +21,7 @@
#include <library.h>
#include <utils/debug.h>
#include <utils/lexparser.h>
#include <asn1/asn1.h>
#include <asn1/asn1_parser.h>
#include <asn1/oid.h>
@@ -28,6 +30,12 @@
#include "scep.h"
static const char *operations[] = {
"PKIOperation",
"GetCACert",
"GetCACaps"
};
static const char *pkiStatus_values[] = { "0", "2", "3" };
static const char *pkiStatus_names[] = {
@@ -37,12 +45,13 @@ static const char *pkiStatus_names[] = {
"UNKNOWN"
};
static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
static const char *msgType_values[] = { "3", "17", "19", "20", "21", "22" };
static const char *msgType_names[] = {
"CertRep",
"RenewalReq",
"PKCSReq",
"GetCertInitial",
"CertPoll",
"GetCert",
"GetCRL",
"Unknown"
@@ -56,6 +65,20 @@ static const char *failInfo_reasons[] = {
"badCertId - No certificate could be identified matching the provided criteria"
};
static const char *caps_names[] = {
"AES",
"DES3",
"SHA-256",
"SHA-384",
"SHA-512",
"SHA-224",
"SHA-1",
"POSTPKIOperation",
"SCEPStandard",
"GetNextCACert",
"Renewal"
};
const scep_attributes_t empty_scep_attributes = {
SCEP_Unknown_MSG , /* msgType */
SCEP_UNKNOWN , /* pkiStatus */
@@ -66,136 +89,76 @@ const scep_attributes_t empty_scep_attributes = {
};
/**
* Extract X.501 attributes
* Parse CA Capabilities of SCEP server
*/
void extract_attributes(pkcs7_t *pkcs7, enumerator_t *enumerator,
scep_attributes_t *attrs)
uint32_t scep_parse_caps(chunk_t response)
{
chunk_t attr;
uint32_t caps_flags = 0;
chunk_t line;
if (pkcs7->get_attribute(pkcs7, OID_PKI_MESSAGE_TYPE, enumerator, &attr))
DBG2(DBG_APP, "CA Capabilities:");
while (fetchline(&response, &line))
{
scep_msg_t m;
int i;
for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
for (i = 0; i < countof(caps_names); i++)
{
if (strncmp(msgType_values[m], attr.ptr, attr.len) == 0)
if (strncaseeq(caps_names[i], line.ptr, line.len))
{
attrs->msgType = m;
DBG2(DBG_APP, " %s", caps_names[i]);
caps_flags |= (1 << i);
}
}
DBG2(DBG_APP, "messageType: %s", msgType_names[attrs->msgType]);
free(attr.ptr);
}
if (pkcs7->get_attribute(pkcs7, OID_PKI_STATUS, enumerator, &attr))
{
pkiStatus_t s;
for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
{
if (strncmp(pkiStatus_values[s], attr.ptr, attr.len) == 0)
{
attrs->pkiStatus = s;
}
}
DBG2(DBG_APP, "pkiStatus: %s", pkiStatus_names[attrs->pkiStatus]);
free(attr.ptr);
}
if (pkcs7->get_attribute(pkcs7, OID_PKI_FAIL_INFO, enumerator, &attr))
{
if (attr.len == 1 && *attr.ptr >= '0' && *attr.ptr <= '4')
{
attrs->failInfo = (failInfo_t)(*attr.ptr - '0');
}
if (attrs->failInfo != SCEP_unknown_REASON)
{
DBG1(DBG_APP, "failInfo: %s", failInfo_reasons[attrs->failInfo]);
}
free(attr.ptr);
}
pkcs7->get_attribute(pkcs7, OID_PKI_SENDER_NONCE, enumerator,
&attrs->senderNonce);
pkcs7->get_attribute(pkcs7, OID_PKI_RECIPIENT_NONCE, enumerator,
&attrs->recipientNonce);
pkcs7->get_attribute(pkcs7, OID_PKI_TRANS_ID, enumerator,
&attrs->transID);
return caps_flags;
}
/**
* Generates a unique fingerprint of the pkcs10 request
* by computing an MD5 hash over it
* Generate a transaction ID as the SHA-1 hash of the publicKeyInfo
* The transaction ID is also used as a unique serial number
*/
chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
bool scep_generate_transaction_id(public_key_t *public,
chunk_t *transId, chunk_t *serialNumber)
{
chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
hasher_t *hasher;
hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
if (!hasher || !hasher->get_hash(hasher, pkcs10, digest.ptr))
{
DESTROY_IF(hasher);
return chunk_empty;
}
hasher->destroy(hasher);
return chunk_to_hex(digest, NULL, FALSE);
}
/**
* Generate a transaction id as the MD5 hash of an public key
* the transaction id is also used as a unique serial number
*/
void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
chunk_t *serialNumber)
{
chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
chunk_t keyEncoding = chunk_empty, keyInfo;
hasher_t *hasher;
chunk_t digest;
int zeros = 0, msb_set = 0;
key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
asn1_bitstring("m", keyEncoding));
hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
if (!hasher || !hasher->get_hash(hasher, keyInfo, digest.ptr))
if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &digest))
{
memset(digest.ptr, 0, digest.len);
}
DESTROY_IF(hasher);
free(keyInfo.ptr);
/* the transaction ID is the fingerprint in hex format */
*transId = chunk_to_hex(digest, NULL, TRUE);
/* the serialNumber should be valid ASN1 integer content:
* remove leading zeros, add one if MSB is set (two's complement) */
while (zeros < digest.len)
{
if (digest.ptr[zeros])
/**
* the serial number must be a valid positive ASN.1 integer
* remove leading zeros, add one if MSB is set (two's complement)
*/
while (zeros < digest.len)
{
if (digest.ptr[zeros] & 0x80)
if (digest.ptr[zeros])
{
msb_set = 1;
if (digest.ptr[zeros] & 0x80)
{
msb_set = 1;
}
break;
}
break;
zeros++;
}
zeros++;
*serialNumber = chunk_alloc(digest.len - zeros + msb_set);
if (msb_set)
{
serialNumber->ptr[0] = 0x00;
}
memcpy(serialNumber->ptr + msb_set, digest.ptr + zeros,
digest.len - zeros);
return TRUE;
}
*serialNumber = chunk_alloc(digest.len - zeros + msb_set);
if (msb_set)
{
serialNumber->ptr[0] = 0x00;
}
memcpy(serialNumber->ptr + msb_set, digest.ptr + zeros,
digest.len - zeros);
/* the transaction id is the serial number in hex format */
*transID = chunk_to_hex(digest, NULL, TRUE);
return FALSE;
}
/**
* Builds a pkcs7 enveloped and signed scep request
* Builds a PKCS#7 enveloped and signed SCEP request
*/
chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
certificate_t *enc_cert, encryption_algorithm_t enc_alg,
@@ -339,99 +302,155 @@ static char* escape_http_request(chunk_t req)
/**
* Send a SCEP request via HTTP and wait for a response
*/
bool scep_http_request(const char *url, chunk_t msg, scep_op_t op,
bool http_get_request, u_int timeout, char *src,
chunk_t *response)
bool scep_http_request(const char *url, scep_op_t op, bool http_post,
chunk_t data, chunk_t *response, u_int *http_code)
{
int len;
status_t status;
char *complete_url = NULL;
const char *operation;
host_t *srcip = NULL;
uint32_t http_timeout = lib->settings->get_time(lib->settings,
"%s.scep.http_timeout", 30, lib->ns);
char *http_bind = lib->settings->get_str(lib->settings,
"%s.scep.http_bind", NULL, lib->ns);
if (http_bind)
{
srcip = host_create_from_string(http_bind, 0);
}
DBG2(DBG_APP, "sending scep request to '%s'", url);
/* initialize response */
*response = chunk_empty;
*http_code = 0;
if (src)
operation = operations[op];
switch (op)
{
srcip = host_create_from_string(src, 0);
}
case SCEP_PKI_OPERATION:
default:
if (http_post)
{
/* form complete url */
len = strlen(url) + 11 + strlen(operation) + 1;
complete_url = malloc(len);
snprintf(complete_url, len, "%s?operation=%s", url, operation);
DBG2(DBG_APP, "sending scep request to '%s'", url);
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_TIMEOUT, http_timeout,
FETCH_REQUEST_DATA, data,
FETCH_REQUEST_TYPE, "",
FETCH_REQUEST_HEADER, "Expect:",
FETCH_SOURCEIP, srcip,
FETCH_RESPONSE_CODE, http_code,
FETCH_END);
}
else /* HTTP_GET */
{
char *msg = escape_http_request(data);
if (op == SCEP_PKI_OPERATION)
{
const char operation[] = "PKIOperation";
/* form complete url */
len = strlen(url) + 20 + strlen(operation) + strlen(msg) + 1;
complete_url = malloc(len);
snprintf(complete_url, len, "%s?operation=%s&message=%s"
, url, operation, msg);
free(msg);
if (http_get_request)
{
char *escaped_req = escape_http_request(msg);
/* form complete url */
len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
complete_url = malloc(len);
snprintf(complete_url, len, "%s?operation=%s&message=%s"
, url, operation, escaped_req);
free(escaped_req);
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_HTTP_VERSION_1_0,
FETCH_TIMEOUT, timeout,
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_TIMEOUT, http_timeout,
FETCH_REQUEST_HEADER, "Pragma:",
FETCH_REQUEST_HEADER, "Host:",
FETCH_REQUEST_HEADER, "Accept:",
FETCH_SOURCEIP, srcip,
FETCH_RESPONSE_CODE, http_code,
FETCH_END);
}
else /* HTTP_POST */
}
break;
case SCEP_GET_CA_CERT:
case SCEP_GET_CA_CAPS:
{
/* form complete url */
len = strlen(url) + 11 + strlen(operation) + 1;
len = strlen(url) + 11 + strlen(operation) + 1;
complete_url = malloc(len);
snprintf(complete_url, len, "%s?operation=%s", url, operation);
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_HTTP_VERSION_1_0,
FETCH_TIMEOUT, timeout,
FETCH_REQUEST_DATA, msg,
FETCH_REQUEST_TYPE, "",
FETCH_REQUEST_HEADER, "Expect:",
FETCH_SOURCEIP, srcip,
FETCH_END);
}
}
else /* SCEP_GET_CA_CERT */
{
const char operation[] = "GetCACert";
int i;
/* escape spaces, TODO: complete URL escape */
for (i = 0; i < msg.len; i++)
{
if (msg.ptr[i] == ' ')
{
msg.ptr[i] = '+';
}
}
/* form complete url */
len = strlen(url) + 32 + strlen(operation) + msg.len + 1;
complete_url = malloc(len);
snprintf(complete_url, len, "%s?operation=%s&message=%.*s",
url, operation, (int)msg.len, msg.ptr);
status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
FETCH_HTTP_VERSION_1_0,
FETCH_TIMEOUT, timeout,
FETCH_TIMEOUT, http_timeout,
FETCH_SOURCEIP, srcip,
FETCH_RESPONSE_CODE, http_code,
FETCH_END);
}
}
DESTROY_IF(srcip);
free(complete_url);
return (status == SUCCESS);
}
err_t scep_parse_response(chunk_t response, chunk_t transID,
/**
* Extract X.501 attributes
*/
void extract_attributes(pkcs7_t *pkcs7, enumerator_t *enumerator,
scep_attributes_t *attrs)
{
chunk_t attr;
if (pkcs7->get_attribute(pkcs7, OID_PKI_MESSAGE_TYPE, enumerator, &attr))
{
scep_msg_t m;
for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
{
if (strncmp(msgType_values[m], attr.ptr, attr.len) == 0)
{
attrs->msgType = m;
}
}
DBG2(DBG_APP, "messageType: %s", msgType_names[attrs->msgType]);
free(attr.ptr);
}
if (pkcs7->get_attribute(pkcs7, OID_PKI_STATUS, enumerator, &attr))
{
pkiStatus_t s;
for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
{
if (strncmp(pkiStatus_values[s], attr.ptr, attr.len) == 0)
{
attrs->pkiStatus = s;
}
}
DBG2(DBG_APP, "pkiStatus: %s", pkiStatus_names[attrs->pkiStatus]);
free(attr.ptr);
}
if (pkcs7->get_attribute(pkcs7, OID_PKI_FAIL_INFO, enumerator, &attr))
{
if (attr.len == 1 && *attr.ptr >= '0' && *attr.ptr <= '4')
{
attrs->failInfo = (failInfo_t)(*attr.ptr - '0');
}
if (attrs->failInfo != SCEP_unknown_REASON)
{
DBG1(DBG_APP, "failInfo: %s", failInfo_reasons[attrs->failInfo]);
}
free(attr.ptr);
}
pkcs7->get_attribute(pkcs7, OID_PKI_SENDER_NONCE, enumerator,
&attrs->senderNonce);
pkcs7->get_attribute(pkcs7, OID_PKI_RECIPIENT_NONCE, enumerator,
&attrs->recipientNonce);
pkcs7->get_attribute(pkcs7, OID_PKI_TRANS_ID, enumerator,
&attrs->transID);
}
/**
* Parse PKCS#7 encoded SCEP response
*/
bool scep_parse_response(chunk_t response, chunk_t transID,
container_t **out, scep_attributes_t *attrs)
{
enumerator_t *enumerator;
@@ -439,16 +458,19 @@ err_t scep_parse_response(chunk_t response, chunk_t transID,
container_t *container;
auth_cfg_t *auth;
*out = NULL;
container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
BUILD_BLOB_ASN1_DER, response, BUILD_END);
if (!container)
{
return "error parsing the scep response";
DBG1(DBG_APP, "error parsing the scep response");
return FALSE;
}
if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
{
container->destroy(container);
return "scep response is not PKCS#7 signed-data";
DBG1(DBG_APP, "scep response is not PKCS#7 signed-data");
goto error;
}
enumerator = container->create_signature_enumerator(container);
@@ -459,16 +481,22 @@ err_t scep_parse_response(chunk_t response, chunk_t transID,
if (!chunk_equals(transID, attrs->transID))
{
enumerator->destroy(enumerator);
container->destroy(container);
return "transaction ID of scep response does not match";
DBG1(DBG_APP, "transaction ID of scep response does not match");
goto error;
}
}
enumerator->destroy(enumerator);
if (!verified)
{
container->destroy(container);
return "unable to verify PKCS#7 container";
DBG1(DBG_APP, "unable to verify PKCS#7 container");
goto error;
}
*out = container;
return NULL;
return TRUE;
error:
container->destroy(container);
return FALSE;
}
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2005 Jan Hutter, Martin Willi
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef _SCEP_H
#define _SCEP_H
#include <credentials/containers/pkcs7.h>
#include <credentials/certificates/certificate.h>
/* supported SCEP operation types */
typedef enum {
SCEP_PKI_OPERATION,
SCEP_GET_CA_CERT,
SCEP_GET_CA_CAPS
} scep_op_t;
/* SCEP pkiStatus values */
typedef enum {
SCEP_SUCCESS,
SCEP_FAILURE,
SCEP_PENDING,
SCEP_UNKNOWN
} pkiStatus_t;
/* SCEP messageType values */
typedef enum {
SCEP_CertRep_MSG,
SCEP_RenewalReq_MSG,
SCEP_PKCSReq_MSG,
SCEP_CertPoll_MSG,
SCEP_GetCert_MSG,
SCEP_GetCRL_MSG,
SCEP_Unknown_MSG
} scep_msg_t;
/* SCEP failure reasons */
typedef enum {
SCEP_badAlg_REASON = 0,
SCEP_badMessageCheck_REASON = 1,
SCEP_badRequest_REASON = 2,
SCEP_badTime_REASON = 3,
SCEP_badCertId_REASON = 4,
SCEP_unknown_REASON = 5
} failInfo_t;
/* SCEP attributes */
typedef struct {
scep_msg_t msgType;
pkiStatus_t pkiStatus;
failInfo_t failInfo;
chunk_t transID;
chunk_t senderNonce;
chunk_t recipientNonce;
} scep_attributes_t;
/* SCEP CA Capabilities */
typedef enum {
SCEP_CAPS_AES = 0,
SCEP_CAPS_DES3 = 1,
SCEP_CAPS_SHA256 = 2,
SCEP_CAPS_SHA384 = 3,
SCEP_CAPS_SHA512 = 4,
SCEP_CAPS_SHA224 = 5,
SCEP_CAPS_SHA1 = 6,
SCEP_CAPS_POSTPKIOPERATION = 7,
SCEP_CAPS_SCEPSTANDARD = 8,
SCEP_CAPS_GETNEXTCACERT = 9,
SCEP_CAPS_RENEWAL = 10
} scep_caps_t;
extern const scep_attributes_t empty_scep_attributes;
/**
* Parse SCEP CA Capabilities
*/
uint32_t scep_parse_caps(chunk_t response);
/**
* Generate a transaction ID as the SHA-1 hash of the publicKeyInfo
* The transaction ID is also used as a unique serial number
*/
bool scep_generate_transaction_id(public_key_t *key,
chunk_t *transId, chunk_t *serialNumber);
/**
* Builds a PKCS#7 enveloped and signed SCEP request
*/
chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
certificate_t *enc_cert, encryption_algorithm_t enc_alg,
size_t key_size, certificate_t *signer_cert,
hash_algorithm_t digest_alg, private_key_t *private_key);
/**
* Send a SCEP request via HTTP and wait for a response
*/
bool scep_http_request(const char *url, scep_op_t op, bool http_post,
chunk_t data, chunk_t *response, u_int *http_code);
/**
* Parse PKCS#7 encoded SCEP response
*/
bool scep_parse_response(chunk_t response, chunk_t transID, container_t **out,
scep_attributes_t *attrs);
#endif /* _SCEP_H */
-1
View File
@@ -1 +0,0 @@
scepclient
-28
View File
@@ -1,28 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# copy-n-paste from Makefile.am
scepclient_SOURCES := \
scepclient.c scep.c scep.h
LOCAL_SRC_FILES := $(filter %.c,$(scepclient_SOURCES))
# build scepclient -------------------------------------------------------------
LOCAL_C_INCLUDES += \
$(strongswan_PATH)/src/libstrongswan
LOCAL_CFLAGS := $(strongswan_CFLAGS) \
-DPLUGINS='"$(strongswan_SCEPCLIENT_PLUGINS)"'
LOCAL_MODULE := scepclient
LOCAL_MODULE_TAGS := optional
LOCAL_ARM_MODE := arm
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES += libstrongswan
include $(BUILD_EXECUTABLE)
-16
View File
@@ -1,16 +0,0 @@
ipsec_PROGRAMS = scepclient
scepclient_SOURCES = \
scepclient.c scep.c scep.h
scepclient.o : $(top_builddir)/config.status
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-DIPSEC_CONFDIR=\"${sysconfdir}\" \
-DPLUGINS=\""${scepclient_plugins}\""
scepclient_LDADD = \
$(top_builddir)/src/libstrongswan/libstrongswan.la
dist_man_MANS = scepclient.8
EXTRA_DIST = Android.mk
+18
View File
@@ -0,0 +1,18 @@
# ipsec scepclient #
## Description ##
The `ipsec scepclient` tool was an early client implementation of the
_Simple Certificate Enrollment Protocol_ (SCEP).
The tool was written in 2005 and only got marginal updates since then. Hence it
implemented an old version of the SCEP Internet Draft (version 10/11 of
`draft-nourse-scep` and used the broken `MD5` hash and single `DES` encryption
algorithms as defaults.
## Obsolescence ##
With strongSwan version 5.9.8 `*ipsec scepclient*` has been removed and replaced
by the `pki` subcommands `pki --scep` and `pki --scepca` which implement the new
SCEP RFC 8894 standard that was released in September 2020 and which supports
trusted **certificate renewal** based on the existing client certificate.
-88
View File
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2005 Jan Hutter, Martin Willi
*
* Copyright (C) secunet Security Networks AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef _SCEP_H
#define _SCEP_H
#include <credentials/containers/pkcs7.h>
#include <credentials/certificates/certificate.h>
/* supported SCEP operation types */
typedef enum {
SCEP_PKI_OPERATION,
SCEP_GET_CA_CERT
} scep_op_t;
/* SCEP pkiStatus values */
typedef enum {
SCEP_SUCCESS,
SCEP_FAILURE,
SCEP_PENDING,
SCEP_UNKNOWN
} pkiStatus_t;
/* SCEP messageType values */
typedef enum {
SCEP_CertRep_MSG,
SCEP_PKCSReq_MSG,
SCEP_GetCertInitial_MSG,
SCEP_GetCert_MSG,
SCEP_GetCRL_MSG,
SCEP_Unknown_MSG
} scep_msg_t;
/* SCEP failure reasons */
typedef enum {
SCEP_badAlg_REASON = 0,
SCEP_badMessageCheck_REASON = 1,
SCEP_badRequest_REASON = 2,
SCEP_badTime_REASON = 3,
SCEP_badCertId_REASON = 4,
SCEP_unknown_REASON = 5
} failInfo_t;
/* SCEP attributes */
typedef struct {
scep_msg_t msgType;
pkiStatus_t pkiStatus;
failInfo_t failInfo;
chunk_t transID;
chunk_t senderNonce;
chunk_t recipientNonce;
} scep_attributes_t;
extern const scep_attributes_t empty_scep_attributes;
bool parse_attributes(chunk_t blob, scep_attributes_t *attrs);
void scep_generate_transaction_id(public_key_t *key,
chunk_t *transID,
chunk_t *serialNumber);
chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10);
chunk_t scep_transId_attribute(chunk_t transaction_id);
chunk_t scep_messageType_attribute(scep_msg_t m);
chunk_t scep_senderNonce_attribute(void);
chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
certificate_t *enc_cert, encryption_algorithm_t enc_alg,
size_t key_size, certificate_t *signer_cert,
hash_algorithm_t digest_alg, private_key_t *private_key);
bool scep_http_request(const char *url, chunk_t msg, scep_op_t op,
bool http_get_request, u_int timeout, char *src,
chunk_t *response);
err_t scep_parse_response(chunk_t response, chunk_t transID,
container_t **out, scep_attributes_t *attrs);
#endif /* _SCEP_H */
-293
View File
@@ -1,293 +0,0 @@
.\"
.TH "IPSEC_SCEPCLIENT" "8" "2012-05-11" "strongSwan" ""
.SH "NAME"
ipsec scepclient \- Client for the SCEP protocol
.SH "SYNOPSIS"
.B ipsec scepclient [argument ...]
.sp
.B ipsec scepclient
.B \-\-help
.br
.B ipsec scepclient
.B \-\-version
.SH "DESCRIPTION"
.BR scepclient
is a client implementation of Cisco System's Simple Certificate Enrollment Protocol (SCEP) written for Linux strongSwan <http://www.strongswan.org>.
.BR scepclient
is designed to be used for certificate enrollment on machines using the OpenSource IPsec solution
.I strongSwan.
.SH "FEATURES"
.BR scepclient
implements the following features of SCEP:
.br
.IP "\-" 4
Automatic enrollment of client certificate using a preshared secret
.IP "\-" 4
Manual enrollment of client certificate. Offline fingerprint check required!
.IP "\-" 4
Acquisition of CA certificate(s)
.SH "OPTIONS"
.SS Basic Startup Options
.B \-v, \-\-version
.RS 4
Display the version of ipsec scepclient.
.PP
.RE
.B \-h, \-\-help
.RS 4
Display usage of ipsec scepclient.
.RE
.SS General Options
.B \-u, \-\-url \fIurl\fP
.RS 4
Full HTTP URL of the SCEP server to be used for certificate enrollment and CA certificate acquisition.
.RE
.PP
.B \-+, \-\-optionsfrom \fIfilename\fP
.RS 4
Reads additional options from \fIfilename\fP.
.RE
.PP
.B \-f, \-\-force
.RS 4
Overwrite existing output file[s].
.RE
.PP
.B \-q, \-\-quiet
.RS 4
Do not write log output to stderr.
.RE
.SS Options for CA Certificate Acquisition
.B \-o, \-\-out cacert[=\fIfilename\fP]
.RS 4
Output file of acquired CA certificate. If more then one CA certificate is
available, \fIfilename\fP is used as prefix for the resulting files (refer to
EXAMPLES below for details).
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
.RE
.SS Options For Certificate Enrollment
.B \-i, \-\-in \fItype\fP[=\fIfilename\fP]
.RS 4
Input file for certificate enrollment. This option can be specified multiple times to specify input files for every \fItype\fP.
Input files can be either DER or PEM encoded.
.PP
Supported values for \fItype\fP:
.IP "\fBpkcs1\fP" 12
RSA private key in PKCS#1 file format. If no input of this type is specified, a RSA key gets generated.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/private/myKey.der.
.IP "\fBpkcs10\fP" 12
PKCS#10 certificate request to be used in the SCEP request. If no input of this type is specified, a request is generated.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/req/myReq.der.
.IP "\fBcacert\-enc\fP" 12
CA certificate to encrypt the SCEP request. Has to be specified for certificate enrollment.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
.IP "\fBcacert\-sig\fP" 12
CA certificate to check signature of SCEP reply. Has to be specified for certificate enrollment.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
.IP "\fBcert-self\fP" 12
Certificate to be used in the SCEP request. If it is not specified a
self-signed certificate is generated automatically.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/certs/selfCert.der.
.RE
.PP
.B \-k, \-\-keylength \fIbits\fP
.RS 4
sets the key length for RSA key generation. The default length for a generated rsa key is set to 2048 bit.
.RE
.PP
.B \-D, \-\-days \fIdays\fP
.RS 4
Validity of the self-signed X.509 certificate in days. The default is 1825 days (5 years).
.RE
.PP
.B \-S, \-\-startdate \fIYYMMDDHHMMSS\fPZ
.RS 4
defines the \fBnotBefore\fP date when the X.509 certificate becomes valid.
The date has the format \fIYYMMDDHHMMSS\fP and must be specified in UTC (Zulu time).
If the \fB--startdate\fP option is not specified then the current date is taken as a default.
.RE
.PP
.B \-E, \-\-enddate \fIYYMMDDHHMMSS\fPZ
.RS 4
defines the \fBnotAfter\fP date when the X.509 certificate will expire.
The date has the format \fIYYMMDDHHMMSS\fP and must be specified in UTC (Zulu time).
If the \fB--enddate\fP option is not specified then the default \fBnotAfter\fP value is computed by
adding the validity interval specified by the \fB--days\fP option to the \fBnotBefore\fP date.
.RE
.PP
.B \-d, \-\-dn \fIdn\fP
.RS 4
Distinguished name as comma separated list of relative distinguished names. Use quotation marks for a distinguished name containing spaces. If the \fB\-\-dn\fP parameter is missing then the default "C=CH, O=Linux strongSwan, CN=\fIhostname\fP"
is used with \fIhostname\fP being the return value of the \fIgethostname\fP() function.
.RE
.PP
.B \-s, \-\-subjectAltName \fItype\fP=\fIvalue\fP
.RS 4
Include subjectAltName in certificate request. This option can be specified multiple times to specify a subjectAltName
for every \fItype\fP.
.PP
Supported values for \fItype\fP:
.IP "\fBemail\fP" 12
subjectAltName is a email address.
.IP "\fBdns\fP" 12
subjectAltName is a hostname.
.IP "\fBip\fP" 12
subjectAltName is a IP address.
.RE
.PP
.B \-p, \-\-password \fIpw\fP
.RS 4
Password to be included as a \fIchallenge password\fP in SCEP request.
If \fIpw\fP is \fB%prompt\fP', the password gets prompted for on the command line.
.IP
\- In automatic mode, this password corresponds to the preshared secret for the given enrollment.
.IP
\- In manual mode, this password can be used to later revoke the corresponding certificate.
.RE
.PP
.B \-a, \-\-algorithm [\fItype\fP=]\fIalgo\fP
.RS 4
Change the algorithms to be used when generating and transporting (PKCS#7)
certificate requests (PKCS#10).
.PP
Supported values for \fItype\fP:
.IP "\fBenc\fP" 12
symmetric encryption algorithm in PKCS#7
.IP "\fBdgst\fP" 12
hash algorithm for message digest in PKCS#7
.IP "\fBsig\fP" 12
hash algorithm for the signature in PKCS#10
.PP
If \fItype\fP is not specified \fBenc\fP is assumed.
.PP
Supported values for \fIalgo\fP (\fBenc\fP):
.IP "\fBdes\fP" 12
DES-CBC encryption (key size = 56 bit). Default.
.IP "\fB3des\fP" 12
Triple DES-EDE-CBC encryption (key size = 168 bit).
.IP "\fBaes128\fP" 12
AES-CBC encryption (key size = 128 bit).
.IP "\fBaes192\fP" 12
AES-CBC encryption (key size = 192 bit).
.IP "\fBaes256\fP" 12
AES-CBC encryption (key size = 256 bit).
.IP "\fBcamellia128\fP" 12
Camellia-CBC encryption (key size = 128 bit).
.IP "\fBcamellia192\fP" 12
Camellia-CBC encryption (key size = 192 bit).
.IP "\fBcamellia256\fP" 12
Camellia-CBC encryption (key size = 256 bit).
.PP
Supported values for \fIalgo\fP (\fBdgst\fP or \fBsig\fP):
.PP
\fBmd5\fP (default), \fBsha1\fP, \fBsha256\fP, \fBsha384\fP, \fBsha512\fP
.RE
.PP
.B \-o, \-\-out \fItype\fP[=\fIfilename\fP]
.RS 4
Output file for certificate enrollment. This option can be specified multiple times to specify output files for every \fItype\fP.
.PP
Supported values for \fItype\fP:
.IP "\fBpkcs1\fP" 12
RSA private key in PKCS#1 file format. If specified, the RSA key used for enrollment is stored in file \fIfilename\fP.
If none of the \fItypes\fP listed below are specified, \fBscepclient\fP will stop after outputting this file.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/private/myKey.der.
.IP "\fBpkcs10\fP" 12
PKCS#10 certificate request. If specified, the PKCS#10 request used or certificate enrollment is stored in file \fIfilename\fP.
If none of the \fItypes\fP listed below are specified, \fBscepclient\fP will stop after outputting this file.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/req/myReq.der.
.IP "\fBpkcs7\fP" 12
PKCS#7 SCEP request as it is sent using HTTP to the SCEP server. If specified, this SCEP request is stored in file \fIfilename\fP.
If none of \fItypes\fP listed below is not specified, \fBscepclient\fP will stop after outputting this file.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/req/pkcs7.der.
.IP "\fBcert-self\fP" 12
Self-signed certificate. If specified the self-signed certificate is stored in file \fIfilename\fP.
.br
The default \fIfilename\fP is $CONFDIR/ipsec.d/certs/selfCert.der.
.IP "\fBcert\fP" 12
Enrolled certificate. This \fItype\fP must be specified for certificate enrollment.
The enrolled certificate is stored in file \fIfilename\fP.
.br
The default \fIfilename\fP is set to $CONFDIR/ipsec.d/certs/myCert.der.
.RE
.PP
.B \-m, \-\-method \fImethod\fP
.RS 4
Change HTTP request method for certificate enrollment. Default is \fBget\fP.
.PP
Supported values for \fImethod\fP:
.IP "\fBpost\fP" 12
Certificate enrollment using HTTP POST. Must be supported by the given SCEP server.
.IP "\fBget\fP" 12
Certificate enrollment using HTTP GET.
.RE
.PP
.B \-t, \-\-interval \fIseconds\fP
.RS 4
Set interval time in seconds when polling in manual mode.
The default interval is set to 5 seconds.
.RE
.PP
.B \-x, \-\-maxpolltime \fIseconds\fP
.RS 4
Set max time in seconds to poll in manual mode.
The default max time is set to unlimited.
.RE
.SS Debugging Output Options:
.B \-l, \-\-debug \fIlevel\fP
.RS 4
Changes the log level (-1..4, default: 1)
.RE
.SH "EXAMPLES"
.B ipsec scepclient \-\-out caCert \-\-url http://scepserver/cgi\-bin/pkiclient.exe \-f
.RS 4
Acquire CA certificate from SCEP server and store it in the default file $CONFDIR/ipsec.d/cacerts/caCert.der.
If more then one CA certificate is returned, store them in files named
\'caCert\-1.der\', \'caCert\-2.der\', etc.
If an RA certificate is returned, store it in a file named \'caCert\-ra.der\'.
If more than one RA certificate is returned, store them in files named
\'caCert\-ra\-1.der\', \'caCert\-ra\-2.der\', etc.
.RE
.PP
.B ipsec scepclient \-\-out pkcs1=joeKey.der \-k 1024
.RS 4
Generate RSA private key with key length of 1024 bit and store it in file joeKey.der.
.RE
.PP
.B ipsec scepclient \-\-in pkcs1=joeKey.der \-\-out pkcs10=joeReq.der \e
.br
.B \-\-dn \*(rqC=AT, CN=John Doe\*(rq \-s [email protected] \-p mypassword
.RS 4
Generate a PKCS#10 request and store it in file joeReq.der. Use the RSA private key joeKey.der
created earlier to sign the PKCS#10\-Request. In addition to the distinguished name include a
email\-subjectAltName and a challenge password in the request.
.RE
.PP
.B ipsec scepclient \-\-out pkcs1=joeKey.der \-\-out cert==joeCert.der \e
.br
.B \-\-dn \*(rqC=CH, CN=John Doe\*(rq \-k 512 \-p 5xH2pnT7wq \e
.br
.B \-\-url http://scep.hsr.ch/cgi\-bin/pkiclient.exe \e
.br
.B \-\-in cacert\-enc=caCert.der \-\-in cacert\-sig=caCert.der
.RS 4
Generate a new RSA key for the request and store it in joeKey.der. Then enroll a certificate and store as joeCert.der.
The challenge password is '5xH2pnT7wq'. The encryption and signature check has to be made with the same CA certificate
caCert.der.
.RE
.SH "BUGS"
\fB\-\-optionsfrom\fP seems to have parsing problems reading option files containing strings in quotation marks.
File diff suppressed because it is too large Load Diff