mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-04 00:00:14 -04:00
Merge branch 'ocsp-responder'
Implements a new --ocsp command for the pki tool that can produce OCSP responses based on information provided by a plugin. A first plugin that accesses the OpenXPKI database is also added. Closes strongswan/strongswan#1958
This commit is contained in:
commit
3197523bd5
@ -79,6 +79,7 @@ plugins = \
|
||||
plugins/lookip.opt \
|
||||
plugins/ntru.opt \
|
||||
plugins/openssl.opt \
|
||||
plugins/openxpki.opt \
|
||||
plugins/osx-attr.opt \
|
||||
plugins/p-cscf.opt \
|
||||
plugins/pkcs11.opt \
|
||||
|
4
conf/plugins/openxpki.opt
Normal file
4
conf/plugins/openxpki.opt
Normal file
@ -0,0 +1,4 @@
|
||||
charon.plugins.openxpki.database =
|
||||
Database URI connecting to the OpenXPKI **certificate** database. If it
|
||||
contains a password, make sure to adjust the permissions of the config
|
||||
file accordingly.
|
@ -178,6 +178,7 @@ ARG_DISBL_SET([pkcs12], [disable PKCS12 container support plugin.])
|
||||
ARG_DISBL_SET([pubkey], [disable RAW public key support plugin.])
|
||||
ARG_DISBL_SET([sshkey], [disable SSH key decoding plugin.])
|
||||
ARG_DISBL_SET([x509], [disable X509 certificate implementation plugin.])
|
||||
ARG_ENABL_SET([openxpki], [enable OCSP responder accessing OpenXPKI certificate database.])
|
||||
# fetcher/resolver plugins
|
||||
ARG_ENABL_SET([curl], [enable CURL fetcher plugin to fetch files via libcurl. Requires libcurl.])
|
||||
ARG_ENABL_SET([files], [enable simple file:// URI fetcher.])
|
||||
@ -1592,8 +1593,9 @@ 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])
|
||||
ADD_PLUGIN([sqlite], [s charon pool manager medsrv attest])
|
||||
ADD_PLUGIN([mysql], [s charon pki pool manager medsrv attest])
|
||||
ADD_PLUGIN([sqlite], [s charon pki pool manager medsrv attest])
|
||||
ADD_PLUGIN([openxpki], [s pki])
|
||||
ADD_PLUGIN([attr], [c charon])
|
||||
ADD_PLUGIN([attr-sql], [c charon])
|
||||
ADD_PLUGIN([load-tester], [c charon])
|
||||
@ -1728,6 +1730,7 @@ AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue)
|
||||
AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue)
|
||||
AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue)
|
||||
AM_CONDITIONAL(USE_PKCS12, test x$pkcs12 = xtrue)
|
||||
AM_CONDITIONAL(USE_OPENXPKI, test x$openxpki = xtrue)
|
||||
AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue)
|
||||
AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue)
|
||||
AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue)
|
||||
@ -2010,6 +2013,7 @@ AC_CONFIG_FILES([
|
||||
src/libstrongswan/plugins/pkcs7/Makefile
|
||||
src/libstrongswan/plugins/pkcs8/Makefile
|
||||
src/libstrongswan/plugins/pkcs12/Makefile
|
||||
src/libstrongswan/plugins/openxpki/Makefile
|
||||
src/libstrongswan/plugins/pgp/Makefile
|
||||
src/libstrongswan/plugins/dnskey/Makefile
|
||||
src/libstrongswan/plugins/sshkey/Makefile
|
||||
@ -2201,6 +2205,7 @@ AC_CONFIG_FILES([
|
||||
src/pki/man/pki---gen.1
|
||||
src/pki/man/pki---issue.1
|
||||
src/pki/man/pki---keyid.1
|
||||
src/pki/man/pki---ocsp.1
|
||||
src/pki/man/pki---pkcs12.1
|
||||
src/pki/man/pki---pkcs7.1
|
||||
src/pki/man/pki---print.1
|
||||
|
@ -25,6 +25,7 @@ credentials/keys/public_key.c credentials/keys/shared_key.c \
|
||||
credentials/keys/signature_params.c \
|
||||
credentials/certificates/certificate.c credentials/certificates/crl.c \
|
||||
credentials/certificates/ocsp_response.c credentials/certificates/x509.c \
|
||||
credentials/certificates/ocsp_single_response.c \
|
||||
credentials/certificates/certificate_printer.c \
|
||||
credentials/containers/container.c credentials/containers/pkcs12.c \
|
||||
credentials/credential_manager.c \
|
||||
|
@ -23,6 +23,7 @@ credentials/keys/public_key.c credentials/keys/shared_key.c \
|
||||
credentials/keys/signature_params.c \
|
||||
credentials/certificates/certificate.c credentials/certificates/crl.c \
|
||||
credentials/certificates/ocsp_response.c credentials/certificates/x509.c \
|
||||
credentials/certificates/ocsp_single_response.c \
|
||||
credentials/certificates/certificate_printer.c \
|
||||
credentials/containers/container.c credentials/containers/pkcs12.c \
|
||||
credentials/credential_manager.c \
|
||||
@ -91,7 +92,9 @@ credentials/keys/signature_params.h \
|
||||
credentials/certificates/certificate.h credentials/certificates/x509.h \
|
||||
credentials/certificates/ac.h credentials/certificates/crl.h \
|
||||
credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \
|
||||
credentials/certificates/ocsp_single_response.h \
|
||||
credentials/certificates/ocsp_response.h \
|
||||
credentials/certificates/ocsp_responder.h \
|
||||
credentials/certificates/pgp_certificate.h \
|
||||
credentials/certificates/certificate_printer.h \
|
||||
credentials/containers/container.h credentials/containers/pkcs7.h \
|
||||
@ -479,6 +482,13 @@ if MONOLITHIC
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_OPENXPKI
|
||||
SUBDIRS += plugins/openxpki
|
||||
if MONOLITHIC
|
||||
libstrongswan_la_LIBADD += plugins/openxpki/libstrongswan-openxpki.la
|
||||
endif
|
||||
endif
|
||||
|
||||
if USE_PGP
|
||||
SUBDIRS += plugins/pgp
|
||||
if MONOLITHIC
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Martin Willi
|
||||
* Copyright (C) 2016-2022 Andreas Steffen
|
||||
* Copyright (C) 2016-2023 Andreas Steffen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -78,5 +78,8 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END,
|
||||
"BUILD_EDDSA_PUB",
|
||||
"BUILD_EDDSA_PRIV_ASN1_DER",
|
||||
"BUILD_CRITICAL_EXTENSION",
|
||||
"BUILD_NONCE",
|
||||
"BUILD_OCSP_STATUS",
|
||||
"BUILD_OCSP_RESPONSES",
|
||||
"BUILD_END",
|
||||
);
|
||||
|
@ -165,6 +165,12 @@ enum builder_part_t {
|
||||
BUILD_EDDSA_PRIV_ASN1_DER,
|
||||
/** OID of an [unsupported] critical extension */
|
||||
BUILD_CRITICAL_EXTENSION,
|
||||
/** nonce needed for some security protocol */
|
||||
BUILD_NONCE,
|
||||
/** OCSP response status, ocsp_status_t */
|
||||
BUILD_OCSP_STATUS,
|
||||
/** enumerator_t over (ocsp_single_response_t *response) */
|
||||
BUILD_OCSP_RESPONSES,
|
||||
/** end of variable argument builder list */
|
||||
BUILD_END,
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2022 Andreas Steffen
|
||||
* Copyright (C) 2015-2023 Andreas Steffen
|
||||
* Copyright (C) 2010 Martin Willi
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
@ -19,6 +19,7 @@
|
||||
#include "credentials/certificates/x509.h"
|
||||
#include "credentials/certificates/crl.h"
|
||||
#include "credentials/certificates/ac.h"
|
||||
#include "credentials/certificates/ocsp_request.h"
|
||||
#include "credentials/certificates/ocsp_response.h"
|
||||
#include "credentials/certificates/pgp_certificate.h"
|
||||
|
||||
@ -474,6 +475,36 @@ static void print_ac(private_certificate_printer_t *this, ac_t *ac)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print OCSP request specific information
|
||||
*/
|
||||
static void print_ocsp_request(private_certificate_printer_t *this,
|
||||
ocsp_request_t *ocsp_request)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
chunk_t nonce, issuerNameHash, issuerKeyHash, serialNumber;
|
||||
hash_algorithm_t hashAlgorithm;
|
||||
FILE *f = this->f;
|
||||
|
||||
nonce = ocsp_request->get_nonce(ocsp_request);
|
||||
fprintf(f, " nonce: %#B\n", &nonce);
|
||||
|
||||
enumerator = ocsp_request->create_request_enumerator(ocsp_request);
|
||||
while (enumerator->enumerate(enumerator, &hashAlgorithm, &issuerNameHash,
|
||||
&issuerKeyHash, &serialNumber))
|
||||
{
|
||||
fprintf(f, " serial: %#B\n", &serialNumber);
|
||||
fprintf(f, " issuer: keyHash: %#B\n", &issuerKeyHash);
|
||||
fprintf(f, " nameHash: %#B\n", &issuerNameHash);
|
||||
if (hashAlgorithm != HASH_SHA1)
|
||||
{
|
||||
fprintf(f, " hashAlg: %#N\n",
|
||||
hash_algorithm_short_names, hashAlgorithm);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print OCSP response specific information
|
||||
*/
|
||||
@ -576,7 +607,8 @@ METHOD(certificate_printer_t, print, void,
|
||||
{
|
||||
fprintf(f, " subject: \"%Y\"\n", subject);
|
||||
}
|
||||
if (type != CERT_TRUSTED_PUBKEY && type != CERT_GPG)
|
||||
if (type != CERT_TRUSTED_PUBKEY && type != CERT_GPG &&
|
||||
type != CERT_X509_OCSP_REQUEST)
|
||||
{
|
||||
fprintf(f, " issuer: \"%Y\"\n", cert->get_issuer(cert));
|
||||
}
|
||||
@ -637,6 +669,9 @@ METHOD(certificate_printer_t, print, void,
|
||||
case CERT_X509_AC:
|
||||
print_ac(this, (ac_t*)cert);
|
||||
break;
|
||||
case CERT_X509_OCSP_REQUEST:
|
||||
print_ocsp_request(this, (ocsp_request_t*)cert);
|
||||
break;
|
||||
case CERT_X509_OCSP_RESPONSE:
|
||||
print_ocsp_response(this, (ocsp_response_t*)cert);
|
||||
break;
|
||||
@ -694,6 +729,9 @@ METHOD(certificate_printer_t, print_caption, void,
|
||||
case CERT_X509_CRL:
|
||||
caption = "X.509 CRL";
|
||||
break;
|
||||
case CERT_X509_OCSP_REQUEST:
|
||||
caption = "OCSP Request";
|
||||
break;
|
||||
case CERT_X509_OCSP_RESPONSE:
|
||||
caption = "OCSP Response";
|
||||
break;
|
||||
|
@ -39,6 +39,7 @@ typedef enum crl_reason_t crl_reason_t;
|
||||
# undef CRL_REASON_CA_COMPROMISE
|
||||
# undef CRL_REASON_AFFILIATION_CHANGED
|
||||
# undef CRL_REASON_SUPERSEDED
|
||||
# undef CRL_REASON_CESSATION_OF_OPERATION
|
||||
# undef CRL_REASON_CERTIFICATE_HOLD
|
||||
# undef CRL_REASON_REMOVE_FROM_CRL
|
||||
#endif
|
||||
@ -52,7 +53,7 @@ enum crl_reason_t {
|
||||
CRL_REASON_CA_COMPROMISE = 2,
|
||||
CRL_REASON_AFFILIATION_CHANGED = 3,
|
||||
CRL_REASON_SUPERSEDED = 4,
|
||||
CRL_REASON_CESSATION_OF_OPERATON = 5,
|
||||
CRL_REASON_CESSATION_OF_OPERATION = 5,
|
||||
CRL_REASON_CERTIFICATE_HOLD = 6,
|
||||
CRL_REASON_REMOVE_FROM_CRL = 8,
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Tobias Brunner
|
||||
* Copyright (C) 2008 Martin Willi
|
||||
* Copyright (C) 2023 Andreas Steffen, strongSec GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -43,6 +44,20 @@ struct ocsp_request_t {
|
||||
* @return nonce in the request (internal data)
|
||||
*/
|
||||
chunk_t (*get_nonce)(ocsp_request_t *this);
|
||||
|
||||
/**
|
||||
* Get the optional signer certificate.
|
||||
*
|
||||
* @return X.509 signer certificate
|
||||
*/
|
||||
certificate_t* (*get_signer_cert)(ocsp_request_t *this);
|
||||
|
||||
/**
|
||||
* Create an enumerator over the request list.
|
||||
*
|
||||
* @return enumerator over the request fields
|
||||
*/
|
||||
enumerator_t* (*create_request_enumerator)(ocsp_request_t *this);
|
||||
};
|
||||
|
||||
#endif /** OCSP_REQUEST_H_ @}*/
|
||||
|
56
src/libstrongswan/credentials/certificates/ocsp_responder.h
Normal file
56
src/libstrongswan/credentials/certificates/ocsp_responder.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 ocsp_responder ocsp_responder
|
||||
* @{ @ingroup certificates
|
||||
*/
|
||||
|
||||
#ifndef OCSP_RESPONDER_H_
|
||||
#define OCSP_RESPONDER_H_
|
||||
|
||||
#include <credentials/certificates/crl.h>
|
||||
|
||||
typedef struct ocsp_responder_t ocsp_responder_t;
|
||||
|
||||
/**
|
||||
* OCSP responder object.
|
||||
*/
|
||||
struct ocsp_responder_t {
|
||||
|
||||
/**
|
||||
* Check the status of a certificate given by its serial number
|
||||
*
|
||||
* @param cacert X.509 certificate of issuer CA
|
||||
* @param serial_number serial number of the certificate to be checked
|
||||
* @param revocation_time receives time of revocation, if revoked
|
||||
* @param reason receives reason of revocation, if revoked
|
||||
* @return certificate validation status
|
||||
*/
|
||||
cert_validation_t (*get_status)(ocsp_responder_t *this,
|
||||
certificate_t *cacert,
|
||||
chunk_t serial_number,
|
||||
time_t *revocation_time,
|
||||
crl_reason_t *revocation_reason);
|
||||
|
||||
/**
|
||||
* Destroy an ocsp_responder_t object.
|
||||
*/
|
||||
void (*destroy)(ocsp_responder_t *this);
|
||||
|
||||
};
|
||||
|
||||
#endif /** OCSP_RESPONDER_H_ @}*/
|
@ -88,9 +88,9 @@ struct ocsp_response_t {
|
||||
enumerator_t* (*create_cert_enumerator)(ocsp_response_t *this);
|
||||
|
||||
/**
|
||||
* Create an enumerator over the contained responses.
|
||||
* Create an enumerator over the contained single responses.
|
||||
*
|
||||
* @return enumerator over major response fields
|
||||
* @return enumerator over ocsp_single_response_t objects
|
||||
*/
|
||||
enumerator_t* (*create_response_enumerator)(ocsp_response_t *this);
|
||||
};
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "ocsp_single_response.h"
|
||||
|
||||
typedef struct private_ocsp_single_response_t private_ocsp_single_response_t;
|
||||
|
||||
/**
|
||||
* Private data of an ocsp_single_response object.
|
||||
*/
|
||||
struct private_ocsp_single_response_t {
|
||||
|
||||
/**
|
||||
* Public interface for this ocsp_single_response object.
|
||||
*/
|
||||
ocsp_single_response_t public;
|
||||
|
||||
/**
|
||||
* reference counter
|
||||
*/
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
METHOD(ocsp_single_response_t, get_ref, ocsp_single_response_t*,
|
||||
private_ocsp_single_response_t *this)
|
||||
{
|
||||
ref_get(&this->ref);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
METHOD(ocsp_single_response_t, destroy, void,
|
||||
private_ocsp_single_response_t *this)
|
||||
{
|
||||
if (ref_put(&this->ref))
|
||||
{
|
||||
free(this->public.issuerNameHash.ptr);
|
||||
free(this->public.issuerKeyHash.ptr);
|
||||
free(this->public.serialNumber.ptr);
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See header.
|
||||
*/
|
||||
ocsp_single_response_t *ocsp_single_response_create()
|
||||
{
|
||||
private_ocsp_single_response_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.hashAlgorithm = HASH_UNKNOWN,
|
||||
.status = VALIDATION_FAILED,
|
||||
.get_ref = _get_ref,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.ref = 1,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 ocsp_single_response ocsp_single_response
|
||||
* @{ @ingroup certificates
|
||||
*/
|
||||
|
||||
#ifndef OCSP_SINGLE_RESPONSE_H_
|
||||
#define OCSP_SINGLE_RESPONSE_H_
|
||||
|
||||
#include <credentials/certificates/x509.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
|
||||
typedef struct ocsp_single_response_t ocsp_single_response_t;
|
||||
|
||||
/**
|
||||
* Single response contained in OCSP response
|
||||
*/
|
||||
struct ocsp_single_response_t {
|
||||
|
||||
/**
|
||||
* Hash algorithm for the two hashes
|
||||
*/
|
||||
int hashAlgorithm;
|
||||
|
||||
/**
|
||||
* hash of issuer DN
|
||||
*/
|
||||
chunk_t issuerNameHash;
|
||||
|
||||
/**
|
||||
* issuerKeyID
|
||||
*/
|
||||
chunk_t issuerKeyHash;
|
||||
|
||||
/**
|
||||
* Serial number of certificate
|
||||
*/
|
||||
chunk_t serialNumber;
|
||||
|
||||
/**
|
||||
* OCSP certificate status
|
||||
*/
|
||||
cert_validation_t status;
|
||||
|
||||
/**
|
||||
* Time of revocation, if revoked
|
||||
*/
|
||||
time_t revocationTime;
|
||||
|
||||
/**
|
||||
* Revocation reason, if revoked
|
||||
*/
|
||||
crl_reason_t revocationReason;
|
||||
|
||||
/**
|
||||
* Creation of the OCSP single response
|
||||
*/
|
||||
time_t thisUpdate;
|
||||
|
||||
/**
|
||||
* Creation of next OCSP single response
|
||||
*/
|
||||
time_t nextUpdate;
|
||||
|
||||
/**
|
||||
* Get a new reference to the ocsp_single_response object.
|
||||
*
|
||||
* @return this, with an increased refcount
|
||||
*/
|
||||
ocsp_single_response_t* (*get_ref)(ocsp_single_response_t *this);
|
||||
|
||||
/**
|
||||
* Destroy an ocsp_single_response_t object.
|
||||
*/
|
||||
void (*destroy)(ocsp_single_response_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an ocsp_single_response_t object
|
||||
*
|
||||
* @return ocsp_single_response_t object
|
||||
*/
|
||||
ocsp_single_response_t *ocsp_single_response_create(void);
|
||||
|
||||
#endif /** OCSP_SINGLE_RESPONSE_H_ @}*/
|
17
src/libstrongswan/plugins/openxpki/Makefile.am
Normal file
17
src/libstrongswan/plugins/openxpki/Makefile.am
Normal file
@ -0,0 +1,17 @@
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/libstrongswan
|
||||
|
||||
AM_CFLAGS = \
|
||||
$(PLUGIN_CFLAGS)
|
||||
|
||||
if MONOLITHIC
|
||||
noinst_LTLIBRARIES = libstrongswan-openxpki.la
|
||||
else
|
||||
plugin_LTLIBRARIES = libstrongswan-openxpki.la
|
||||
endif
|
||||
|
||||
libstrongswan_openxpki_la_SOURCES = \
|
||||
openxpki_plugin.h openxpki_plugin.c \
|
||||
openxpki_ocsp_responder.h openxpki_ocsp_responder.c
|
||||
|
||||
libstrongswan_openxpki_la_LDFLAGS = -module -avoid-version
|
192
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c
Normal file
192
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 <library.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "openxpki_ocsp_responder.h"
|
||||
|
||||
typedef struct private_openxpki_ocsp_responder_t private_openxpki_ocsp_responder_t;
|
||||
|
||||
/**
|
||||
* Private Data of a openxpki_ocsp_responder_t object.
|
||||
*/
|
||||
struct private_openxpki_ocsp_responder_t {
|
||||
|
||||
/**
|
||||
* Public data
|
||||
*/
|
||||
openxpki_ocsp_responder_t public;
|
||||
|
||||
/**
|
||||
* OpenXPKI certificate database
|
||||
*/
|
||||
database_t *db;
|
||||
};
|
||||
|
||||
METHOD(ocsp_responder_t, get_status, cert_validation_t,
|
||||
private_openxpki_ocsp_responder_t *this, certificate_t *cacert,
|
||||
chunk_t serial_number, time_t *revocation_time, crl_reason_t *reason)
|
||||
{
|
||||
cert_validation_t validation = VALIDATION_FAILED;
|
||||
int rev_time;
|
||||
const int max_decimals = 49;
|
||||
const int max_bytes = max_decimals / 2.4083;
|
||||
char serial[max_decimals + 1], authKeyId[60];
|
||||
char *status, *reason_code;
|
||||
chunk_t subjectKeyId;
|
||||
public_key_t *public;
|
||||
enumerator_t *e;
|
||||
bool success;
|
||||
|
||||
/* convert serialNumber from binary to decimal as required for the DB query.
|
||||
* check for a potential overflow since the database table field supports
|
||||
* up to 49 decimal digits, only.
|
||||
*/
|
||||
if (serial_number.len > max_bytes)
|
||||
{
|
||||
DBG1(DBG_LIB, "serialNumber conversion exceeds %d decimals", max_decimals);
|
||||
return VALIDATION_FAILED;
|
||||
|
||||
}
|
||||
chunk_to_dec(serial_number, serial);
|
||||
|
||||
/* additionally use the subjectyKeyId of the issuing CA for the DB query */
|
||||
public = cacert->get_public_key(cacert);
|
||||
success = public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &subjectKeyId);
|
||||
public->destroy(public);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to extract subjectKeyId from CA certificate");
|
||||
return VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
/* the authKeyId of the certificate is the subjectKeyId of the issuing CA */
|
||||
snprintf(authKeyId, sizeof(authKeyId), "%#B", &subjectKeyId);
|
||||
|
||||
/* query the OpenXPKI certificate database */
|
||||
e = this->db->query(this->db, "SELECT status, reason_code, revocation_time "
|
||||
"FROM certificate WHERE cert_key = ? "
|
||||
"AND authority_key_identifier = ?", DB_TEXT, serial,
|
||||
DB_TEXT, authKeyId, DB_TEXT, DB_TEXT, DB_INT);
|
||||
if (e && e->enumerate(e, &status, &reason_code, &rev_time))
|
||||
{
|
||||
if (streq(status, "ISSUED") || streq(status, "ISSUANCE_PENDING"))
|
||||
{
|
||||
validation = VALIDATION_GOOD;
|
||||
}
|
||||
else if (streq(status, "UNKNOWN"))
|
||||
{
|
||||
validation = VALIDATION_FAILED;
|
||||
}
|
||||
else if (streq(status, "REVOKED") || streq(status, "HOLD") ||
|
||||
streq(status, "CRL_ISSUANCE_PENDING"))
|
||||
{
|
||||
validation = VALIDATION_REVOKED;
|
||||
|
||||
if (revocation_time)
|
||||
{
|
||||
*revocation_time = rev_time;
|
||||
}
|
||||
if (reason)
|
||||
{
|
||||
if (streq(reason_code, "unspecified"))
|
||||
{
|
||||
*reason = CRL_REASON_UNSPECIFIED;
|
||||
}
|
||||
else if (streq(reason_code, "keyCompromise"))
|
||||
{
|
||||
*reason = CRL_REASON_KEY_COMPROMISE;
|
||||
}
|
||||
else if (streq(reason_code, "CACompromise"))
|
||||
{
|
||||
*reason = CRL_REASON_CA_COMPROMISE;
|
||||
}
|
||||
else if (streq(reason_code, "affiliationChanged"))
|
||||
{
|
||||
*reason = CRL_REASON_AFFILIATION_CHANGED;
|
||||
}
|
||||
else if (streq(reason_code, "superseded"))
|
||||
{
|
||||
*reason = CRL_REASON_SUPERSEDED;
|
||||
}
|
||||
else if (streq(reason_code, "cessationOfOperation"))
|
||||
{
|
||||
*reason = CRL_REASON_CESSATION_OF_OPERATION;
|
||||
}
|
||||
else if (streq(reason_code, "certificateHold"))
|
||||
{
|
||||
*reason = CRL_REASON_CERTIFICATE_HOLD;
|
||||
validation = VALIDATION_ON_HOLD;
|
||||
}
|
||||
else if (streq(reason_code, "removeFromCRL"))
|
||||
{
|
||||
*reason = CRL_REASON_REMOVE_FROM_CRL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*reason = CRL_REASON_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DESTROY_IF(e);
|
||||
|
||||
return validation;
|
||||
}
|
||||
|
||||
METHOD(ocsp_responder_t, destroy, void,
|
||||
private_openxpki_ocsp_responder_t *this)
|
||||
{
|
||||
this->db->destroy(this->db);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* See header
|
||||
*/
|
||||
ocsp_responder_t *openxpki_ocsp_responder_create()
|
||||
{
|
||||
private_openxpki_ocsp_responder_t *this;
|
||||
database_t *db;
|
||||
char *uri;
|
||||
|
||||
uri = lib->settings->get_str(lib->settings,
|
||||
"%s.plugins.openxpki.database", NULL, lib->ns);
|
||||
if (!uri)
|
||||
{
|
||||
DBG1(DBG_CFG, "openxpki database URI missing");
|
||||
return NULL;
|
||||
}
|
||||
db = lib->db->create(lib->db, uri);
|
||||
if (!db)
|
||||
{
|
||||
DBG1(DBG_CFG, "opening openxpki database failed");
|
||||
return NULL;
|
||||
}
|
||||
INIT(this,
|
||||
.public = {
|
||||
.interface = {
|
||||
.get_status = _get_status,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
.db = db,
|
||||
);
|
||||
|
||||
return &this->public.interface;
|
||||
}
|
45
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h
Normal file
45
src/libstrongswan/plugins/openxpki/openxpki_ocsp_responder.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 openxpki_ocsp_responder openxpki_ocsp_responder
|
||||
* @{ @ingroup openxpki_p
|
||||
*/
|
||||
|
||||
#ifndef OPENXPKI_OCSP_RESPONDER_H_
|
||||
#define OPENXPKI_OCSP_RESPONDER_H_
|
||||
|
||||
typedef struct openxpki_ocsp_responder_t openxpki_ocsp_responder_t;
|
||||
|
||||
#include <credentials/certificates/ocsp_responder.h>
|
||||
|
||||
/**
|
||||
* OCSP responder implementation using OpenXPKI.
|
||||
*/
|
||||
struct openxpki_ocsp_responder_t {
|
||||
|
||||
/**
|
||||
* Implements ocsp_responder interface
|
||||
*/
|
||||
ocsp_responder_t interface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a openxpki_ocsp_responder instance.
|
||||
*/
|
||||
ocsp_responder_t *openxpki_ocsp_responder_create();
|
||||
|
||||
#endif /** OPENXPKI_OCSP_RESPONDER_H_ @}*/
|
104
src/libstrongswan/plugins/openxpki/openxpki_plugin.c
Normal file
104
src/libstrongswan/plugins/openxpki/openxpki_plugin.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 "openxpki_plugin.h"
|
||||
|
||||
#include "openxpki_ocsp_responder.h"
|
||||
|
||||
typedef struct private_openxpki_plugin_t private_openxpki_plugin_t;
|
||||
|
||||
/**
|
||||
* Private data of an openxpki_plugin_t object.
|
||||
*/
|
||||
struct private_openxpki_plugin_t {
|
||||
|
||||
/**
|
||||
* Public interface.
|
||||
*/
|
||||
openxpki_plugin_t public;
|
||||
|
||||
/**
|
||||
* OCSP responder
|
||||
*/
|
||||
ocsp_responder_t *ocsp_responder;
|
||||
};
|
||||
|
||||
METHOD(plugin_t, get_name, char*,
|
||||
private_openxpki_plugin_t *this)
|
||||
{
|
||||
return "openxpki";
|
||||
}
|
||||
|
||||
static bool plugin_cb(private_openxpki_plugin_t *this,
|
||||
plugin_feature_t *feature, bool reg, void *cb_data)
|
||||
{
|
||||
if (reg)
|
||||
{
|
||||
/* Is there already a registered OCSP responder? */
|
||||
if (!lib->get(lib, "ocsp-responder"))
|
||||
{
|
||||
this->ocsp_responder = openxpki_ocsp_responder_create();
|
||||
lib->set(lib, "ocsp-responder", this->ocsp_responder);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->ocsp_responder)
|
||||
{
|
||||
lib->set(lib, "ocsp-responder", NULL);
|
||||
this->ocsp_responder->destroy(this->ocsp_responder);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(plugin_t, get_features, int,
|
||||
private_openxpki_plugin_t *this, plugin_feature_t *features[])
|
||||
{
|
||||
static plugin_feature_t f[] = {
|
||||
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
|
||||
PLUGIN_PROVIDE(CUSTOM, "ocsp-responder"),
|
||||
PLUGIN_DEPENDS(DATABASE, DB_MYSQL),
|
||||
};
|
||||
*features = f;
|
||||
return countof(f);
|
||||
}
|
||||
|
||||
METHOD(plugin_t, destroy, void,
|
||||
private_openxpki_plugin_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
plugin_t *openxpki_plugin_create()
|
||||
{
|
||||
private_openxpki_plugin_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.plugin = {
|
||||
.get_name = _get_name,
|
||||
.get_features = _get_features,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public.plugin;
|
||||
}
|
43
src/libstrongswan/plugins/openxpki/openxpki_plugin.h
Normal file
43
src/libstrongswan/plugins/openxpki/openxpki_plugin.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 openxpki_p openxpki
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @defgroup openxpki_plugin openxpki_plugin
|
||||
* @{ @ingroup openxpki_p
|
||||
*/
|
||||
|
||||
#ifndef OPENXPKI_PLUGIN_H_
|
||||
#define OPENXPKI_PLUGIN_H_
|
||||
|
||||
#include <plugins/plugin.h>
|
||||
|
||||
typedef struct openxpki_plugin_t openxpki_plugin_t;
|
||||
|
||||
/**
|
||||
* Plugin implementing an OCSP responder based on OpenXPKI.
|
||||
*/
|
||||
struct openxpki_plugin_t {
|
||||
|
||||
/**
|
||||
* implements plugin interface
|
||||
*/
|
||||
plugin_t plugin;
|
||||
};
|
||||
|
||||
#endif /** OPENXPKI_PLUGIN_H_ @}*/
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Tobias Brunner
|
||||
* Copyright (C) 2008-2009 Martin Willi
|
||||
* Copyright (C) 2007-2022 Andreas Steffen
|
||||
* Copyright (C) 2007-2023 Andreas Steffen, strongSec GmbH
|
||||
* Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
@ -22,13 +22,15 @@
|
||||
#include <library.h>
|
||||
#include <asn1/oid.h>
|
||||
#include <asn1/asn1.h>
|
||||
#include <asn1/asn1_parser.h>
|
||||
#include <utils/identification.h>
|
||||
#include <collections/linked_list.h>
|
||||
#include <utils/debug.h>
|
||||
#include <credentials/certificates/x509.h>
|
||||
#include <credentials/keys/private_key.h>
|
||||
|
||||
#define NONCE_LEN 16
|
||||
/* RFC 8954 OCSP Nonce Extension */
|
||||
#define NONCE_LEN 32
|
||||
|
||||
typedef struct private_x509_ocsp_request_t private_x509_ocsp_request_t;
|
||||
|
||||
@ -43,9 +45,9 @@ struct private_x509_ocsp_request_t {
|
||||
x509_ocsp_request_t public;
|
||||
|
||||
/**
|
||||
* CA the candidates belong to
|
||||
* CA the certificates where issued by
|
||||
*/
|
||||
x509_t *ca;
|
||||
certificate_t *cacert;
|
||||
|
||||
/**
|
||||
* Requestor name, subject of cert used if not set
|
||||
@ -63,9 +65,9 @@ struct private_x509_ocsp_request_t {
|
||||
private_key_t *key;
|
||||
|
||||
/**
|
||||
* list of certificates to check, x509_t
|
||||
* list of X.509 certificates to check
|
||||
*/
|
||||
linked_list_t *candidates;
|
||||
linked_list_t *reqCerts;
|
||||
|
||||
/**
|
||||
* nonce used in request
|
||||
@ -77,12 +79,53 @@ struct private_x509_ocsp_request_t {
|
||||
*/
|
||||
chunk_t encoding;
|
||||
|
||||
/**
|
||||
* data for signature verification
|
||||
*/
|
||||
chunk_t tbsRequest;
|
||||
|
||||
/**
|
||||
* signature scheme
|
||||
*/
|
||||
signature_params_t *scheme;
|
||||
|
||||
/**
|
||||
* signature
|
||||
*/
|
||||
chunk_t signature;
|
||||
|
||||
/**
|
||||
* reference count
|
||||
*/
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* Single reqCert object sent in OCSP request
|
||||
*/
|
||||
typedef struct {
|
||||
/** hash algorithm for the two hashes */
|
||||
hash_algorithm_t hashAlgorithm;
|
||||
/** hash of issuer DN */
|
||||
chunk_t issuerNameHash;
|
||||
/** issuerKeyID */
|
||||
chunk_t issuerKeyHash;
|
||||
/** serial number of certificate */
|
||||
chunk_t serialNumber;
|
||||
} req_cert_t;
|
||||
|
||||
/**
|
||||
* Clean up a reqCert object
|
||||
*/
|
||||
CALLBACK(req_cert_destroy, void,
|
||||
req_cert_t *reqCert)
|
||||
{
|
||||
chunk_free(&reqCert->issuerNameHash);
|
||||
chunk_free(&reqCert->issuerKeyHash);
|
||||
chunk_free(&reqCert->serialNumber);
|
||||
free(reqCert);
|
||||
}
|
||||
|
||||
static const chunk_t ASN1_nonce_oid = chunk_from_chars(
|
||||
0x06, 0x09,
|
||||
0x2B, 0x06,
|
||||
@ -125,15 +168,15 @@ static chunk_t build_requestorName(private_x509_ocsp_request_t *this)
|
||||
* build Request, not using singleRequestExtensions
|
||||
*/
|
||||
static chunk_t build_Request(private_x509_ocsp_request_t *this,
|
||||
chunk_t issuerNameHash, chunk_t issuerKeyHash,
|
||||
chunk_t serialNumber)
|
||||
req_cert_t *reqCert)
|
||||
{
|
||||
return asn1_wrap(ASN1_SEQUENCE, "m",
|
||||
asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
asn1_algorithmIdentifier(OID_SHA1),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, issuerNameHash),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, issuerKeyHash),
|
||||
asn1_integer("c", serialNumber)));
|
||||
asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
asn1_algorithmIdentifier(
|
||||
hasher_algorithm_to_oid(reqCert->hashAlgorithm)),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, reqCert->issuerNameHash),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, reqCert->issuerKeyHash),
|
||||
asn1_integer("c", reqCert->serialNumber)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,57 +184,18 @@ static chunk_t build_Request(private_x509_ocsp_request_t *this,
|
||||
*/
|
||||
static chunk_t build_requestList(private_x509_ocsp_request_t *this)
|
||||
{
|
||||
chunk_t issuerNameHash, issuerKeyHash;
|
||||
identification_t *issuer;
|
||||
x509_t *x509;
|
||||
certificate_t *cert;
|
||||
chunk_t list = chunk_empty;
|
||||
public_key_t *public;
|
||||
chunk_t list = chunk_empty, request;
|
||||
enumerator_t *enumerator;
|
||||
req_cert_t *reqCert;
|
||||
|
||||
cert = (certificate_t*)this->ca;
|
||||
public = cert->get_public_key(cert);
|
||||
if (public)
|
||||
enumerator = this->reqCerts->create_enumerator(this->reqCerts);
|
||||
while (enumerator->enumerate(enumerator, &reqCert))
|
||||
{
|
||||
hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
if (hasher)
|
||||
{
|
||||
if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1,
|
||||
&issuerKeyHash))
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
|
||||
issuer = cert->get_subject(cert);
|
||||
if (hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
|
||||
&issuerNameHash))
|
||||
{
|
||||
enumerator = this->candidates->create_enumerator(
|
||||
this->candidates);
|
||||
while (enumerator->enumerate(enumerator, &x509))
|
||||
{
|
||||
chunk_t request, serialNumber;
|
||||
|
||||
serialNumber = x509->get_serial(x509);
|
||||
request = build_Request(this, issuerNameHash,
|
||||
issuerKeyHash, serialNumber);
|
||||
list = chunk_cat("mm", list, request);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
chunk_free(&issuerNameHash);
|
||||
}
|
||||
hasher->destroy(hasher);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_LIB, "creating OCSP request failed, SHA1 not supported");
|
||||
}
|
||||
public->destroy(public);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_LIB, "creating OCSP request failed, CA certificate has "
|
||||
"no public key");
|
||||
request = build_Request(this, reqCert);
|
||||
list = chunk_cat("mm", list, request);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
return asn1_wrap(ASN1_SEQUENCE, "m", list);
|
||||
}
|
||||
|
||||
@ -205,7 +209,7 @@ static chunk_t build_nonce(private_x509_ocsp_request_t *this)
|
||||
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
if (!rng || !rng->allocate_bytes(rng, NONCE_LEN, &this->nonce))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating OCSP request nonce failed, no RNG found");
|
||||
DBG1(DBG_LIB, "failed to create RNG");
|
||||
DESTROY_IF(rng);
|
||||
return chunk_empty;
|
||||
}
|
||||
@ -237,7 +241,7 @@ static chunk_t build_requestExtensions(private_x509_ocsp_request_t *this)
|
||||
}
|
||||
|
||||
/**
|
||||
* build tbsRequest
|
||||
* Build tbsRequest
|
||||
*/
|
||||
static chunk_t build_tbsRequest(private_x509_ocsp_request_t *this)
|
||||
{
|
||||
@ -298,7 +302,6 @@ static chunk_t build_optionalSignature(private_x509_ocsp_request_t *this,
|
||||
|
||||
/**
|
||||
* Build the OCSPRequest data
|
||||
*
|
||||
*/
|
||||
static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this)
|
||||
{
|
||||
@ -312,6 +315,187 @@ static chunk_t build_OCSPRequest(private_x509_ocsp_request_t *this)
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mm", tbsRequest, optionalSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of ocspRequest
|
||||
*/
|
||||
static const asn1Object_t ocspRequestObjects[] = {
|
||||
{ 0, "OCSPRequest", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
|
||||
{ 1, "tbsRequest", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */
|
||||
{ 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE |
|
||||
ASN1_DEF }, /* 2 */
|
||||
{ 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */
|
||||
{ 2, "requestorNameContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */
|
||||
{ 3, "requestorName", ASN1_CONTEXT_C_4, ASN1_BODY }, /* 5 */
|
||||
{ 2, "end opt", ASN1_EOC, ASN1_END }, /* 6 */
|
||||
{ 2, "requestList", ASN1_SEQUENCE, ASN1_LOOP }, /* 7 */
|
||||
{ 3, "request", ASN1_SEQUENCE, ASN1_BODY }, /* 8 */
|
||||
{ 4, "reqCert", ASN1_SEQUENCE, ASN1_NONE }, /* 9 */
|
||||
{ 5, "hashAlgorithm", ASN1_EOC, ASN1_RAW }, /* 10 */
|
||||
{ 5, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 11 */
|
||||
{ 5, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 12 */
|
||||
{ 5, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 13 */
|
||||
{ 4, "singleRequestExtensions", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 14 */
|
||||
{ 4, "end opt", ASN1_EOC, ASN1_END }, /* 15 */
|
||||
{ 2, "end loop", ASN1_EOC, ASN1_END }, /* 16 */
|
||||
{ 2, "requestExtensions", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 17 */
|
||||
{ 3, "Extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 18 */
|
||||
{ 4, "Extension", ASN1_SEQUENCE, ASN1_NONE }, /* 19 */
|
||||
{ 5, "extnID", ASN1_OID, ASN1_BODY }, /* 20 */
|
||||
{ 5, "critical", ASN1_BOOLEAN, ASN1_BODY |
|
||||
ASN1_DEF }, /* 21 */
|
||||
{ 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 22 */
|
||||
{ 3, "end loop", ASN1_EOC, ASN1_END }, /* 23 */
|
||||
{ 2, "end opt", ASN1_EOC, ASN1_END }, /* 24 */
|
||||
{ 1, "optionalSignature", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 25 */
|
||||
{ 2, "signature", ASN1_SEQUENCE, ASN1_NONE }, /* 26 */
|
||||
{ 3, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */
|
||||
{ 3, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 28 */
|
||||
{ 3, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 29 */
|
||||
{ 4, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 30 */
|
||||
{ 5, "certificate", ASN1_EOC, ASN1_RAW }, /* 31 */
|
||||
{ 4, "end loop", ASN1_EOC, ASN1_END }, /* 32 */
|
||||
{ 3, "end opt", ASN1_EOC, ASN1_END }, /* 33 */
|
||||
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 34 */
|
||||
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
||||
};
|
||||
|
||||
#define OCSP_REQ_TBS_REQUEST 1
|
||||
#define OCSP_REQ_REQUESTOR 5
|
||||
#define OCSP_REQ_REQ_CERT 9
|
||||
#define OCSP_REQ_HASH_ALG 10
|
||||
#define OCSP_REQ_ISSUER_NAME_HASH 11
|
||||
#define OCSP_REQ_ISSUER_KEY_HASH 12
|
||||
#define OCSP_REQ_SERIAL_NUMBER 13
|
||||
#define OCSP_REQ_EXTN_ID 20
|
||||
#define OCSP_REQ_CRITICAL 21
|
||||
#define OCSP_REQ_EXTN_VALUE 22
|
||||
#define OCSP_REQ_SIG_ALG 27
|
||||
#define OCSP_REQ_SIGNATURE 28
|
||||
#define OCSP_REQ_CERTIFICATE 31
|
||||
|
||||
/**
|
||||
* Parse the OCSPRequest data
|
||||
*
|
||||
*/
|
||||
static bool parse_OCSPRequest(private_x509_ocsp_request_t *this)
|
||||
{
|
||||
asn1_parser_t *parser;
|
||||
req_cert_t *reqCert = NULL;
|
||||
chunk_t object;
|
||||
int extn_oid = OID_UNKNOWN;
|
||||
int objectID;
|
||||
bool critical = FALSE, success = FALSE;
|
||||
|
||||
parser = asn1_parser_create(ocspRequestObjects, this->encoding);
|
||||
|
||||
while (parser->iterate(parser, &objectID, &object))
|
||||
{
|
||||
u_int level = parser->get_level(parser)+1;
|
||||
|
||||
switch (objectID)
|
||||
{
|
||||
|
||||
case OCSP_REQ_TBS_REQUEST:
|
||||
this->tbsRequest = object;
|
||||
break;
|
||||
case OCSP_REQ_REQUESTOR:
|
||||
this->requestor = identification_create_from_encoding(ID_DER_ASN1_DN, object);
|
||||
break;
|
||||
case OCSP_REQ_REQ_CERT:
|
||||
INIT(reqCert);
|
||||
this->reqCerts->insert_last(this->reqCerts, reqCert);
|
||||
break;
|
||||
case OCSP_REQ_HASH_ALG:
|
||||
reqCert->hashAlgorithm = hasher_algorithm_from_oid(
|
||||
asn1_parse_algorithmIdentifier(object, level, NULL));
|
||||
if (reqCert->hashAlgorithm == HASH_UNKNOWN)
|
||||
{
|
||||
DBG1(DBG_ASN, "unknowm hash algorithm");
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
case OCSP_REQ_ISSUER_NAME_HASH:
|
||||
reqCert->issuerNameHash = chunk_clone(object);
|
||||
break;
|
||||
case OCSP_REQ_ISSUER_KEY_HASH:
|
||||
reqCert->issuerKeyHash = chunk_clone(object);
|
||||
break;
|
||||
case OCSP_REQ_SERIAL_NUMBER:
|
||||
reqCert->serialNumber = chunk_clone(chunk_skip_zero(object));
|
||||
break;
|
||||
case OCSP_REQ_EXTN_ID:
|
||||
extn_oid = asn1_known_oid(object);
|
||||
break;
|
||||
case OCSP_REQ_CRITICAL:
|
||||
critical = object.len && *object.ptr;
|
||||
DBG2(DBG_ASN, " %s", critical ? "TRUE" : "FALSE");
|
||||
break;
|
||||
case OCSP_REQ_EXTN_VALUE:
|
||||
{
|
||||
switch (extn_oid)
|
||||
{
|
||||
case OID_NONCE:
|
||||
if (!asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
|
||||
level, "nonce"))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
this->nonce = chunk_clone(object);
|
||||
break;
|
||||
case OID_RESPONSE:
|
||||
if (!asn1_parse_simple_object(&object, ASN1_SEQUENCE,
|
||||
level, "acceptableResponses"))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (critical && lib->settings->get_bool(lib->settings,
|
||||
"%s.x509.enforce_critical", TRUE, lib->ns))
|
||||
{
|
||||
DBG1(DBG_ASN, "critical '%s' extension not supported",
|
||||
(extn_oid == OID_UNKNOWN) ? "unknown" :
|
||||
(char*)oid_names[extn_oid].name);
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OCSP_REQ_SIG_ALG:
|
||||
INIT(this->scheme);
|
||||
if (!signature_params_parse(object, level, this->scheme))
|
||||
{
|
||||
DBG1(DBG_ASN, " unable to parse signature algorithm");
|
||||
goto end;
|
||||
}
|
||||
|
||||
break;
|
||||
case OCSP_REQ_SIGNATURE:
|
||||
this->signature = chunk_skip(object, 1);
|
||||
break;
|
||||
case OCSP_REQ_CERTIFICATE:
|
||||
if (this->cert)
|
||||
{
|
||||
DBG1(DBG_LIB, " skipping additional signing certificate");
|
||||
break;
|
||||
}
|
||||
this->cert = lib->creds->create(lib->creds,
|
||||
CRED_CERTIFICATE,CERT_X509,
|
||||
BUILD_BLOB_ASN1_DER, object, BUILD_END);
|
||||
if (!this->cert)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
success = parser->success(parser);
|
||||
|
||||
end:
|
||||
parser->destroy(parser);
|
||||
return success;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, get_type, certificate_type_t,
|
||||
private_x509_ocsp_request_t *this)
|
||||
@ -322,62 +506,60 @@ METHOD(certificate_t, get_type, certificate_type_t,
|
||||
METHOD(certificate_t, get_subject, identification_t*,
|
||||
private_x509_ocsp_request_t *this)
|
||||
{
|
||||
certificate_t *ca = (certificate_t*)this->ca;
|
||||
|
||||
if (this->requestor)
|
||||
{
|
||||
return this->requestor;
|
||||
}
|
||||
if (this->cert)
|
||||
{
|
||||
return this->cert->get_subject(this->cert);
|
||||
}
|
||||
return ca->get_subject(ca);
|
||||
return (this->cert) ? this->cert->get_subject(this->cert) : NULL;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, get_issuer, identification_t*,
|
||||
private_x509_ocsp_request_t *this)
|
||||
{
|
||||
certificate_t *ca = (certificate_t*)this->ca;
|
||||
|
||||
return ca->get_subject(ca);
|
||||
return this->cacert ? this->cacert->get_subject(this->cacert) : NULL;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, has_subject, id_match_t,
|
||||
private_x509_ocsp_request_t *this, identification_t *subject)
|
||||
{
|
||||
certificate_t *current;
|
||||
enumerator_t *enumerator;
|
||||
id_match_t match, best = ID_MATCH_NONE;
|
||||
|
||||
enumerator = this->candidates->create_enumerator(this->candidates);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
match = current->has_subject(current, subject);
|
||||
if (match > best)
|
||||
{
|
||||
best = match;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return best;
|
||||
return ID_MATCH_NONE;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, has_issuer, id_match_t,
|
||||
private_x509_ocsp_request_t *this,
|
||||
identification_t *issuer)
|
||||
{
|
||||
certificate_t *ca = (certificate_t*)this->ca;
|
||||
|
||||
return ca->has_subject(ca, issuer);
|
||||
return this->cacert ? this->cacert->has_subject(this->cacert, issuer) :
|
||||
ID_MATCH_NONE;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, issued_by, bool,
|
||||
private_x509_ocsp_request_t *this, certificate_t *issuer,
|
||||
signature_params_t **scheme)
|
||||
{
|
||||
DBG1(DBG_LIB, "OCSP request validation not implemented!");
|
||||
return FALSE;
|
||||
public_key_t *key;
|
||||
bool valid;
|
||||
|
||||
if (issuer->get_type(issuer) != CERT_X509 || this->cert == NULL ||
|
||||
!issuer->equals(issuer, this->cert))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
key = issuer->get_public_key(issuer);
|
||||
if (!key)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
valid = key->verify(key, this->scheme->scheme, this->scheme->params,
|
||||
this->tbsRequest, this->signature);
|
||||
key->destroy(key);
|
||||
|
||||
if (valid && scheme)
|
||||
{
|
||||
*scheme = signature_params_clone(this->scheme);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, get_public_key, public_key_t*,
|
||||
@ -390,17 +572,7 @@ METHOD(certificate_t, get_validity, bool,
|
||||
private_x509_ocsp_request_t *this, time_t *when, time_t *not_before,
|
||||
time_t *not_after)
|
||||
{
|
||||
certificate_t *cert;
|
||||
|
||||
if (this->cert)
|
||||
{
|
||||
cert = this->cert;
|
||||
}
|
||||
else
|
||||
{
|
||||
cert = (certificate_t*)this->ca;
|
||||
}
|
||||
return cert->get_validity(cert, when, not_before, not_after);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(certificate_t, get_encoding, bool,
|
||||
@ -455,11 +627,12 @@ METHOD(certificate_t, destroy, void,
|
||||
{
|
||||
if (ref_put(&this->ref))
|
||||
{
|
||||
DESTROY_IF((certificate_t*)this->ca);
|
||||
DESTROY_IF(this->cacert);
|
||||
DESTROY_IF(this->requestor);
|
||||
DESTROY_IF(this->cert);
|
||||
DESTROY_IF(this->key);
|
||||
this->candidates->destroy_offset(this->candidates, offsetof(certificate_t, destroy));
|
||||
signature_params_destroy(this->scheme);
|
||||
this->reqCerts->destroy_function(this->reqCerts, req_cert_destroy);
|
||||
chunk_free(&this->nonce);
|
||||
chunk_free(&this->encoding);
|
||||
free(this);
|
||||
@ -472,6 +645,52 @@ METHOD(ocsp_request_t, get_nonce, chunk_t,
|
||||
return this->nonce;
|
||||
}
|
||||
|
||||
METHOD(ocsp_request_t, get_signer_cert, certificate_t*,
|
||||
private_x509_ocsp_request_t *this)
|
||||
{
|
||||
return this->cert;
|
||||
}
|
||||
|
||||
CALLBACK(filter, bool,
|
||||
void *data, enumerator_t *orig, va_list args)
|
||||
{
|
||||
req_cert_t *reqCert;
|
||||
hash_algorithm_t *hashAlgorithm;
|
||||
chunk_t *issuerNameHash, *issuerKeyHash, *serialNumber;
|
||||
|
||||
VA_ARGS_VGET(args, hashAlgorithm, issuerNameHash, issuerKeyHash, serialNumber);
|
||||
|
||||
if (orig->enumerate(orig, &reqCert))
|
||||
{
|
||||
if (hashAlgorithm)
|
||||
{
|
||||
*hashAlgorithm = reqCert->hashAlgorithm;
|
||||
}
|
||||
if (issuerNameHash)
|
||||
{
|
||||
*issuerNameHash = reqCert->issuerNameHash;
|
||||
}
|
||||
if (issuerKeyHash)
|
||||
{
|
||||
*issuerKeyHash = reqCert->issuerKeyHash;
|
||||
}
|
||||
if (serialNumber)
|
||||
{
|
||||
*serialNumber = reqCert->serialNumber;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(ocsp_request_t, create_request_enumerator, enumerator_t*,
|
||||
private_x509_ocsp_request_t *this)
|
||||
{
|
||||
return enumerator_create_filter(
|
||||
this->reqCerts->create_enumerator(this->reqCerts),
|
||||
filter, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* create an empty but initialized OCSP request
|
||||
*/
|
||||
@ -497,9 +716,11 @@ static private_x509_ocsp_request_t *create_empty()
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.get_nonce = _get_nonce,
|
||||
.get_signer_cert = _get_signer_cert,
|
||||
.create_request_enumerator = _create_request_enumerator,
|
||||
},
|
||||
},
|
||||
.candidates = linked_list_create(),
|
||||
.reqCerts = linked_list_create(),
|
||||
.ref = 1,
|
||||
);
|
||||
|
||||
@ -511,12 +732,15 @@ static private_x509_ocsp_request_t *create_empty()
|
||||
*/
|
||||
x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args)
|
||||
{
|
||||
private_x509_ocsp_request_t *req;
|
||||
certificate_t *cert;
|
||||
private_x509_ocsp_request_t *this;
|
||||
private_key_t *private;
|
||||
identification_t *subject;
|
||||
certificate_t *cert;
|
||||
x509_t *x509;
|
||||
req_cert_t *reqCert;
|
||||
|
||||
this = create_empty();
|
||||
|
||||
req = create_empty();
|
||||
while (TRUE)
|
||||
{
|
||||
switch (va_arg(args, builder_part_t))
|
||||
@ -525,43 +749,137 @@ x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args
|
||||
cert = va_arg(args, certificate_t*);
|
||||
if (cert->get_type(cert) == CERT_X509)
|
||||
{
|
||||
req->ca = (x509_t*)cert->get_ref(cert);
|
||||
this->cacert = cert->get_ref(cert);
|
||||
}
|
||||
continue;
|
||||
case BUILD_CERT:
|
||||
cert = va_arg(args, certificate_t*);
|
||||
if (cert->get_type(cert) == CERT_X509)
|
||||
{
|
||||
req->candidates->insert_last(req->candidates,
|
||||
cert->get_ref(cert));
|
||||
x509 = (x509_t*)cert;
|
||||
INIT(reqCert,
|
||||
.serialNumber = chunk_clone(x509->get_serial(x509)),
|
||||
);
|
||||
this->reqCerts->insert_last(this->reqCerts, reqCert);
|
||||
}
|
||||
continue;
|
||||
case BUILD_SIGNING_CERT:
|
||||
cert = va_arg(args, certificate_t*);
|
||||
req->cert = cert->get_ref(cert);
|
||||
this->cert = cert->get_ref(cert);
|
||||
continue;
|
||||
case BUILD_SIGNING_KEY:
|
||||
private = va_arg(args, private_key_t*);
|
||||
req->key = private->get_ref(private);
|
||||
this->key = private->get_ref(private);
|
||||
continue;
|
||||
case BUILD_SUBJECT:
|
||||
subject = va_arg(args, identification_t*);
|
||||
req->requestor = subject->clone(subject);
|
||||
this->requestor = subject->clone(subject);
|
||||
continue;
|
||||
case BUILD_END:
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->cacert)
|
||||
{
|
||||
chunk_t issuerNameHash, issuerKeyHash;
|
||||
enumerator_t *enumerator;
|
||||
identification_t *issuer;
|
||||
public_key_t *public;
|
||||
req_cert_t *reqCert;
|
||||
hasher_t *hasher;
|
||||
|
||||
public = this->cacert->get_public_key(this->cacert);
|
||||
if (!public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &issuerKeyHash))
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to compute SHA1 issuerKeyHash");
|
||||
public->destroy(public);
|
||||
goto error;
|
||||
}
|
||||
public->destroy(public);
|
||||
|
||||
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
if (!hasher)
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to create SHA1 hasher");
|
||||
goto error;
|
||||
}
|
||||
|
||||
issuer = this->cacert->get_subject(this->cacert);
|
||||
if (!hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
|
||||
&issuerNameHash))
|
||||
{
|
||||
DBG1(DBG_LIB, "failed to compute SHA1 issuerNameHash");
|
||||
hasher->destroy(hasher);
|
||||
goto error;
|
||||
}
|
||||
hasher->destroy(hasher);
|
||||
|
||||
enumerator = this->reqCerts->create_enumerator(this->reqCerts);
|
||||
while (enumerator->enumerate(enumerator, &reqCert))
|
||||
{
|
||||
reqCert->hashAlgorithm = HASH_SHA1;
|
||||
reqCert->issuerNameHash = chunk_clone(issuerNameHash);
|
||||
reqCert->issuerKeyHash = chunk_clone(issuerKeyHash);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
chunk_free(&issuerNameHash);
|
||||
|
||||
this->encoding = build_OCSPRequest(this);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
error:
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* load an OCSP request
|
||||
*/
|
||||
static x509_ocsp_request_t *load(chunk_t blob)
|
||||
{
|
||||
private_x509_ocsp_request_t *this;
|
||||
|
||||
this = create_empty();
|
||||
this->encoding = chunk_clone(blob);
|
||||
|
||||
if (!parse_OCSPRequest(this))
|
||||
{
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* See header.
|
||||
*/
|
||||
x509_ocsp_request_t *x509_ocsp_request_load(certificate_type_t type, va_list args)
|
||||
{
|
||||
chunk_t blob = chunk_empty;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (va_arg(args, builder_part_t))
|
||||
{
|
||||
case BUILD_BLOB_ASN1_DER:
|
||||
blob = va_arg(args, chunk_t);
|
||||
continue;
|
||||
case BUILD_END:
|
||||
break;
|
||||
default:
|
||||
destroy(req);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (req->ca)
|
||||
if (blob.ptr)
|
||||
{
|
||||
req->encoding = build_OCSPRequest(req);
|
||||
return &req->public;
|
||||
return load(blob);
|
||||
}
|
||||
destroy(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Martin Willi
|
||||
* Copyright (C) 2023 Andreas Steffen, strongSec GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -54,4 +55,13 @@ struct x509_ocsp_request_t {
|
||||
*/
|
||||
x509_ocsp_request_t *x509_ocsp_request_gen(certificate_type_t type, va_list args);
|
||||
|
||||
/**
|
||||
* Load a X.509 OCSP request.
|
||||
*
|
||||
* @param type certificate type, CERT_X509_OCSP_REQUEST only
|
||||
* @param args builder_part_t argument list
|
||||
* @return OCSP request, NULL on failure
|
||||
*/
|
||||
x509_ocsp_request_t *x509_ocsp_request_load(certificate_type_t type, va_list args);
|
||||
|
||||
#endif /** X509_OCSP_REQUEST_H_ @}*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 Tobias Brunner
|
||||
* Copyright (C) 2008-2009 Martin Willi
|
||||
* Copyright (C) 2007-2022 Andreas Steffen
|
||||
* Copyright (C) 2007-2023 Andreas Steffen
|
||||
* Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
@ -31,6 +31,7 @@
|
||||
#include <library.h>
|
||||
#include <credentials/certificates/x509.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
#include <credentials/certificates/ocsp_single_response.h>
|
||||
|
||||
/**
|
||||
* how long do we use an OCSP response without a nextUpdate
|
||||
@ -73,6 +74,11 @@ struct private_x509_ocsp_response_t {
|
||||
*/
|
||||
chunk_t signature;
|
||||
|
||||
/**
|
||||
* OCSP response status
|
||||
*/
|
||||
ocsp_status_t ocsp_status;
|
||||
|
||||
/**
|
||||
* name or keyid of the responder
|
||||
*/
|
||||
@ -103,36 +109,22 @@ struct private_x509_ocsp_response_t {
|
||||
*/
|
||||
chunk_t nonce;
|
||||
|
||||
/**
|
||||
* Signer certificate, included in response
|
||||
*/
|
||||
certificate_t *cert;
|
||||
|
||||
/**
|
||||
* Signer private key to sign response
|
||||
*/
|
||||
private_key_t *key;
|
||||
|
||||
/**
|
||||
* reference counter
|
||||
*/
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* single response contained in OCSP response
|
||||
*/
|
||||
typedef struct {
|
||||
/** hash algorithm OID to for the two hashes */
|
||||
int hashAlgorithm;
|
||||
/** hash of issuer DN */
|
||||
chunk_t issuerNameHash;
|
||||
/** issuerKeyID */
|
||||
chunk_t issuerKeyHash;
|
||||
/** serial number of certificate */
|
||||
chunk_t serialNumber;
|
||||
/** OCSP certificate status */
|
||||
cert_validation_t status;
|
||||
/** time of revocation, if revoked */
|
||||
time_t revocationTime;
|
||||
/** revocation reason, if revoked */
|
||||
crl_reason_t revocationReason;
|
||||
/** creation of associated CRL */
|
||||
time_t thisUpdate;
|
||||
/** creation of next CRL */
|
||||
time_t nextUpdate;
|
||||
} single_response_t;
|
||||
|
||||
/* our OCSP response version implementation */
|
||||
#define OCSP_BASIC_RESPONSE_VERSION 1
|
||||
|
||||
@ -142,7 +134,7 @@ METHOD(ocsp_response_t, get_status, cert_validation_t,
|
||||
time_t *this_update, time_t *next_update)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
single_response_t *response;
|
||||
ocsp_single_response_t *response;
|
||||
cert_validation_t status = VALIDATION_FAILED;
|
||||
certificate_t *issuercert = &issuer->interface;
|
||||
|
||||
@ -232,7 +224,7 @@ METHOD(ocsp_response_t, create_cert_enumerator, enumerator_t*,
|
||||
CALLBACK(filter, bool,
|
||||
void *data, enumerator_t *orig, va_list args)
|
||||
{
|
||||
single_response_t *response;
|
||||
ocsp_single_response_t *response;
|
||||
cert_validation_t *status;
|
||||
crl_reason_t *revocationReason;
|
||||
chunk_t *serialNumber;
|
||||
@ -277,6 +269,54 @@ METHOD(ocsp_response_t, get_nonce, chunk_t,
|
||||
return this->nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build singleResponse
|
||||
*/
|
||||
static chunk_t build_singleResponse(private_x509_ocsp_response_t *this,
|
||||
ocsp_single_response_t *response)
|
||||
{
|
||||
chunk_t certID, certStatus, nextUpdate = chunk_empty;
|
||||
|
||||
certID = asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
asn1_algorithmIdentifier(
|
||||
hasher_algorithm_to_oid(response->hashAlgorithm)),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, response->issuerNameHash),
|
||||
asn1_simple_object(ASN1_OCTET_STRING, response->issuerKeyHash),
|
||||
asn1_integer("c", response->serialNumber));
|
||||
|
||||
switch (response->status)
|
||||
{
|
||||
case VALIDATION_GOOD:
|
||||
certStatus = asn1_wrap(ASN1_CONTEXT_S_0, "c", chunk_empty);
|
||||
break;
|
||||
case VALIDATION_REVOKED:
|
||||
case VALIDATION_ON_HOLD:
|
||||
certStatus = asn1_wrap(ASN1_CONTEXT_C_1, "mm",
|
||||
asn1_from_time(&response->revocationTime,
|
||||
ASN1_GENERALIZEDTIME),
|
||||
asn1_wrap(ASN1_CONTEXT_C_0, "m",
|
||||
asn1_simple_object(ASN1_ENUMERATED,
|
||||
chunk_from_chars(response->revocationReason))));
|
||||
break;
|
||||
case VALIDATION_FAILED:
|
||||
default:
|
||||
certStatus = asn1_wrap(ASN1_CONTEXT_S_2, "c", chunk_empty);
|
||||
}
|
||||
|
||||
if (response->nextUpdate != 0)
|
||||
{
|
||||
nextUpdate = asn1_wrap(ASN1_CONTEXT_C_0, "m",
|
||||
asn1_from_time(&response->nextUpdate,
|
||||
ASN1_GENERALIZEDTIME));
|
||||
}
|
||||
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
certID,
|
||||
certStatus,
|
||||
asn1_from_time(&response->thisUpdate, ASN1_GENERALIZEDTIME),
|
||||
nextUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of singleResponse
|
||||
*/
|
||||
@ -312,6 +352,7 @@ static const asn1Object_t singleResponseObjects[] = {
|
||||
{ 1, "end opt", ASN1_EOC, ASN1_END }, /* 27 */
|
||||
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
||||
};
|
||||
|
||||
#define SINGLE_RESPONSE_ALGORITHM 2
|
||||
#define SINGLE_RESPONSE_ISSUER_NAME_HASH 3
|
||||
#define SINGLE_RESPONSE_ISSUER_KEY_HASH 4
|
||||
@ -338,17 +379,10 @@ static bool parse_singleResponse(private_x509_ocsp_response_t *this,
|
||||
int objectID;
|
||||
bool success = FALSE;
|
||||
|
||||
single_response_t *response;
|
||||
ocsp_single_response_t *response;
|
||||
|
||||
response = ocsp_single_response_create();
|
||||
|
||||
response = malloc_thing(single_response_t);
|
||||
response->hashAlgorithm = OID_UNKNOWN;
|
||||
response->issuerNameHash = chunk_empty;
|
||||
response->issuerKeyHash = chunk_empty;
|
||||
response->serialNumber = chunk_empty;
|
||||
response->status = VALIDATION_FAILED;
|
||||
response->revocationTime = 0;
|
||||
response->revocationReason = CRL_REASON_UNSPECIFIED;
|
||||
response->thisUpdate = UNDEFINED_TIME;
|
||||
/* if nextUpdate is missing, we give it a short lifetime */
|
||||
response->nextUpdate = this->producedAt + OCSP_DEFAULT_LIFETIME;
|
||||
|
||||
@ -364,13 +398,13 @@ static bool parse_singleResponse(private_x509_ocsp_response_t *this,
|
||||
parser->get_level(parser)+1, NULL);
|
||||
break;
|
||||
case SINGLE_RESPONSE_ISSUER_NAME_HASH:
|
||||
response->issuerNameHash = object;
|
||||
response->issuerNameHash = chunk_clone(object);
|
||||
break;
|
||||
case SINGLE_RESPONSE_ISSUER_KEY_HASH:
|
||||
response->issuerKeyHash = object;
|
||||
response->issuerKeyHash = chunk_clone(object);
|
||||
break;
|
||||
case SINGLE_RESPONSE_SERIAL_NUMBER:
|
||||
response->serialNumber = chunk_skip_zero(object);
|
||||
response->serialNumber = chunk_clone(chunk_skip_zero(object));
|
||||
break;
|
||||
case SINGLE_RESPONSE_CERT_STATUS_GOOD:
|
||||
response->status = VALIDATION_GOOD;
|
||||
@ -414,11 +448,31 @@ static bool parse_singleResponse(private_x509_ocsp_response_t *this,
|
||||
}
|
||||
else
|
||||
{
|
||||
free(response);
|
||||
response->destroy(response);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build responses
|
||||
*/
|
||||
static chunk_t build_responses(private_x509_ocsp_response_t *this)
|
||||
{
|
||||
ocsp_single_response_t *response;
|
||||
chunk_t responses = chunk_empty, single_response;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
enumerator = this->responses->create_enumerator(this->responses);
|
||||
while (enumerator->enumerate(enumerator, &response))
|
||||
{
|
||||
single_response = build_singleResponse(this, response);
|
||||
responses = chunk_cat("mm", responses, single_response);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
return asn1_wrap(ASN1_SEQUENCE, "m", responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of responses
|
||||
*/
|
||||
@ -466,6 +520,94 @@ end:
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build tbsResponseData
|
||||
*/
|
||||
static chunk_t build_tbsResponseData(private_x509_ocsp_response_t *this)
|
||||
{
|
||||
chunk_t responderIdByName;
|
||||
chunk_t responseExtensions = chunk_empty;
|
||||
|
||||
responderIdByName = asn1_wrap(ASN1_CONTEXT_C_1, "c",
|
||||
this->responderId->get_encoding(this->responderId));
|
||||
|
||||
this->producedAt = time(NULL);
|
||||
|
||||
responseExtensions = asn1_wrap(ASN1_CONTEXT_C_1, "m",
|
||||
asn1_wrap(ASN1_SEQUENCE, "m",
|
||||
asn1_wrap(ASN1_SEQUENCE, "mm",
|
||||
asn1_build_known_oid(OID_NONCE),
|
||||
asn1_wrap(ASN1_OCTET_STRING, "m",
|
||||
asn1_simple_object(ASN1_OCTET_STRING,
|
||||
this->nonce)))));
|
||||
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
responderIdByName,
|
||||
asn1_from_time(&this->producedAt, ASN1_GENERALIZEDTIME),
|
||||
build_responses(this),
|
||||
responseExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the signature
|
||||
*/
|
||||
static bool build_signature(private_x509_ocsp_response_t *this,
|
||||
chunk_t tbsResponseData, chunk_t *signature)
|
||||
{
|
||||
if (!this->key->sign(this->key, this->scheme->scheme, this->scheme->params,
|
||||
tbsResponseData, signature))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating OCSP response signature failed");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the basicOCSPResponse
|
||||
*/
|
||||
static bool build_basicOCSPResponse(private_x509_ocsp_response_t *this,
|
||||
chunk_t *basicResponse)
|
||||
{
|
||||
chunk_t tbsResponseData, sig_scheme, signature;
|
||||
chunk_t cert_encoding, certs = chunk_empty;
|
||||
x509_t *x509 = (x509_t*)this->cert;
|
||||
|
||||
*basicResponse = chunk_empty;
|
||||
|
||||
if (!signature_params_build(this->scheme, &sig_scheme))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
tbsResponseData = build_tbsResponseData(this);
|
||||
|
||||
if (!build_signature(this, tbsResponseData, &signature))
|
||||
{
|
||||
free(tbsResponseData.ptr);
|
||||
free(sig_scheme.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* don't include self-signed signer certificates */
|
||||
if (!(x509->get_flags(x509) & X509_SELF_SIGNED))
|
||||
{
|
||||
if (!this->cert->get_encoding(this->cert, CERT_ASN1_DER, &cert_encoding))
|
||||
{
|
||||
free(tbsResponseData.ptr);
|
||||
free(sig_scheme.ptr);
|
||||
free(signature.ptr);
|
||||
return FALSE;
|
||||
}
|
||||
certs = asn1_wrap(ASN1_CONTEXT_C_0, "m",
|
||||
asn1_wrap(ASN1_SEQUENCE, "m", cert_encoding));
|
||||
}
|
||||
|
||||
*basicResponse = asn1_wrap(ASN1_SEQUENCE, "mmmm",
|
||||
tbsResponseData, sig_scheme,
|
||||
asn1_bitstring("m", signature), certs);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of basicResponse
|
||||
*/
|
||||
@ -580,7 +722,7 @@ static bool parse_basicOCSPResponse(private_x509_ocsp_response_t *this,
|
||||
asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
|
||||
parser->get_level(parser)+1, "nonce"))
|
||||
{
|
||||
this->nonce = object;
|
||||
this->nonce = chunk_clone(object);
|
||||
}
|
||||
break;
|
||||
case BASIC_RESPONSE_ALGORITHM:
|
||||
@ -624,6 +766,31 @@ end:
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the OCSPResponse
|
||||
*
|
||||
*/
|
||||
static chunk_t build_OCSPResponse(private_x509_ocsp_response_t *this)
|
||||
{
|
||||
chunk_t response, responseBytes = chunk_empty;
|
||||
|
||||
if (this->ocsp_status == OCSP_SUCCESSFUL)
|
||||
{
|
||||
if (!build_basicOCSPResponse(this, &response))
|
||||
{
|
||||
return chunk_empty;
|
||||
}
|
||||
responseBytes = asn1_wrap(ASN1_CONTEXT_C_0, "m",
|
||||
asn1_wrap(ASN1_SEQUENCE, "mm",
|
||||
asn1_build_known_oid(OID_BASIC),
|
||||
asn1_wrap(ASN1_OCTET_STRING, "m", response)));
|
||||
}
|
||||
return asn1_wrap(ASN1_SEQUENCE, "mm",
|
||||
asn1_simple_object(ASN1_ENUMERATED,
|
||||
chunk_from_chars(this->ocsp_status)),
|
||||
responseBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 definition of ocspResponse
|
||||
*/
|
||||
@ -651,7 +818,6 @@ static bool parse_OCSPResponse(private_x509_ocsp_response_t *this)
|
||||
int objectID;
|
||||
int responseType = OID_UNKNOWN;
|
||||
bool success = FALSE;
|
||||
ocsp_status_t status;
|
||||
|
||||
parser = asn1_parser_create(ocspResponseObjects, this->encoding);
|
||||
|
||||
@ -660,15 +826,16 @@ static bool parse_OCSPResponse(private_x509_ocsp_response_t *this)
|
||||
switch (objectID)
|
||||
{
|
||||
case OCSP_RESPONSE_STATUS:
|
||||
status = (ocsp_status_t)*object.ptr;
|
||||
switch (status)
|
||||
this->ocsp_status = (ocsp_status_t)*object.ptr;
|
||||
switch (this->ocsp_status)
|
||||
{
|
||||
case OCSP_SUCCESSFUL:
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_LIB, " ocsp response status: %N",
|
||||
ocsp_status_names, status);
|
||||
goto end;
|
||||
ocsp_status_names, this->ocsp_status);
|
||||
success = TRUE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case OCSP_RESPONSE_TYPE:
|
||||
@ -845,19 +1012,24 @@ METHOD(certificate_t, destroy, void,
|
||||
{
|
||||
if (ref_put(&this->ref))
|
||||
{
|
||||
this->certs->destroy_offset(this->certs, offsetof(certificate_t, destroy));
|
||||
this->responses->destroy_function(this->responses, free);
|
||||
signature_params_destroy(this->scheme);
|
||||
this->certs->destroy_offset(this->certs,
|
||||
offsetof(certificate_t, destroy));
|
||||
this->responses->destroy_offset(this->responses,
|
||||
offsetof(ocsp_single_response_t, destroy));
|
||||
DESTROY_IF(this->cert);
|
||||
DESTROY_IF(this->key);
|
||||
DESTROY_IF(this->responderId);
|
||||
signature_params_destroy(this->scheme);
|
||||
free(this->nonce.ptr);
|
||||
free(this->encoding.ptr);
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load an OCSP response
|
||||
* create an empty but initialized OCSP response
|
||||
*/
|
||||
static x509_ocsp_response_t *load(chunk_t blob)
|
||||
static private_x509_ocsp_response_t *create_empty()
|
||||
{
|
||||
private_x509_ocsp_response_t *this;
|
||||
|
||||
@ -885,13 +1057,118 @@ static x509_ocsp_response_t *load(chunk_t blob)
|
||||
},
|
||||
},
|
||||
.ref = 1,
|
||||
.encoding = chunk_clone(blob),
|
||||
.producedAt = UNDEFINED_TIME,
|
||||
.usableUntil = UNDEFINED_TIME,
|
||||
.responses = linked_list_create(),
|
||||
.certs = linked_list_create(),
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See header.
|
||||
*/
|
||||
x509_ocsp_response_t *x509_ocsp_response_gen(certificate_type_t type, va_list args)
|
||||
{
|
||||
private_x509_ocsp_response_t *this;
|
||||
private_key_t *private;
|
||||
certificate_t *cert;
|
||||
chunk_t nonce;
|
||||
identification_t *subject;
|
||||
enumerator_t *enumerator;
|
||||
ocsp_single_response_t *response;
|
||||
|
||||
this = create_empty();
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (va_arg(args, builder_part_t))
|
||||
{
|
||||
case BUILD_OCSP_STATUS:
|
||||
this->ocsp_status = va_arg(args, ocsp_status_t);
|
||||
continue;
|
||||
case BUILD_OCSP_RESPONSES:
|
||||
enumerator = va_arg(args, enumerator_t*);
|
||||
while (enumerator->enumerate(enumerator, &response))
|
||||
{
|
||||
this->responses->insert_last(this->responses,
|
||||
response->get_ref(response));
|
||||
}
|
||||
continue;
|
||||
case BUILD_SIGNING_CERT:
|
||||
cert = va_arg(args, certificate_t*);
|
||||
if (cert)
|
||||
{
|
||||
subject = cert->get_subject(cert);
|
||||
this->cert = cert->get_ref(cert);
|
||||
this->responderId = subject->clone(subject);
|
||||
}
|
||||
continue;
|
||||
case BUILD_SIGNING_KEY:
|
||||
private = va_arg(args, private_key_t*);
|
||||
if (private)
|
||||
{
|
||||
this->key = private->get_ref(private);
|
||||
}
|
||||
continue;
|
||||
case BUILD_SIGNATURE_SCHEME:
|
||||
this->scheme = va_arg(args, signature_params_t*);
|
||||
this->scheme = signature_params_clone(this->scheme);
|
||||
continue;
|
||||
case BUILD_NONCE:
|
||||
nonce = va_arg(args, chunk_t);
|
||||
this->nonce = chunk_clone(nonce);
|
||||
continue;
|
||||
case BUILD_END:
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->ocsp_status == OCSP_SUCCESSFUL)
|
||||
{
|
||||
if (!this->key)
|
||||
{
|
||||
DBG1(DBG_LIB, "no OCSP signing key defined");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* select signature scheme, if not already specified */
|
||||
if (!this->scheme)
|
||||
{
|
||||
INIT(this->scheme,
|
||||
.scheme = signature_scheme_from_oid(
|
||||
hasher_signature_algorithm_to_oid(HASH_SHA256,
|
||||
this->key->get_type(this->key))),
|
||||
);
|
||||
}
|
||||
if (this->scheme->scheme == SIGN_UNKNOWN)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
this->encoding = build_OCSPResponse(this);
|
||||
return &this->public;
|
||||
|
||||
error:
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* load an OCSP response
|
||||
*/
|
||||
static x509_ocsp_response_t *load(chunk_t blob)
|
||||
{
|
||||
private_x509_ocsp_response_t *this;
|
||||
|
||||
this = create_empty();
|
||||
this->encoding = chunk_clone(blob);
|
||||
|
||||
if (!parse_OCSPResponse(this))
|
||||
{
|
||||
destroy(this);
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Martin Willi
|
||||
* Copyright (C) 2023 Andreas Steffen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -38,6 +39,23 @@ struct x509_ocsp_response_t {
|
||||
ocsp_response_t interface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a X.509 OCSP response.
|
||||
*
|
||||
* The resulting builder accepts:
|
||||
* BUILD_OCSP_STATUS: status from OCSP respnder
|
||||
* BUILD_OCSP_RESPONSES: enumerator over the list of OCSP single responses
|
||||
* BUILD_NONCE: nonce extracted from the OCSP request
|
||||
* BUILD_SIGNING_CERT: certificate to create OCSP response signature
|
||||
* BUILD_SIGNING_KEY: private key to create OCSP response signature
|
||||
* BUILD_SIGNATURE_SCHEME: scheme used for the OCSP response signature
|
||||
*
|
||||
* @param type certificate type, CERT_X509_OCSP_REQUEST only
|
||||
* @param args builder_part_t argument list
|
||||
* @return OCSP request, NULL on failure
|
||||
*/
|
||||
x509_ocsp_response_t *x509_ocsp_response_gen(certificate_type_t type, va_list args);
|
||||
|
||||
/**
|
||||
* Load a X.509 OCSP response.
|
||||
*
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Martin Willi
|
||||
* Copyright (C) 2023 Andreas Steffen, strongSec GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -69,6 +70,10 @@ METHOD(plugin_t, get_features, int,
|
||||
PLUGIN_PROVIDE(CERT_ENCODE, CERT_X509_OCSP_REQUEST),
|
||||
PLUGIN_DEPENDS(HASHER, HASH_SHA1),
|
||||
PLUGIN_DEPENDS(RNG, RNG_WEAK),
|
||||
PLUGIN_REGISTER(CERT_DECODE, x509_ocsp_request_load, TRUE),
|
||||
PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_OCSP_REQUEST),
|
||||
PLUGIN_REGISTER(CERT_ENCODE, x509_ocsp_response_gen, FALSE),
|
||||
PLUGIN_PROVIDE(CERT_ENCODE, CERT_X509_OCSP_RESPONSE),
|
||||
PLUGIN_REGISTER(CERT_DECODE, x509_ocsp_response_load, TRUE),
|
||||
PLUGIN_PROVIDE(CERT_DECODE, CERT_X509_OCSP_RESPONSE),
|
||||
|
||||
|
@ -569,6 +569,57 @@ START_TEST(test_base32)
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*******************************************************************************
|
||||
* DEC encoding test
|
||||
*/
|
||||
|
||||
START_TEST(test_dec)
|
||||
{
|
||||
typedef struct {
|
||||
chunk_t in;
|
||||
char *out;
|
||||
} testdata_t;
|
||||
|
||||
testdata_t test[] = {
|
||||
{ chunk_from_chars( 0x00), "0" },
|
||||
{ chunk_from_chars( 0x09), "9" },
|
||||
{ chunk_from_chars( 0x0a), "10" },
|
||||
{ chunk_from_chars( 0x13), "19" },
|
||||
{ chunk_from_chars( 0x14), "20" },
|
||||
{ chunk_from_chars( 0x63), "99" },
|
||||
{ chunk_from_chars( 0x64), "100" },
|
||||
{ chunk_from_chars( 0x65), "101" },
|
||||
{ chunk_from_chars( 0xff), "255" },
|
||||
{ chunk_from_chars( 0x00, 0xff), "255" },
|
||||
{ chunk_from_chars( 0x01, 0x00), "256" },
|
||||
{ chunk_from_chars( 0x01, 0x03), "259" },
|
||||
{ chunk_from_chars( 0x01, 0x04), "260" },
|
||||
{ chunk_from_chars( 0x09, 0xff), "2559" },
|
||||
{ chunk_from_chars( 0x0a, 0x00), "2560" },
|
||||
{ chunk_from_chars( 0x0a, 0x01), "2561" },
|
||||
{ chunk_from_chars( 0xff, 0xff), "65535" },
|
||||
{ chunk_from_chars(0x00, 0xff, 0xff), "65535" },
|
||||
{ chunk_from_chars(0x01, 0x00, 0x00), "65536" },
|
||||
{ chunk_from_chars(0x01, 0x86, 0x9f), "99999" },
|
||||
{ chunk_from_chars(0x01, 0x86, 0xa0), "100000" },
|
||||
{ chunk_from_chars(0x0f, 0x42, 0x40), "1000000" },
|
||||
{ chunk_from_chars(0xa9, 0x8a, 0xc7), "11111111" },
|
||||
{ chunk_from_chars(0xbc, 0x61, 0x4e), "12345678" },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < countof(test); i++)
|
||||
{
|
||||
char buf[10];
|
||||
chunk_t out;
|
||||
|
||||
out = chunk_to_dec(test[i].in, buf);
|
||||
ck_assert_str_eq(out.ptr, test[i].out);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/*******************************************************************************
|
||||
* chunk_increment test
|
||||
*/
|
||||
@ -1190,6 +1241,7 @@ Suite *chunk_suite_create()
|
||||
tcase_add_test(tc, test_base64);
|
||||
tcase_add_test(tc, test_base32);
|
||||
tcase_add_test(tc, test_base16);
|
||||
tcase_add_test(tc, test_dec);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("chunk_mac");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Andreas Steffen, strongSec GmbH
|
||||
* Copyright (C) 2022-2023 Andreas Steffen, strongSec GmbH
|
||||
*
|
||||
* Copyright (C) secunet Security Networks AG
|
||||
*
|
||||
@ -18,7 +18,9 @@
|
||||
|
||||
#include <credentials/certificates/x509.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
#include <credentials/certificates/ocsp_request.h>
|
||||
#include <credentials/certificates/ocsp_response.h>
|
||||
#include <credentials/certificates/ocsp_single_response.h>
|
||||
#include <credentials/certificates/ac.h>
|
||||
|
||||
#include <time.h>
|
||||
@ -182,6 +184,83 @@ static certificate_t* create_ocsp_request(certificate_t *cert)
|
||||
return ocsp_req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 encoded OCSP request
|
||||
*/
|
||||
static certificate_t* parse_ocsp_request(chunk_t encoding)
|
||||
{
|
||||
certificate_t *ocsp_req;
|
||||
|
||||
ocsp_req = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
|
||||
BUILD_BLOB_ASN1_DER, encoding,
|
||||
BUILD_END);
|
||||
ck_assert(ocsp_req);
|
||||
|
||||
return ocsp_req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OCSP response based on an OCSP request
|
||||
*/
|
||||
static certificate_t* create_ocsp_response(ocsp_request_t *ocsp_request,
|
||||
cert_validation_t status,
|
||||
certificate_t *cert)
|
||||
{
|
||||
private_key_t *privkey;
|
||||
certificate_t *ocsp_rsp;
|
||||
ocsp_status_t ocsp_status = OCSP_SUCCESSFUL;
|
||||
ocsp_single_response_t *response;
|
||||
chunk_t issuerNameHash, issuerKeyHash, serialNumber, nonce;
|
||||
hash_algorithm_t hashAlgorithm;
|
||||
linked_list_t *responses = linked_list_create();
|
||||
enumerator_t *enumerator;
|
||||
|
||||
privkey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
|
||||
BUILD_BLOB_ASN1_DER, chunk_from_thing(keydata),
|
||||
BUILD_END);
|
||||
ck_assert(privkey);
|
||||
|
||||
/* generate OCSP single response */
|
||||
enumerator = ocsp_request->create_request_enumerator(ocsp_request);
|
||||
ck_assert(enumerator->enumerate(enumerator, &hashAlgorithm, &issuerNameHash,
|
||||
&issuerKeyHash, &serialNumber));
|
||||
response = ocsp_single_response_create();
|
||||
response->hashAlgorithm = hashAlgorithm;
|
||||
response->issuerNameHash = chunk_clone(issuerNameHash);
|
||||
response->issuerKeyHash = chunk_clone(issuerKeyHash);
|
||||
response->serialNumber = chunk_clone(serialNumber);
|
||||
response->thisUpdate = time(NULL);
|
||||
response->status = status;
|
||||
if (status == VALIDATION_REVOKED)
|
||||
{
|
||||
response->revocationReason = CRL_REASON_KEY_COMPROMISE;
|
||||
response->revocationTime = time(NULL);
|
||||
}
|
||||
responses->insert_last(responses, response);
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
nonce = ocsp_request->get_nonce(ocsp_request);
|
||||
|
||||
/* generate OCSP response */
|
||||
enumerator = responses->create_enumerator(responses);
|
||||
ocsp_rsp = lib->creds->create(lib->creds, CRED_CERTIFICATE,
|
||||
CERT_X509_OCSP_RESPONSE,
|
||||
BUILD_OCSP_STATUS, ocsp_status,
|
||||
BUILD_OCSP_RESPONSES, enumerator,
|
||||
BUILD_SIGNING_KEY, privkey,
|
||||
BUILD_SIGNING_CERT, cert,
|
||||
BUILD_NONCE, nonce,
|
||||
BUILD_END);
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
ck_assert(ocsp_rsp);
|
||||
|
||||
privkey->destroy(privkey);
|
||||
responses->destroy_offset(responses, offsetof(ocsp_single_response_t, destroy));
|
||||
|
||||
return ocsp_rsp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ASN.1 encoded OCSP response
|
||||
*/
|
||||
@ -290,72 +369,22 @@ static serial_number_t serial_numbers[] = {
|
||||
chunk_from_chars(0x05,0x00,0xff,0xff,0xff,0xff) },
|
||||
};
|
||||
|
||||
static chunk_t ocsp_responses[] = {
|
||||
chunk_from_chars(
|
||||
0x30,0x82,0x01,0x85,0x0a,0x01,0x00,0xa0,0x82,0x01,0x7e,0x30,0x82,0x01,0x7a,0x06,
|
||||
0x09,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x01,0x04,0x82,0x01,0x6b,0x30,0x82,
|
||||
0x01,0x67,0x30,0x81,0xd1,0xa1,0x33,0x30,0x31,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,0x0d,0x30,0x0b,0x06,
|
||||
0x03,0x55,0x04,0x03,0x13,0x04,0x74,0x65,0x73,0x74,0x18,0x0f,0x32,0x30,0x32,0x32,
|
||||
0x31,0x31,0x32,0x32,0x30,0x39,0x31,0x36,0x34,0x37,0x5a,0x30,0x64,0x30,0x62,0x30,
|
||||
0x3a,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0xbd,0x25,
|
||||
0xa0,0xdf,0xc3,0x21,0xf2,0xd8,0xed,0x19,0x63,0x0a,0x4b,0x90,0x6d,0xc3,0x0f,0xe7,
|
||||
0x79,0x20,0x04,0x14,0xe2,0x6d,0x1e,0xdf,0x83,0x8e,0xa2,0x1f,0xc3,0x00,0xdd,0x44,
|
||||
0x6f,0x8a,0x4d,0x70,0x0c,0x02,0xe3,0x1f,0x02,0x01,0x7f,0x80,0x00,0x18,0x0f,0x32,
|
||||
0x30,0x32,0x32,0x31,0x31,0x32,0x32,0x30,0x39,0x31,0x36,0x34,0x37,0x5a,0xa0,0x11,
|
||||
0x18,0x0f,0x32,0x30,0x32,0x32,0x31,0x31,0x32,0x32,0x31,0x39,0x31,0x36,0x34,0x37,
|
||||
0x5a,0xa1,0x23,0x30,0x21,0x30,0x1f,0x06,0x09,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,
|
||||
0x01,0x02,0x04,0x12,0x04,0x10,0x86,0x45,0x82,0x11,0xe6,0x62,0x43,0x83,0xbc,0x01,
|
||||
0xe4,0x5c,0x48,0x87,0xcd,0x2e,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,
|
||||
0x01,0x01,0x0b,0x05,0x00,0x03,0x81,0x81,0x00,0x42,0x32,0xa9,0x24,0x97,0x8c,0xc5,
|
||||
0x35,0x37,0xe7,0x14,0xf0,0x84,0x7e,0x69,0xf1,0x99,0xf8,0xf0,0x02,0x7d,0xe4,0xd8,
|
||||
0x25,0x78,0x65,0x86,0x40,0xf6,0x30,0xc1,0x50,0x57,0x16,0x13,0xe9,0xe5,0xbc,0xa9,
|
||||
0xbb,0x87,0xce,0xb8,0x0d,0x35,0x5d,0xad,0x68,0x3b,0x34,0x9f,0x82,0x2b,0xe5,0x1f,
|
||||
0xcc,0xd5,0x54,0x8a,0xe3,0xd7,0xed,0xc9,0x7d,0xb6,0x50,0xd2,0xcb,0xc2,0xff,0x03,
|
||||
0x24,0x8c,0xcf,0x49,0x40,0xd4,0x7f,0xcb,0xc0,0x20,0x75,0x78,0x45,0xb8,0x50,0x3c,
|
||||
0x84,0xdd,0xdc,0xb7,0xfc,0xcd,0x64,0xc3,0x81,0xc6,0xb6,0xcd,0xc5,0xe9,0xc4,0x70,
|
||||
0x31,0x30,0x7c,0xff,0x93,0xc3,0x9d,0x55,0x7b,0x32,0x77,0x53,0x07,0x45,0xc2,0x80,
|
||||
0x7b,0x9b,0xfb,0x0e,0x45,0x27,0xf2,0xc5,0x16),
|
||||
chunk_from_chars(
|
||||
0x30,0x82,0x01,0x9c,0x0a,0x01,0x00,0xa0,0x82,0x01,0x95,0x30,0x82,0x01,0x91,0x06,
|
||||
0x09,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x01,0x04,0x82,0x01,0x82,0x30,0x82,
|
||||
0x01,0x7e,0x30,0x81,0xe8,0xa1,0x33,0x30,0x31,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,0x0d,0x30,0x0b,0x06,
|
||||
0x03,0x55,0x04,0x03,0x13,0x04,0x74,0x65,0x73,0x74,0x18,0x0f,0x32,0x30,0x32,0x32,
|
||||
0x31,0x31,0x32,0x32,0x30,0x39,0x32,0x32,0x32,0x34,0x5a,0x30,0x7b,0x30,0x79,0x30,
|
||||
0x3b,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0xbd,0x25,
|
||||
0xa0,0xdf,0xc3,0x21,0xf2,0xd8,0xed,0x19,0x63,0x0a,0x4b,0x90,0x6d,0xc3,0x0f,0xe7,
|
||||
0x79,0x20,0x04,0x14,0xe2,0x6d,0x1e,0xdf,0x83,0x8e,0xa2,0x1f,0xc3,0x00,0xdd,0x44,
|
||||
0x6f,0x8a,0x4d,0x70,0x0c,0x02,0xe3,0x1f,0x02,0x02,0x00,0x80,0xa1,0x16,0x18,0x0f,
|
||||
0x32,0x30,0x32,0x32,0x31,0x31,0x31,0x35,0x31,0x38,0x34,0x30,0x35,0x34,0x5a,0xa0,
|
||||
0x03,0x0a,0x01,0x01,0x18,0x0f,0x32,0x30,0x32,0x32,0x31,0x31,0x32,0x32,0x30,0x39,
|
||||
0x32,0x32,0x32,0x34,0x5a,0xa0,0x11,0x18,0x0f,0x32,0x30,0x32,0x32,0x31,0x31,0x32,
|
||||
0x32,0x31,0x39,0x32,0x32,0x32,0x34,0x5a,0xa1,0x23,0x30,0x21,0x30,0x1f,0x06,0x09,
|
||||
0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x02,0x04,0x12,0x04,0x10,0xf2,0x47,0xbd,
|
||||
0xd2,0xdd,0x6d,0x58,0xba,0xa4,0x6f,0xa5,0xed,0x31,0xb1,0x37,0x89,0x30,0x0d,0x06,
|
||||
0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x81,0x81,0x00,
|
||||
0x17,0x96,0x3b,0x5a,0x4d,0x4e,0x90,0x8a,0xdf,0xe9,0x2b,0x1c,0x48,0x15,0x5d,0x8e,
|
||||
0xed,0xf0,0xa6,0x42,0x3a,0x9c,0x71,0xd9,0x6a,0xa6,0xc1,0xfd,0xef,0xe8,0x0a,0xa1,
|
||||
0x61,0x46,0xe4,0x04,0x5c,0x64,0xaf,0x47,0x95,0xdd,0x4c,0xba,0x8e,0x53,0xf6,0x9b,
|
||||
0xbc,0x16,0xcb,0xeb,0xfe,0x80,0x6a,0x70,0x54,0x10,0x59,0x40,0x5b,0xa0,0x2b,0xb3,
|
||||
0x62,0x27,0x9e,0x5d,0xd2,0xd6,0x15,0x2a,0x9d,0xa3,0xb1,0xcb,0x44,0x09,0xd8,0x29,
|
||||
0xb5,0x55,0xd9,0x63,0x86,0xd5,0xb3,0x3c,0x4b,0x78,0x14,0x5a,0x27,0x37,0x3a,0x28,
|
||||
0xd8,0xae,0x69,0x51,0x2e,0x7d,0xf1,0x06,0xc1,0xac,0x4e,0x5d,0x25,0x7a,0xd2,0xf4,
|
||||
0x41,0xfd,0x9f,0xbf,0x05,0xc1,0x70,0xa5,0x3f,0x7a,0x53,0x06,0x85,0x7b,0xeb,0x94)
|
||||
};
|
||||
|
||||
START_TEST(test_gen_serial_numbers)
|
||||
{
|
||||
chunk_t encoding, serial, serial_asn1;
|
||||
certificate_t *cert, *crl, *ocsp_req, *acert, *acert1;
|
||||
enumerator_t *enumerator;
|
||||
certificate_t *cert, *crl, *ocsp_req, *ocsp_rsp, *acert, *acert1;
|
||||
time_t revocation_time, this_update, next_update;
|
||||
ocsp_request_t *ocsp_request;
|
||||
ocsp_response_t *ocsp_resp;
|
||||
cert_validation_t status;
|
||||
crl_reason_t revocation_reason;
|
||||
crl_t *x509_crl;
|
||||
x509_t *x509;
|
||||
ac_t *ac;
|
||||
size_t offset;
|
||||
u_char *pos;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
/**
|
||||
* Use serial number with canonical encoding (no leading zeroes)
|
||||
@ -502,50 +531,92 @@ START_TEST(test_gen_serial_numbers)
|
||||
pos = encoding.ptr + 68;
|
||||
serial_asn1 = chunk_create(pos, 1 + *pos);
|
||||
ck_assert_chunk_eq(serial_asn1, serial_numbers[_i].serial_asn1);
|
||||
ocsp_req->destroy(ocsp_req);
|
||||
|
||||
/* parse ocsp request */
|
||||
ocsp_req = parse_ocsp_request(encoding);
|
||||
ocsp_request = (ocsp_request_t*)ocsp_req;
|
||||
|
||||
/* test ocsp request */
|
||||
enumerator = ocsp_request->create_request_enumerator(ocsp_request);
|
||||
ck_assert(enumerator->enumerate(enumerator, NULL, NULL, NULL, &serial));
|
||||
ck_assert_chunk_eq(serial, serial_numbers[_i].serial);
|
||||
enumerator->destroy(enumerator);
|
||||
chunk_free(&encoding);
|
||||
|
||||
/* create ocsp response */
|
||||
status = (_i % 2) ? VALIDATION_GOOD : VALIDATION_REVOKED;
|
||||
ocsp_rsp = create_ocsp_response(ocsp_request, status, cert);
|
||||
|
||||
/* the ASN.1 TLV (Type-Length-Value) encoding of an OCSP response is
|
||||
*
|
||||
* 0 "OCSPResponse", ASN1_SEQUENCE
|
||||
* 1 "responseStatus", ASN1_ENUMERATED
|
||||
* 1 "responseBytesContext", ASN1_CONTEXT_C_0
|
||||
* 2 "responseBytes", ASN1_SEQUENCE
|
||||
* 3 "responseType", ASN1_OID
|
||||
* 3 "response", ASN1_OCTET_STRING
|
||||
* 4 "BasicOCSPResponse", ASN1_SEQUENCE
|
||||
* 5 "tbsResponseData", ASN1_SEQUENCE
|
||||
* 6 "responderIdContext", ASN1_CONTEXT_C_1
|
||||
* 6 "producedAt", ASN1_GENERALIZEDTIME
|
||||
* 6 "responses", ASN1_SEQUENCE
|
||||
* 7 "singleResponse", ASN1_SEQUENCE
|
||||
* 8 "certID", ASN1_SEQUENCE
|
||||
* 9 "algorithm", ASN1_SEQUENCE
|
||||
* 9 "issuerNameHash", ASN1_OCTET_STRING
|
||||
* 9 "issuerKeyHash", ASN1_OCTET_STRING
|
||||
* 9 "serialNumber", ASN1_INTEGER
|
||||
*
|
||||
* The one octet length field of the serialNumber (8) is at
|
||||
* pos = 4 (TL0) + 3 (TLV1) + 4 (TL1) + 4 (TL2) + 11 (TLV3) + 4 (TL3) +
|
||||
4 (TL4) + 3 (TL5) + 53 (TLV6) + 17 (TVL6) + 2 (TL6) + 2 (TL7) +
|
||||
2 (TL8) + 11 (TLV9) + 22 (TLV9) + 22 (TLV9) + 1 (T9) = 169
|
||||
*/
|
||||
ck_assert(ocsp_rsp->get_encoding(ocsp_rsp, CERT_ASN1_DER, &encoding));
|
||||
DBG2(DBG_LIB, "ocsp response: %B", &encoding);
|
||||
|
||||
/* check ASN.1 integer encoding of requested serial number */
|
||||
pos = encoding.ptr + 169;
|
||||
serial_asn1 = chunk_create(pos, 1 + *pos);
|
||||
ck_assert_chunk_eq(serial_asn1, serial_numbers[_i].serial_asn1);
|
||||
ocsp_rsp->destroy(ocsp_rsp);
|
||||
|
||||
/* parse ocsp response */
|
||||
ocsp_rsp = parse_ocsp_response(encoding);
|
||||
ocsp_resp = (ocsp_response_t*)ocsp_rsp;
|
||||
|
||||
/* test ocsp response */
|
||||
if (_i == 2 || _i == 3)
|
||||
ck_assert_chunk_eq(ocsp_request->get_nonce(ocsp_request),
|
||||
ocsp_resp->get_nonce(ocsp_resp));
|
||||
|
||||
status = ocsp_resp->get_status(ocsp_resp, x509, x509, &revocation_time,
|
||||
&revocation_reason, &this_update, &next_update);
|
||||
if (_i % 2)
|
||||
{
|
||||
certificate_t *ocsp_rsp;
|
||||
ocsp_response_t *ocsp_resp;
|
||||
cert_validation_t status;
|
||||
crl_reason_t revocation_reason;
|
||||
time_t revocation_time, this_update, next_update;
|
||||
enumerator_t *enumerator;
|
||||
|
||||
ocsp_rsp = parse_ocsp_response(ocsp_responses[_i-2]);
|
||||
ocsp_resp = (ocsp_response_t*)ocsp_rsp;
|
||||
|
||||
status = ocsp_resp->get_status(ocsp_resp, x509, x509, &revocation_time,
|
||||
&revocation_reason, &this_update, &next_update);
|
||||
if (_i == 2)
|
||||
{
|
||||
ck_assert(status == VALIDATION_GOOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(status == VALIDATION_REVOKED);
|
||||
ck_assert(revocation_reason == CRL_REASON_KEY_COMPROMISE);
|
||||
}
|
||||
|
||||
enumerator = ocsp_resp->create_response_enumerator(ocsp_resp);
|
||||
ck_assert(enumerator->enumerate(enumerator, &serial, &status,
|
||||
&revocation_time, &revocation_reason));
|
||||
ck_assert_chunk_eq(serial, serial_numbers[_i].serial);
|
||||
if (_i == 2)
|
||||
{
|
||||
ck_assert(status == VALIDATION_GOOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(status == VALIDATION_REVOKED);
|
||||
ck_assert(revocation_reason == CRL_REASON_KEY_COMPROMISE);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
ocsp_rsp->destroy(ocsp_rsp);
|
||||
ck_assert(status == VALIDATION_GOOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(status == VALIDATION_REVOKED);
|
||||
ck_assert(revocation_reason == CRL_REASON_KEY_COMPROMISE);
|
||||
}
|
||||
|
||||
enumerator = ocsp_resp->create_response_enumerator(ocsp_resp);
|
||||
ck_assert(enumerator->enumerate(enumerator, &serial, &status,
|
||||
&revocation_time, &revocation_reason));
|
||||
ck_assert_chunk_eq(serial, serial_numbers[_i].serial);
|
||||
if (_i % 2)
|
||||
{
|
||||
ck_assert(status == VALIDATION_GOOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
ck_assert(status == VALIDATION_REVOKED);
|
||||
ck_assert(revocation_reason == CRL_REASON_KEY_COMPROMISE);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
chunk_free(&encoding);
|
||||
|
||||
/* create attribute certificate */
|
||||
acert = create_acert(serial_numbers[_i].serial, cert);
|
||||
@ -633,6 +704,7 @@ START_TEST(test_gen_serial_numbers)
|
||||
cert->destroy(cert);
|
||||
crl->destroy(crl);
|
||||
ocsp_req->destroy(ocsp_req);
|
||||
ocsp_rsp->destroy(ocsp_rsp);
|
||||
acert->destroy(acert);
|
||||
acert1->destroy(acert1);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 Tobias Brunner
|
||||
* Copyright (C) 2023 Andreas Steffen
|
||||
* Copyright (C) 2005-2006 Martin Willi
|
||||
* Copyright (C) 2005 Jan Hutter
|
||||
*
|
||||
@ -744,6 +745,71 @@ chunk_t chunk_to_base32(chunk_t chunk, char *buf)
|
||||
return chunk_create(buf, len * 8 / 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
chunk_t chunk_to_dec(chunk_t chunk, char *buf)
|
||||
{
|
||||
int len, i, i_buf, i_bin = 0;
|
||||
uint16_t remainder;
|
||||
chunk_t bin;
|
||||
|
||||
/* Determine the number of needed decimal digits:
|
||||
* 10^len > 2^(8*chunk.len) =>
|
||||
* len > log(256) * chunk.len =>
|
||||
* len > 2.4083 * chunk.len
|
||||
*/
|
||||
len = (int)(2.4083 * (double)chunk.len) + 1;
|
||||
|
||||
if (!buf)
|
||||
{
|
||||
buf = malloc(len + 1);
|
||||
}
|
||||
i_buf = len;
|
||||
buf[i_buf] = '\0';
|
||||
bin = chunk_clone(chunk);
|
||||
while (i_bin < bin.len)
|
||||
{
|
||||
remainder = 0;
|
||||
for (i = i_bin; i < bin.len; i++)
|
||||
{
|
||||
remainder = bin.ptr[i] + (remainder << 8);
|
||||
if (remainder < 10)
|
||||
{
|
||||
remainder = bin.ptr[i];
|
||||
bin.ptr[i] = 0;
|
||||
if (i == i_bin)
|
||||
{
|
||||
i_bin++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bin.ptr[i] = remainder / 10;
|
||||
remainder %= 10;
|
||||
}
|
||||
}
|
||||
if (i_buf > 0)
|
||||
{
|
||||
buf[--i_buf] = 0x30 + remainder;
|
||||
}
|
||||
}
|
||||
chunk_free(&bin);
|
||||
|
||||
/* align decimal number to the start of the string */
|
||||
if (i_buf > 0)
|
||||
{
|
||||
len -= i_buf;
|
||||
|
||||
for (i = 0; i <= len; i++)
|
||||
{
|
||||
buf[i] = buf[i + i_buf];
|
||||
}
|
||||
}
|
||||
|
||||
return chunk_create(buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Described in header.
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 Tobias Brunner
|
||||
* Copyright (C) 2023 Andreas Steffen
|
||||
* Copyright (C) 2005-2008 Martin Willi
|
||||
* Copyright (C) 2005 Jan Hutter
|
||||
*
|
||||
@ -213,6 +214,18 @@ chunk_t chunk_from_base64(chunk_t base64, char *buf);
|
||||
*/
|
||||
chunk_t chunk_to_base32(chunk_t chunk, char *buf);
|
||||
|
||||
/**
|
||||
* Convert a chunk of data to decimal encoding.
|
||||
*
|
||||
* The resulting string is '\\0' terminated, but the chunk does not include
|
||||
* the '\\0'. If buf is supplied, it must hold at least (chunk.len * 2.41 + 1).
|
||||
*
|
||||
* @param chunk data to convert to decimal encoding
|
||||
* @param buf buffer to write to, NULL to malloc
|
||||
* @return chunk of encoded data
|
||||
*/
|
||||
chunk_t chunk_to_dec(chunk_t chunk, char *buf);
|
||||
|
||||
/**
|
||||
* Free contents of a chunk
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@ pki_SOURCES = pki.c pki.h pki_cert.c pki_cert.h command.c command.h \
|
||||
commands/gen.c \
|
||||
commands/issue.c \
|
||||
commands/keyid.c \
|
||||
commands/ocsp.c \
|
||||
commands/pkcs12.c \
|
||||
commands/pkcs7.c \
|
||||
commands/print.c \
|
||||
|
@ -25,7 +25,7 @@
|
||||
/**
|
||||
* Maximum number of commands (+1).
|
||||
*/
|
||||
#define MAX_COMMANDS 18
|
||||
#define MAX_COMMANDS 19
|
||||
|
||||
/**
|
||||
* Maximum number of options in a command (+3)
|
||||
|
601
src/pki/commands/ocsp.c
Normal file
601
src/pki/commands/ocsp.c
Normal file
@ -0,0 +1,601 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 <time.h>
|
||||
|
||||
#include "pki.h"
|
||||
|
||||
#include <credentials/sets/mem_cred.h>
|
||||
#include <credentials/certificates/ocsp_request.h>
|
||||
#include <credentials/certificates/ocsp_response.h>
|
||||
#include <credentials/certificates/ocsp_single_response.h>
|
||||
#include <credentials/certificates/ocsp_responder.h>
|
||||
|
||||
/*
|
||||
* Verifies the optional OCSP request signature generated by an OCSP requestor
|
||||
*/
|
||||
static bool verify_signature(certificate_t *ocsp_req, mem_cred_t *creds)
|
||||
{
|
||||
identification_t *signer;
|
||||
certificate_t *signer_cert, *cert_found;
|
||||
ocsp_request_t *ocsp_request;
|
||||
enumerator_t *certs;
|
||||
bool trusted = TRUE;
|
||||
|
||||
ocsp_request = (ocsp_request_t*)ocsp_req;
|
||||
|
||||
signer_cert = ocsp_request->get_signer_cert(ocsp_request);
|
||||
if (signer_cert)
|
||||
{
|
||||
signer = signer_cert->get_subject(signer_cert);
|
||||
|
||||
/* establish trust relative to root CA */
|
||||
creds->add_cert(creds, FALSE, signer_cert->get_ref(signer_cert));
|
||||
certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
|
||||
KEY_ANY, signer, FALSE);
|
||||
trusted = certs->enumerate(certs, &cert_found, NULL) &&
|
||||
(cert_found == signer_cert);
|
||||
certs->destroy(certs);
|
||||
trusted = trusted && ocsp_req->issued_by(ocsp_req, signer_cert, NULL);
|
||||
}
|
||||
DBG1(DBG_APP, "requestor is %strusted", trusted ? "" : "not ");
|
||||
|
||||
return trusted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the CA certificate of the certificate issuer based on the issuerKey and
|
||||
* issuerName hashes contained in the OCSP request
|
||||
*/
|
||||
static bool find_issuer_cacert(hash_algorithm_t hashAlgorithm,
|
||||
chunk_t issuerKeyHash, chunk_t issuerNameHash,
|
||||
certificate_t **issuer_cacert)
|
||||
{
|
||||
bool issuerKeyHash_ok = FALSE, issuerNameHash_ok = FALSE;
|
||||
certificate_t *candidate;
|
||||
identification_t *issuer;
|
||||
public_key_t *public;
|
||||
x509_t *x509_ca;
|
||||
chunk_t caKeyHash = chunk_empty, caNameHash = chunk_empty;
|
||||
hasher_t *hasher;
|
||||
enumerator_t *certs;
|
||||
|
||||
*issuer_cacert = NULL;
|
||||
|
||||
if (hashAlgorithm != HASH_SHA1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
certs = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509,
|
||||
KEY_ANY, NULL, TRUE);
|
||||
while (certs->enumerate(certs, &candidate))
|
||||
{
|
||||
x509_ca = (x509_t*)candidate;
|
||||
|
||||
if (!(x509_ca->get_flags(x509_ca) & X509_CA))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* retrieve the caKeyHash of the candidate issuer */
|
||||
public = candidate->get_public_key(candidate);
|
||||
if (!public)
|
||||
{
|
||||
DBG1(DBG_APP, "could not retrieve public key of cacert candidate");
|
||||
break;
|
||||
}
|
||||
if (!public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &caKeyHash))
|
||||
{
|
||||
DBG1(DBG_APP, "could not retrieve SHA1 hash of public key");
|
||||
public->destroy(public);
|
||||
break;
|
||||
}
|
||||
|
||||
issuerKeyHash_ok = chunk_equals_const(issuerKeyHash, caKeyHash);
|
||||
public->destroy(public);
|
||||
if (!issuerKeyHash_ok)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* compute the caNameHash of the candidate issuer */
|
||||
issuer = candidate->get_subject(candidate);
|
||||
|
||||
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
if (!hasher)
|
||||
{
|
||||
DBG1(DBG_APP, "failed to create SHA1 hasher");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasher->allocate_hash(hasher, issuer->get_encoding(issuer),
|
||||
&caNameHash))
|
||||
{
|
||||
hasher->destroy(hasher);
|
||||
DBG1(DBG_APP, "failed to compute SHA1 caNameHash");
|
||||
break;
|
||||
}
|
||||
hasher->destroy(hasher);
|
||||
|
||||
issuerNameHash_ok = chunk_equals_const(issuerNameHash, caNameHash);
|
||||
chunk_free(&caNameHash);
|
||||
if (!issuerNameHash_ok)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we found the issuer */
|
||||
DBG1(DBG_APP, "issuer: \"%Y\"", issuer);
|
||||
*issuer_cacert = candidate;
|
||||
|
||||
break;
|
||||
}
|
||||
certs->destroy(certs);
|
||||
|
||||
DBG1(DBG_APP, " issuerKeyHash: %#B (%s)", &issuerKeyHash,
|
||||
issuerKeyHash_ok ? "ok" : "no match");
|
||||
DBG1(DBG_APP, " issuerNameHash: %#B (%s)", &issuerNameHash,
|
||||
issuerNameHash_ok ? "ok" : "no match");
|
||||
|
||||
return (*issuer_cacert != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an OCSP signer certificate. Either the certificate of the CA itself that
|
||||
* issued the end entitity certificate, the certificate of an OCSP signer
|
||||
* delegated by the CA via the standard OCSPSigning Extended Key Usage (EKU)
|
||||
* flag or a self-signed OCSP signer certificate when multiple issuer OCSP
|
||||
* requests have to be supported.
|
||||
*/
|
||||
static void find_ocsp_signer(certificate_t *first_issuer, bool *self_signed,
|
||||
certificate_t **cert, private_key_t **key)
|
||||
{
|
||||
certificate_t *candidate;
|
||||
identification_t *keyid;
|
||||
private_key_t *key_candidate;
|
||||
chunk_t subKeyId, authKeyId, subKeyId_ca;
|
||||
x509_t *x509, *x509_ca;
|
||||
x509_flag_t flags;
|
||||
enumerator_t *certs;
|
||||
|
||||
/* retrieve the subjectKeyIdentifier of the first issuer certificate */
|
||||
x509_ca = (x509_t*)first_issuer;
|
||||
subKeyId_ca = x509_ca->get_subjectKeyIdentifier(x509_ca);
|
||||
|
||||
/* iterate over all certificates */
|
||||
certs = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509,
|
||||
KEY_ANY, NULL, TRUE);
|
||||
while (certs->enumerate(certs, &candidate))
|
||||
{
|
||||
/* get the flags and key identfiers of the candidate certificate */
|
||||
x509 = (x509_t*)candidate;
|
||||
flags = x509->get_flags(x509);
|
||||
subKeyId = x509->get_subjectKeyIdentifier(x509);
|
||||
authKeyId = x509->get_authKeyIdentifier(x509);
|
||||
|
||||
/* get a private key matching the candidate certificate */
|
||||
keyid = identification_create_from_encoding(ID_KEY_ID, subKeyId);
|
||||
key_candidate = lib->credmgr->get_private(lib->credmgr,
|
||||
KEY_ANY, keyid, NULL);
|
||||
keyid->destroy(keyid);
|
||||
|
||||
if (key_candidate)
|
||||
{
|
||||
if (((flags & X509_OCSP_SIGNER && chunk_equals(authKeyId, subKeyId_ca)) ||
|
||||
(flags & X509_CA && chunk_equals(subKeyId, subKeyId_ca))) && !*key)
|
||||
{
|
||||
*cert = candidate;
|
||||
*key = key_candidate;
|
||||
continue;
|
||||
}
|
||||
else if (flags & X509_SELF_SIGNED && !(flags & X509_CA))
|
||||
{
|
||||
DESTROY_IF(*key);
|
||||
*cert = candidate;
|
||||
*key = key_candidate;
|
||||
*self_signed = TRUE;
|
||||
break;
|
||||
}
|
||||
key_candidate->destroy(key_candidate);
|
||||
}
|
||||
}
|
||||
certs->destroy(certs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show|Respond to OCSP requests
|
||||
*/
|
||||
static int ocsp()
|
||||
{
|
||||
char *arg, *file = NULL, *error = NULL;
|
||||
identification_t *requestor;
|
||||
cred_encoding_type_t form = CERT_ASN1_DER;
|
||||
private_key_t *key = NULL;
|
||||
certificate_t *cert = NULL, *ocsp_req = NULL, *ocsp_resp = NULL;
|
||||
certificate_t *cacert = NULL, *first_issuer = NULL;
|
||||
ocsp_request_t *ocsp_request;
|
||||
ocsp_status_t ocsp_status = OCSP_SUCCESSFUL;
|
||||
ocsp_responder_t *ocsp_responder = NULL;
|
||||
linked_list_t *responses = NULL;
|
||||
chunk_t encoding = chunk_empty, nonce = chunk_empty;
|
||||
chunk_t issuerNameHash, issuerKeyHash, serialNumber;
|
||||
hash_algorithm_t hashAlgorithm = HASH_SHA1, digest = HASH_UNKNOWN;
|
||||
signature_params_t *scheme = NULL;
|
||||
time_t lifetime = 0;
|
||||
mem_cred_t *creds;
|
||||
bool multiple_issuers = FALSE, self_signed = FALSE;
|
||||
enumerator_t *enumerator;
|
||||
int res = 1;
|
||||
|
||||
enum {
|
||||
OP_SHOW,
|
||||
OP_RESPOND,
|
||||
} op = OP_SHOW;
|
||||
|
||||
bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE,
|
||||
lib->ns);
|
||||
|
||||
creds = mem_cred_create();
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (command_getopt(&arg))
|
||||
{
|
||||
case 'h':
|
||||
goto usage;
|
||||
case 'i':
|
||||
file = arg;
|
||||
continue;
|
||||
case 'r':
|
||||
op = OP_RESPOND;
|
||||
continue;
|
||||
case 'k':
|
||||
key = lib->creds->create(lib->creds,
|
||||
CRED_PRIVATE_KEY, KEY_ANY,
|
||||
BUILD_FROM_FILE, arg, BUILD_END);
|
||||
if (!key)
|
||||
{
|
||||
error = "parsing private key failed";
|
||||
goto usage;
|
||||
}
|
||||
creds->add_key(creds, key);
|
||||
continue;
|
||||
case 'c':
|
||||
cert = lib->creds->create(lib->creds,
|
||||
CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_FROM_FILE, arg, BUILD_END);
|
||||
if (!cert)
|
||||
{
|
||||
error = "parsing certificate failed";
|
||||
goto usage;
|
||||
}
|
||||
creds->add_cert(creds, TRUE, cert);
|
||||
continue;
|
||||
case 'C':
|
||||
cacert = lib->creds->create(lib->creds,
|
||||
CRED_CERTIFICATE, CERT_X509,
|
||||
BUILD_FROM_FILE, arg, BUILD_END);
|
||||
if (!cacert)
|
||||
{
|
||||
error = "parsing CA certificate failed";
|
||||
goto usage;
|
||||
}
|
||||
creds->add_cert(creds, TRUE, cacert);
|
||||
continue;
|
||||
case 'l':
|
||||
lifetime = atoi(arg) * 60;
|
||||
if (!lifetime)
|
||||
{
|
||||
error = "invalid --lifetime value";
|
||||
goto usage;
|
||||
}
|
||||
continue;
|
||||
case 'g':
|
||||
if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
|
||||
{
|
||||
error = "invalid --digest type";
|
||||
goto usage;
|
||||
}
|
||||
continue;
|
||||
case 'R':
|
||||
if (!parse_rsa_padding(arg, &pss))
|
||||
{
|
||||
error = "invalid RSA padding";
|
||||
goto usage;
|
||||
}
|
||||
continue;
|
||||
case EOF:
|
||||
break;
|
||||
default:
|
||||
error = "invalid --ocsp option";
|
||||
goto usage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
lib->credmgr->add_local_set(lib->credmgr, &creds->set, FALSE);
|
||||
|
||||
responses = linked_list_create();
|
||||
|
||||
if (op == OP_RESPOND && !cacert)
|
||||
{
|
||||
error = "respond mode requires a ca certificate";
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (op == OP_RESPOND && !key)
|
||||
{
|
||||
error = "respond mode requires a private signer key";
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* re-initialize signing certificate and key pointers */
|
||||
cert = NULL;
|
||||
key = NULL;
|
||||
|
||||
if (file)
|
||||
{
|
||||
ocsp_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
|
||||
CERT_X509_OCSP_REQUEST,
|
||||
BUILD_FROM_FILE, file, BUILD_END);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_t chunk;
|
||||
|
||||
set_file_mode(stdin, CERT_ASN1_DER);
|
||||
if (!chunk_from_fd(0, &chunk))
|
||||
{
|
||||
fprintf(stderr, "%s: ", strerror(errno));
|
||||
error = "reading certificate request failed";
|
||||
goto end;
|
||||
}
|
||||
ocsp_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
|
||||
CERT_X509_OCSP_REQUEST,
|
||||
BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
|
||||
free(chunk.ptr);
|
||||
}
|
||||
if (!ocsp_req)
|
||||
{
|
||||
if (op == OP_SHOW)
|
||||
{
|
||||
error = "malformed OCSP request";
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_APP, "malformed OCSP request");
|
||||
ocsp_status = OCSP_MALFORMEDREQUEST;
|
||||
goto gen;
|
||||
}
|
||||
}
|
||||
ocsp_request = (ocsp_request_t*)ocsp_req;
|
||||
|
||||
/* does the requestor identify itself? */
|
||||
requestor = ocsp_req->get_subject(ocsp_req);
|
||||
if (requestor)
|
||||
{
|
||||
DBG1(DBG_APP, "requestor: \"%Y\"", requestor);
|
||||
|
||||
/* verify an optional ocsp request signature */
|
||||
if (!verify_signature(ocsp_req, creds))
|
||||
{
|
||||
ocsp_status = OCSP_UNAUTHORIZED;
|
||||
goto gen;
|
||||
}
|
||||
}
|
||||
|
||||
/* extract nonce from OCSP request */
|
||||
nonce = ocsp_request->get_nonce(ocsp_request);
|
||||
if (nonce.len > 0)
|
||||
{
|
||||
DBG1(DBG_APP, "nonce: %#B", &nonce);
|
||||
}
|
||||
|
||||
/* check for an ocsp responder */
|
||||
if (op == OP_RESPOND)
|
||||
{
|
||||
ocsp_responder = lib->get(lib, "ocsp-responder");
|
||||
if (!ocsp_responder)
|
||||
{
|
||||
DBG1(DBG_APP, " no ocsp-responder found");
|
||||
ocsp_status = OCSP_INTERNALERROR;
|
||||
goto gen;
|
||||
}
|
||||
}
|
||||
|
||||
/* enumerate over the ocsp requests and try to identify the issuers */
|
||||
enumerator = ocsp_request->create_request_enumerator(ocsp_request);
|
||||
while (enumerator->enumerate(enumerator, &hashAlgorithm, &issuerNameHash,
|
||||
&issuerKeyHash, &serialNumber))
|
||||
{
|
||||
certificate_t *issuer_cacert = NULL;
|
||||
cert_validation_t status = VALIDATION_FAILED;
|
||||
ocsp_single_response_t *response = NULL;
|
||||
crl_reason_t revocationReason;
|
||||
time_t revocationTime;
|
||||
|
||||
/* search for the matching issuer cacert */
|
||||
if (find_issuer_cacert(hashAlgorithm, issuerKeyHash, issuerNameHash,
|
||||
&issuer_cacert))
|
||||
{
|
||||
if (!first_issuer)
|
||||
{
|
||||
first_issuer = issuer_cacert;
|
||||
|
||||
/* search for a signing certificate plus matching private key */
|
||||
if (op == OP_RESPOND)
|
||||
{
|
||||
find_ocsp_signer(first_issuer, &self_signed, &cert, &key);
|
||||
}
|
||||
}
|
||||
else if (first_issuer != issuer_cacert)
|
||||
{
|
||||
multiple_issuers = TRUE;
|
||||
}
|
||||
}
|
||||
DBG1(DBG_APP, " serialNumber: %#B", &serialNumber);
|
||||
|
||||
if (op == OP_SHOW)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* fill in the OCSP single response
|
||||
*/
|
||||
response = ocsp_single_response_create();
|
||||
response->hashAlgorithm = hashAlgorithm;
|
||||
response->issuerNameHash = chunk_clone(issuerNameHash);
|
||||
response->issuerKeyHash = chunk_clone(issuerKeyHash);
|
||||
response->serialNumber = chunk_clone(serialNumber);
|
||||
response->thisUpdate = time(NULL);
|
||||
DBG1(DBG_APP, " thisUpdate: %#T", &response->thisUpdate, TRUE);
|
||||
|
||||
if (lifetime)
|
||||
{
|
||||
response->nextUpdate = response->thisUpdate + lifetime;
|
||||
DBG1(DBG_APP, " nextUpdate: %#T", &response->nextUpdate, TRUE);
|
||||
}
|
||||
|
||||
if (issuer_cacert && (issuer_cacert == first_issuer || self_signed))
|
||||
{
|
||||
status = ocsp_responder->get_status(ocsp_responder,
|
||||
issuer_cacert, serialNumber,
|
||||
&revocationTime, &revocationReason);
|
||||
}
|
||||
DBG1(DBG_APP, " certValidation: %N", cert_validation_names, status);
|
||||
response->status = status;
|
||||
|
||||
if (status == VALIDATION_REVOKED || status == VALIDATION_ON_HOLD)
|
||||
{
|
||||
DBG1(DBG_APP, " revocationTime: %T", &revocationTime, TRUE);
|
||||
DBG1(DBG_APP, " revocationReason: %N", crl_reason_names,
|
||||
revocationReason);
|
||||
response->revocationTime = revocationTime;
|
||||
response->revocationReason = revocationReason;
|
||||
}
|
||||
responses->insert_last(responses, response);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (multiple_issuers)
|
||||
{
|
||||
DBG1(DBG_APP, "there are multiple known issuers");
|
||||
}
|
||||
|
||||
gen:
|
||||
if (op == OP_RESPOND)
|
||||
{
|
||||
if (cert)
|
||||
{
|
||||
DBG1(DBG_APP, "%s \"%Y\"",
|
||||
self_signed ? "self-signed signer:" : "trusted signer: ",
|
||||
cert->get_subject(cert));
|
||||
|
||||
scheme = get_signature_scheme(key, digest, pss);
|
||||
if (scheme)
|
||||
{
|
||||
if (digest == HASH_UNKNOWN)
|
||||
{
|
||||
digest = hasher_from_signature_scheme(scheme->scheme,
|
||||
scheme->params);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_APP, "no signature scheme found");
|
||||
ocsp_status = OCSP_INTERNALERROR;
|
||||
}
|
||||
}
|
||||
DBG1(DBG_APP, "ocspResponseStatus: %N", ocsp_status_names, ocsp_status);
|
||||
|
||||
enumerator = responses->create_enumerator(responses);
|
||||
ocsp_resp = lib->creds->create(lib->creds, CRED_CERTIFICATE,
|
||||
CERT_X509_OCSP_RESPONSE,
|
||||
BUILD_OCSP_STATUS, ocsp_status,
|
||||
BUILD_OCSP_RESPONSES, enumerator,
|
||||
BUILD_SIGNING_KEY, key,
|
||||
BUILD_SIGNING_CERT, cert,
|
||||
BUILD_SIGNATURE_SCHEME, scheme,
|
||||
BUILD_NONCE, nonce,
|
||||
BUILD_END);
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
if (!ocsp_resp)
|
||||
{
|
||||
error = "generating OCSP response failed";
|
||||
goto end;
|
||||
}
|
||||
if (!ocsp_resp->get_encoding(ocsp_resp, form, &encoding))
|
||||
{
|
||||
error = "encoding OCSP response failed";
|
||||
goto end;
|
||||
}
|
||||
set_file_mode(stdout, form);
|
||||
if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
|
||||
{
|
||||
error = "writing OCSP response failed";
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
res = 0;
|
||||
|
||||
end:
|
||||
DESTROY_IF(key);
|
||||
lib->credmgr->remove_local_set(lib->credmgr, &creds->set);
|
||||
creds->destroy(creds);
|
||||
responses->destroy_offset(responses,
|
||||
offsetof(ocsp_single_response_t, destroy));
|
||||
DESTROY_IF(ocsp_req);
|
||||
DESTROY_IF(ocsp_resp);
|
||||
signature_params_destroy(scheme);
|
||||
free(encoding.ptr);
|
||||
if (error)
|
||||
{
|
||||
fprintf(stderr, "%s\n", error);
|
||||
}
|
||||
return res;
|
||||
|
||||
usage:
|
||||
creds->destroy(creds);
|
||||
return command_usage(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the command.
|
||||
*/
|
||||
static void __attribute__ ((constructor))reg()
|
||||
{
|
||||
command_register((command_t) {
|
||||
ocsp, 'o', "ocsp", "OCSP responder",
|
||||
{"[--in file] [--respond] [--cert file]+ [--key file]+ [--cacert file]+ ",
|
||||
"[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
|
||||
"[--rsa-padding pkcs1|pss] [--lifetime minutes]"},
|
||||
{
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"respond", 'r', 0, "respond to OCSP request with OCSP response"},
|
||||
{"in", 'i', 1, "input file, default: stdin"},
|
||||
{"key", 'k', 1, "path to OCSP signing private key (can be used multiple times)"},
|
||||
{"cert", 'c', 1, "path to OCSP signing certificate (can be used multiple times"},
|
||||
{"cacert", 'C', 1, "CA certificate (can be used multiple times"},
|
||||
{"digest", 'g', 1, "digest for signature creation, default: key-specific"},
|
||||
{"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
|
||||
{"lifetime", 'l', 1, "validity in minutes of the OCSP response (if missing, nextUpdate is omitted)"},
|
||||
}
|
||||
});
|
||||
}
|
@ -288,7 +288,7 @@ static int pkcs7()
|
||||
hash_algorithm_t digest = HASH_UNKNOWN;
|
||||
signature_params_t *scheme = NULL;
|
||||
mem_cred_t *creds;
|
||||
int res = 0;
|
||||
int res = 1;
|
||||
FILE *in;
|
||||
enum {
|
||||
OP_NONE,
|
||||
@ -450,7 +450,7 @@ static int pkcs7()
|
||||
case OP_DECRYPT:
|
||||
if (!key)
|
||||
{
|
||||
fprintf(stderr, "decryption requires a private key\n");
|
||||
error = "decryption requires a private key";
|
||||
break;
|
||||
}
|
||||
res = decrypt(data);
|
||||
|
@ -124,6 +124,16 @@ static int print()
|
||||
type = CRED_PRIVATE_KEY;
|
||||
subtype = KEY_BLISS;
|
||||
}
|
||||
else if (streq(arg, "ocsp-req"))
|
||||
{
|
||||
type = CRED_CERTIFICATE;
|
||||
subtype = CERT_X509_OCSP_REQUEST;
|
||||
}
|
||||
else if (streq(arg, "ocsp-rsp"))
|
||||
{
|
||||
type = CRED_CERTIFICATE;
|
||||
subtype = CERT_X509_OCSP_RESPONSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return command_usage( "invalid input type");
|
||||
@ -202,7 +212,7 @@ static void __attribute__ ((constructor))reg()
|
||||
{ print, 'a', "print",
|
||||
"print a credential in a human readable form",
|
||||
{"[--in file|--keyid hex]",
|
||||
"[--type x509|crl|ac|pub|priv|rsa|ecdsa|ed25519|ed448|bliss]"},
|
||||
"[--type x509|crl|ac|pub|priv|rsa|ecdsa|ed25519|ed448|bliss|ocsp-req|ocsp-rsp]"},
|
||||
{
|
||||
{"help", 'h', 0, "show usage information"},
|
||||
{"in", 'i', 1, "input file, default: stdin"},
|
||||
|
@ -240,7 +240,7 @@ static int sign_crl()
|
||||
}
|
||||
else if (streq(arg, "cessation-of-operation"))
|
||||
{
|
||||
reason = CRL_REASON_CESSATION_OF_OPERATON;
|
||||
reason = CRL_REASON_CESSATION_OF_OPERATION;
|
||||
}
|
||||
else if (streq(arg, "certificate-hold"))
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ man1_MANS = \
|
||||
pki---gen.1 \
|
||||
pki---issue.1 \
|
||||
pki---keyid.1 \
|
||||
pki---ocsp.1 \
|
||||
pki---pkcs7.1 \
|
||||
pki---print.1 \
|
||||
pki---pub.1 \
|
||||
|
247
src/pki/man/pki---ocsp.1.in
Normal file
247
src/pki/man/pki---ocsp.1.in
Normal file
@ -0,0 +1,247 @@
|
||||
.TH "PKI \-\-OCSP" 1 "2023-10-29" "@PACKAGE_VERSION@" "strongSwan"
|
||||
.
|
||||
.SH "NAME"
|
||||
.
|
||||
pki \-\-ocsp \- OCSP request parser and OCSP responder.
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
.
|
||||
.SY pki\ \-\-ocsp
|
||||
.OP \-\-in file
|
||||
.OP \-\-cacert file
|
||||
.OP \-\-debug level
|
||||
.YS
|
||||
|
||||
.SY pki\ \-\-ocsp
|
||||
.BI \-\-respond
|
||||
.OP \-\-in file
|
||||
.BI \-\-cacert\~ file
|
||||
.BI \-\-key\~ file
|
||||
.OP \-\-cert file
|
||||
.OP \-\-lifetime minutes
|
||||
.OP \-\-digest digest
|
||||
.OP \-\-rsa\-padding padding
|
||||
.OP \-\-debug level
|
||||
.YS
|
||||
.
|
||||
.SY pki\ \-\-ocsp
|
||||
.BI \-\-options\~ file
|
||||
.YS
|
||||
.
|
||||
.SY "pki \-\-ocsp"
|
||||
.B \-h
|
||||
|
|
||||
.B \-\-help
|
||||
.YS
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
.
|
||||
This sub-command of
|
||||
.BR pki (1)
|
||||
parses an
|
||||
.B Online Certificate Status Protocol
|
||||
(OCSP) request as defined by RFC 6960 and with the
|
||||
.B --respond
|
||||
option generates an OCSP response based on the OCSP request.
|
||||
The certificate status is directly retrieved from the internal
|
||||
.B certificate
|
||||
database of an
|
||||
.B OpenXPKI
|
||||
(https://openxpki.org) server. The
|
||||
.B --respond
|
||||
option requires the
|
||||
.B openxpki
|
||||
and
|
||||
.B mysql
|
||||
libstrongswan plugins in order to access the
|
||||
.B certificate
|
||||
database of the
|
||||
.B OpenXPKI
|
||||
server running on the same host.
|
||||
.
|
||||
.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 "\-i, \-\-in " file
|
||||
OCSP request. If not given, the OCSP request is read from
|
||||
\fISTDIN\fR.
|
||||
.TP
|
||||
.BI "\-C, \-\-cacert " file
|
||||
CA certificate corresponding to one of the issuer hashes contained in the OCSP
|
||||
request. If the OCSP request is signed, a CA certificate forming the
|
||||
trust chain. Can be used multiple times.
|
||||
.TP
|
||||
.BI "\-k, \-\-key " file
|
||||
OCSP signer key. Can be used multiple times.
|
||||
.TP
|
||||
.BI "\-c, \-\-cert " file
|
||||
OCSP signer certificate (if it is not a CA certificate). Can be used
|
||||
multiple times.
|
||||
.TP
|
||||
.BI "\-l, \-\-lifetime " minutes
|
||||
Validity in minutes of the OCSP response (if missing, nextUpdate is omitted).
|
||||
.TP
|
||||
.BI "\-g, \-\-digest " digest
|
||||
Digest to use for signature creation. One of \fImd5\fR, \fIsha1\fR,
|
||||
\fIsha224\fR, \fIsha256\fR, \fIsha384\fR, or \fIsha512\fR, \fIsha3_224\fR,
|
||||
\fIsha3_256\fR, \fIsha3_384\fR, \fIsha3_512\fR. The default is
|
||||
determined based on the type and size of the ocsp signing key.
|
||||
.TP
|
||||
.BI "\-R, \-\-rsa\-padding " padding
|
||||
Padding to use for RSA signatures. Either \fIpkcs1\fR or \fIpss\fR, defaults
|
||||
to \fIpkcs1\fR.
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
.
|
||||
Show the raw content of an OCSP request:
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp \-\-in req_ca.der
|
||||
|
||||
nonce: 5b:14:e3:cc:d5:b2:65:ec:c4:0d:c3:11:37:6a:9d:71
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (no match)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (no match)
|
||||
serialNumber: 4f:33:21:1d:4d:fd:9b:db
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (no match)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (no match)
|
||||
serialNumber: 68:f2:93:10:65:d0:5e:d1
|
||||
.EE
|
||||
.PP
|
||||
Show the content of the same OCSP request if the issuer certificate is given:
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp \-\-in req_ca.der \-\-cacert cacert.pem
|
||||
|
||||
nonce: 5b:14:e3:cc:d5:b2:65:ec:c4:0d:c3:11:37:6a:9d:71
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (ok)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (ok)
|
||||
serialNumber: 4f:33:21:1d:4d:fd:9b:db
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (ok)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (ok)
|
||||
serialNumber: 68:f2:93:10:65:d0:5e:d1
|
||||
.EE
|
||||
.PP
|
||||
Respond to the OCSP request above, with the OCSP response signed by the CA itself:
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp \-\-respond \-\-in req_ca.der \-\-cacert cacert.pem \-\-key cakey.pem \\
|
||||
\-\-lifetime 10 > rsp_ca.der
|
||||
|
||||
nonce: 5b:14:e3:cc:d5:b2:65:ec:c4:0d:c3:11:37:6a:9d:71
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (ok)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (ok)
|
||||
serialNumber: 4f:33:21:1d:4d:fd:9b:db
|
||||
thisUpdate: Oct 19 15:54:15 UTC 2023
|
||||
nextUpdate: Oct 19 16:04:15 UTC 2023
|
||||
certValidation: GOOD
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
issuerKeyHash: b6:76:79:95:b5:58:..:06:93:f3:39:79:19 (ok)
|
||||
issuerNameHash: af:25:78:ce:fc:15:..:67:95:81:31:a3:4d (ok)
|
||||
serialNumber: 68:f2:93:10:65:d0:5e:d1
|
||||
thisUpdate: Oct 19 15:54:15 UTC 2023
|
||||
nextUpdate: Oct 19 16:04:15 UTC 2023
|
||||
certValidation: GOOD
|
||||
trusted signer: "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
ocspResponseStatus: successful
|
||||
.EE
|
||||
.PP
|
||||
Respond to a signed OCSP request providing the complete trust chain:
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp --respond --in req_signed.der --cacert cacert.pem --cacert issuer1.pem \\
|
||||
\-\-key signerKey1.pem \-\-cert signerCert1.pem \-\-lifetime 10 > rsp_signed.der
|
||||
|
||||
requestor: "C=CH, O=strongSwan Project, CN=vpn.strongswan.org"
|
||||
using certificate "C=CH, O=strongSwan Project, CN=vpn.strongswan.org"
|
||||
using trusted intermediate ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 1"
|
||||
using trusted ca certificate "C=CH, O=strongSwan Project, CN=strongSwan Root CA"
|
||||
reached self-signed root ca with a path length of 1
|
||||
requestor is trusted
|
||||
nonce: a8:0f:29:0f:08:9c:29:c1:0d:a8:cb:b0:21:fa:e1:f7
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 1"
|
||||
issuerKeyHash: 5a:1b:ec:17:f0:6d:..:a2:c8:e7:6a:84:20 (ok)
|
||||
issuerNameHash: df:1e:24:71:96:e6:..:b9:82:18:45:e7:09 (ok)
|
||||
serialNumber: 04:ff:cc:8d:36:91:cb:35:d7:c4
|
||||
thisUpdate: Oct 19 16:30:54 UTC 2023
|
||||
nextUpdate: Oct 19 16:40:54 UTC 2023
|
||||
certValidation: REVOKED
|
||||
revocationTime: Mar 26 06:41:54 UTC 2023
|
||||
revocationReason: superseded
|
||||
trusted signer: "C=CH, O=strongSwan Project, CN=OCSP signer of strongSwan Issuing CA 1"
|
||||
ocspResponseStatus: successful
|
||||
.EE
|
||||
.PP
|
||||
Respond to an OCSP request containing two items from different known issuers
|
||||
having an OCSP signer each. The issuer of the first request item determines the
|
||||
OCSP signer used to sign the OCSP response:
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp \-\-respond \-\-in req.der \-\-cacert issuer1.pem \-\-cacert issuer2.pem \\
|
||||
\-\-key signerKey1.pem \-\-cert signerCert1.pem \\
|
||||
\-\-key signerKey2.pem \-\-cert signerCert2.pem \\
|
||||
\-\-lifetime 10 > rsp_trusted.der
|
||||
|
||||
nonce: a1:33:aa:bc:96:60:69:76:f3:bc:9c:88:3b:07:50:47
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 2"
|
||||
issuerKeyHash: 72:41:ca:f9:35:87:..:d3:83:ab:d5:89:7b (ok)
|
||||
issuerNameHash: 5e:b2:b4:42:e1:a5:..:b2:c3:9a:38:4f:cd (ok)
|
||||
serialNumber: 29:ff:36:d9:9a:21:49:61:91:1d
|
||||
thisUpdate: Oct 19 16:02:35 UTC 2023
|
||||
nextUpdate: Oct 19 16:12:35 UTC 2023
|
||||
certValidation: REVOKED
|
||||
revocationTime: Sep 22 13:13:04 UTC 2023
|
||||
revocationReason: superseded
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 1"
|
||||
issuerKeyHash: 5a:1b:ec:17:f0:6d:..:a2:c8:e7:6a:84:20 (ok)
|
||||
issuerNameHash: df:1e:24:71:96:e6:..:b9:82:18:45:e7:09 (ok)
|
||||
serialNumber: 10:ff:45:9a:6d:ee:4c:ec:7c:97
|
||||
thisUpdate: Oct 19 16:02:35 UTC 2023
|
||||
nextUpdate: Oct 19 16:12:35 UTC 2023
|
||||
certValidation: FAILED
|
||||
there are multiple known issuers
|
||||
trusted signer: "C=CH, O=strongSwan Project, CN=OCSP signer of strongSwan Issuing CA 2"
|
||||
ocspResponseStatus: successful
|
||||
.EE
|
||||
.PP
|
||||
Repeat the OCSP response above but with a self-signed OCSP signing certificate
|
||||
.PP
|
||||
.EX
|
||||
pki \-\-ocsp --respond \-\-in req.der \-\-cacert issuer1.pem \-\-cacert issuer2.pem \\
|
||||
\-\-key signerKey.pem \-\-cert signerCert.pem \-\-lifetime 10 > rsp_self_signed.der
|
||||
|
||||
nonce: a1:33:aa:bc:96:60:69:76:f3:bc:9c:88:3b:07:50:47
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 2"
|
||||
issuerKeyHash: 72:41:ca:f9:35:87:..:d3:83:ab:d5:89:7b (ok)
|
||||
issuerNameHash: 5e:b2:b4:42:e1:a5:..:b2:c3:9a:38:4f:cd (ok)
|
||||
serialNumber: 29:ff:36:d9:9a:21:49:61:91:1d
|
||||
thisUpdate: Oct 19 16:13:23 UTC 2023
|
||||
nextUpdate: Oct 19 16:23:23 UTC 2023
|
||||
certValidation: REVOKED
|
||||
revocationTime: Sep 22 13:13:04 UTC 2023
|
||||
revocationReason: superseded
|
||||
issuer: "C=CH, O=strongSwan Project, CN=strongSwan Issuing CA 1"
|
||||
issuerKeyHash: 5a:1b:ec:17:f0:6d:..:a2:c8:e7:6a:84:20 (ok)
|
||||
issuerNameHash: df:1e:24:71:96:e6:..:b9:82:18:45:e7:09 (ok)
|
||||
serialNumber: 10:ff:45:9a:6d:ee:4c:ec:7c:97
|
||||
thisUpdate: Oct 19 16:13:23 UTC 2023
|
||||
nextUpdate: Oct 19 16:23:23 UTC 2023
|
||||
certValidation: GOOD
|
||||
there are multiple known issuers
|
||||
self-signed signer: "C=CH, O=strongSwan Project, CN=strongSwan OCSP signer"
|
||||
ocspResponseStatus: successful
|
||||
.EE
|
||||
.PP
|
||||
.SH "SEE ALSO"
|
||||
.BR pki (1)
|
@ -54,7 +54,8 @@ Type of input. One of \fIx509\fR (X.509 certificate), \fIcrl\fR (Certificate
|
||||
Revocation List, CRL), \fIac\fR (Attribute Certificate), \fIpub\fR (public key),
|
||||
\fIpriv\fR (private key), \fIrsa\fR (RSA private key), \fIecdsa\fR (ECDSA
|
||||
private key), \fIed25519\fR (Ed25519 private key), \fIed448\fR (Ed448 private
|
||||
key), \fIbliss\fR (BLISS private key), defaults to \fIx509\fR.
|
||||
key), \fIbliss\fR (BLISS private key), \fIocsp-req\fR (OCSP request),
|
||||
\fIocsp-rsp\fR (OCSP response), defaults to \fIx509\fR.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH PKI 1 "2022-08-22" "@PACKAGE_VERSION@" "strongSwan"
|
||||
.TH PKI 1 "2023-10-20" "@PACKAGE_VERSION@" "strongSwan"
|
||||
.
|
||||
.SH "NAME"
|
||||
.
|
||||
@ -33,13 +33,19 @@ key IDs.
|
||||
.P
|
||||
The
|
||||
.B pki
|
||||
command now supports certificate enrollment via the
|
||||
command also 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.
|
||||
.P
|
||||
The latest feature is an
|
||||
.B Online Certificate Status Protocol
|
||||
(OCSP) responder as defined by RFC 6960, interoperating with an
|
||||
.B OpenXPKI
|
||||
server by directly accessing its internal certificate datebase.
|
||||
.
|
||||
.SH "COMMANDS"
|
||||
.
|
||||
@ -94,6 +100,9 @@ Enroll an X.509 certificate with an EST server.
|
||||
.TP
|
||||
.B "\-e, \-\-estca"
|
||||
Get CA certificate[s] from an EST server.
|
||||
.TP
|
||||
.B "\-o, \-\-ocsp"
|
||||
OCSP request parser and OCSP responder.
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
.
|
||||
@ -188,3 +197,4 @@ certificates with the \-\-crl option.
|
||||
.BR pki\ \-\-scepca (1)
|
||||
.BR pki\ \-\-est (1)
|
||||
.BR pki\ \-\-estca (1)
|
||||
.BR pki\ \-\-ocsp (1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user