pki: Added ocsp command
The pki --ocsp command implements an OCSP responder.
This commit is contained in:
committed by
Tobias Brunner
parent
ec325b4c09
commit
a0c9f9b842
@@ -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 \
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -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)"},
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user