Merge branch 'pki-scep'
This commit is contained in:
+11
-1
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -75,10 +75,6 @@ if USE_UPDOWN
|
||||
SUBDIRS += _updown
|
||||
endif
|
||||
|
||||
if USE_SCEPCLIENT
|
||||
SUBDIRS += scepclient
|
||||
endif
|
||||
|
||||
if USE_PKI
|
||||
SUBDIRS += pki
|
||||
endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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*
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -661,6 +661,7 @@ static char *whitelist[] = {
|
||||
"botan_privkey_create",
|
||||
"botan_privkey_load_ecdh",
|
||||
"botan_privkey_load",
|
||||
"botan_privkey_load_rsa_pkcs1",
|
||||
"botan_kdf",
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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, ¤t, &auth))
|
||||
{
|
||||
found = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
|
||||
|
||||
+10
-2
@@ -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
@@ -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)
|
||||
|
||||
@@ -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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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"},
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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(¶meter, ':', &line) && eat_whitespace(&line))
|
||||
{
|
||||
if (matchcase("Content-Length", ¶meter))
|
||||
{
|
||||
if (sscanf(line.ptr, "%u", &len) == 1)
|
||||
{
|
||||
*content_len = len;
|
||||
}
|
||||
}
|
||||
else if (matchcase("Content-Transfer-Encoding", ¶meter) &&
|
||||
matchcase("Base64", &line))
|
||||
{
|
||||
*base64 = TRUE;
|
||||
}
|
||||
else if (matchcase("Retry-After", ¶meter))
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 +0,0 @@
|
||||
scepclient
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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 */
|
||||
@@ -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
Reference in New Issue
Block a user