hx509: Add support for cert policies and mappings
This commit is contained in:
162
lib/hx509/ca.c
162
lib/hx509/ca.c
@@ -32,7 +32,6 @@
|
||||
*/
|
||||
|
||||
#include "hx_locl.h"
|
||||
#include <pkinit_asn1.h>
|
||||
|
||||
/**
|
||||
* @page page_ca Hx509 CA functions
|
||||
@@ -46,6 +45,8 @@ struct hx509_ca_tbs {
|
||||
KeyUsage ku;
|
||||
ExtKeyUsage eku;
|
||||
GeneralNames san;
|
||||
CertificatePolicies cps;
|
||||
PolicyMappings pms;
|
||||
heim_integer serial;
|
||||
struct {
|
||||
unsigned int proxy:1;
|
||||
@@ -102,6 +103,8 @@ hx509_ca_tbs_free(hx509_ca_tbs *tbs)
|
||||
return;
|
||||
|
||||
free_SubjectPublicKeyInfo(&(*tbs)->spki);
|
||||
free_CertificatePolicies(&(*tbs)->cps);
|
||||
free_PolicyMappings(&(*tbs)->pms);
|
||||
free_GeneralNames(&(*tbs)->san);
|
||||
free_ExtKeyUsage(&(*tbs)->eku);
|
||||
der_free_heim_integer(&(*tbs)->serial);
|
||||
@@ -528,6 +531,127 @@ hx509_ca_tbs_add_eku(hx509_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a certificate policy to the to-be-signed certificate object. Duplicates
|
||||
* will detected and not added.
|
||||
*
|
||||
* @param context A hx509 context.
|
||||
* @param tbs object to be signed.
|
||||
* @param oid policy OID.
|
||||
* @param cps_uri CPS URI to qualify policy with.
|
||||
* @param user_notice user notice display text to qualify policy with.
|
||||
*
|
||||
* @return An hx509 error code, see hx509_get_error_string().
|
||||
*
|
||||
* @ingroup hx509_ca
|
||||
*/
|
||||
|
||||
HX509_LIB_FUNCTION int HX509_LIB_CALL
|
||||
hx509_ca_tbs_add_pol(hx509_context context,
|
||||
hx509_ca_tbs tbs,
|
||||
const heim_oid *oid,
|
||||
const char *cps_uri,
|
||||
const char *user_notice)
|
||||
{
|
||||
PolicyQualifierInfos pqis;
|
||||
PolicyQualifierInfo pqi;
|
||||
PolicyInformation pi;
|
||||
size_t i, size;
|
||||
int ret = 0;
|
||||
|
||||
/* search for duplicates */
|
||||
for (i = 0; i < tbs->cps.len; i++) {
|
||||
if (der_heim_oid_cmp(oid, &tbs->cps.val[i].policyIdentifier) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
memset(&pqi, 0, sizeof(pqi));
|
||||
memset(&pqis, 0, sizeof(pqis));
|
||||
|
||||
pi.policyIdentifier = *oid;
|
||||
if (cps_uri) {
|
||||
CPSuri uri;
|
||||
|
||||
uri.length = strlen(cps_uri);
|
||||
uri.data = (void *)(uintptr_t)cps_uri;
|
||||
pqi.policyQualifierId = asn1_oid_id_pkix_qt_cps;
|
||||
|
||||
ASN1_MALLOC_ENCODE(CPSuri,
|
||||
pqi.qualifier.data,
|
||||
pqi.qualifier.length,
|
||||
&uri, &size, ret);
|
||||
if (ret == 0) {
|
||||
ret = add_PolicyQualifierInfos(&pqis, &pqi);
|
||||
free_heim_any(&pqi.qualifier);
|
||||
}
|
||||
}
|
||||
if (ret == 0 && user_notice) {
|
||||
DisplayText dt;
|
||||
UserNotice un;
|
||||
|
||||
dt.element = choice_DisplayText_utf8String;
|
||||
dt.u.utf8String = (void *)(uintptr_t)user_notice;
|
||||
un.explicitText = &dt;
|
||||
un.noticeRef = 0;
|
||||
|
||||
pqi.policyQualifierId = asn1_oid_id_pkix_qt_unotice;
|
||||
ASN1_MALLOC_ENCODE(UserNotice,
|
||||
pqi.qualifier.data,
|
||||
pqi.qualifier.length,
|
||||
&un, &size, ret);
|
||||
if (ret == 0) {
|
||||
ret = add_PolicyQualifierInfos(&pqis, &pqi);
|
||||
free_heim_any(&pqi.qualifier);
|
||||
}
|
||||
}
|
||||
|
||||
pi.policyQualifiers = pqis.len ? &pqis : 0;
|
||||
|
||||
if (ret == 0)
|
||||
ret = add_CertificatePolicies(&tbs->cps, &pi);
|
||||
|
||||
free_PolicyQualifierInfos(&pqis);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a certificate policy mapping to the to-be-signed certificate object.
|
||||
* Duplicates will detected and not added.
|
||||
*
|
||||
* @param context A hx509 context.
|
||||
* @param tbs object to be signed.
|
||||
* @param issuer issuerDomainPolicy policy OID.
|
||||
* @param subject subjectDomainPolicy policy OID.
|
||||
*
|
||||
* @return An hx509 error code, see hx509_get_error_string().
|
||||
*
|
||||
* @ingroup hx509_ca
|
||||
*/
|
||||
|
||||
HX509_LIB_FUNCTION int HX509_LIB_CALL
|
||||
hx509_ca_tbs_add_pol_mapping(hx509_context context,
|
||||
hx509_ca_tbs tbs,
|
||||
const heim_oid *issuer,
|
||||
const heim_oid *subject)
|
||||
{
|
||||
PolicyMapping pm;
|
||||
size_t i;
|
||||
|
||||
/* search for duplicates */
|
||||
for (i = 0; i < tbs->pms.len; i++) {
|
||||
PolicyMapping *pmp = &tbs->pms.val[i];
|
||||
if (der_heim_oid_cmp(issuer, &pmp->issuerDomainPolicy) == 0 &&
|
||||
der_heim_oid_cmp(subject, &pmp->subjectDomainPolicy) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&pm, 0, sizeof(pm));
|
||||
pm.issuerDomainPolicy = *issuer;
|
||||
pm.subjectDomainPolicy = *subject;
|
||||
return add_PolicyMappings(&tbs->pms, &pm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add CRL distribution point URI to the to-be-signed certificate
|
||||
* object.
|
||||
@@ -1613,8 +1737,8 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add CRL distribution point */
|
||||
if (tbs->crldp.len) {
|
||||
|
||||
ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
|
||||
&tbs->crldp, &size, ret);
|
||||
if (ret) {
|
||||
@@ -1631,6 +1755,40 @@ ca_sign(hx509_context context,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add CertificatePolicies */
|
||||
if (tbs->cps.len) {
|
||||
ASN1_MALLOC_ENCODE(CertificatePolicies, data.data, data.length,
|
||||
&tbs->cps, &size, ret);
|
||||
if (ret) {
|
||||
hx509_set_error_string(context, 0, ret, "Out of memory");
|
||||
goto out;
|
||||
}
|
||||
if (size != data.length)
|
||||
_hx509_abort("internal ASN.1 encoder error");
|
||||
ret = add_extension(context, tbsc, FALSE,
|
||||
&asn1_oid_id_x509_ce_certificatePolicies, &data);
|
||||
free(data.data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add PolicyMappings */
|
||||
if (tbs->cps.len) {
|
||||
ASN1_MALLOC_ENCODE(PolicyMappings, data.data, data.length,
|
||||
&tbs->pms, &size, ret);
|
||||
if (ret) {
|
||||
hx509_set_error_string(context, 0, ret, "Out of memory");
|
||||
goto out;
|
||||
}
|
||||
if (size != data.length)
|
||||
_hx509_abort("internal ASN.1 encoder error");
|
||||
ret = add_extension(context, tbsc, FALSE,
|
||||
&asn1_oid_id_x509_ce_policyMappings, &data);
|
||||
free(data.data);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret);
|
||||
if (ret) {
|
||||
hx509_set_error_string(context, 0, ret, "malloc out of memory");
|
||||
|
@@ -761,6 +761,16 @@ command = {
|
||||
type = "string"
|
||||
help = "XMPP jabber id (for SAN)"
|
||||
}
|
||||
option = {
|
||||
long = "policy"
|
||||
type = "strings"
|
||||
help = "Certificate Policy OID and optional URI and/or notice (OID:URI<space>notice_text)"
|
||||
}
|
||||
option = {
|
||||
long = "policy-mapping"
|
||||
type = "strings"
|
||||
help = "Certificate Policy mapping (OID:OID)"
|
||||
}
|
||||
option = {
|
||||
long = "req"
|
||||
type = "string"
|
||||
|
@@ -109,7 +109,7 @@ parse_oid(const char *str, const heim_oid *def, heim_oid *oid)
|
||||
if (ret == 0)
|
||||
ret = der_copy_oid(found, oid);
|
||||
else
|
||||
ret = der_parse_heim_oid (str, " .", oid);
|
||||
ret = der_parse_heim_oid(str, " .", oid);
|
||||
} else {
|
||||
ret = der_copy_oid(def, oid);
|
||||
}
|
||||
@@ -1411,10 +1411,7 @@ request_create(struct request_create_options *opt, int argc, char **argv)
|
||||
for (i = 0; i < opt->registered_strings.num_strings; i++) {
|
||||
heim_oid oid;
|
||||
|
||||
ret = der_parse_heim_oid(opt->registered_strings.strings[i], NULL,
|
||||
&oid);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "OID parse error");
|
||||
parse_oid(opt->registered_strings.strings[i], NULL, &oid);
|
||||
ret = hx509_request_add_registered(context, req, &oid);
|
||||
der_free_oid(&oid);
|
||||
if (ret)
|
||||
@@ -1879,9 +1876,11 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
|
||||
hx509_private_key cert_key = NULL;
|
||||
hx509_name subject = NULL;
|
||||
SubjectPublicKeyInfo spki;
|
||||
heim_oid oid;
|
||||
size_t i;
|
||||
int delta = 0;
|
||||
|
||||
memset(&oid, 0, sizeof(oid));
|
||||
memset(&spki, 0, sizeof(spki));
|
||||
|
||||
if (opt->ca_certificate_string == NULL && !opt->self_signed_flag)
|
||||
@@ -2021,12 +2020,11 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
|
||||
hx509_err(context, 1, ret, "hx509_ca_tbs_init");
|
||||
|
||||
for (i = 0; i < opt->eku_strings.num_strings; i++) {
|
||||
heim_oid oid;
|
||||
|
||||
parse_oid(opt->eku_strings.strings[i], NULL, &oid);
|
||||
ret = hx509_ca_tbs_add_eku(context, tbs, &oid);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "hx509_request_add_eku");
|
||||
der_free_oid(&oid);
|
||||
}
|
||||
if (opt->ku_strings.num_strings) {
|
||||
const struct units *kus = asn1_KeyUsage_units();
|
||||
@@ -2117,6 +2115,48 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
|
||||
|
||||
eval_types(context, tbs, opt);
|
||||
|
||||
for (i = 0; ret == 0 && i < opt->policy_strings.num_strings; i++) {
|
||||
char *oidstr, *uri, *dt;
|
||||
|
||||
if ((oidstr = strdup(opt->policy_strings.strings[i])) == NULL)
|
||||
hx509_err(context, 1, ENOMEM, "out of memory");
|
||||
uri = strchr(oidstr, ':');
|
||||
if (uri)
|
||||
*(uri++) = '\0';
|
||||
dt = strchr(uri ? uri : "", ' ');
|
||||
if (dt)
|
||||
*(dt++) = '\0';
|
||||
|
||||
parse_oid(oidstr, NULL, &oid);
|
||||
ret = hx509_ca_tbs_add_pol(context, tbs, &oid, uri, dt);
|
||||
der_free_oid(&oid);
|
||||
free(oidstr);
|
||||
}
|
||||
|
||||
for (i = 0; ret == 0 && i < opt->policy_mapping_strings.num_strings; i++) {
|
||||
char *issuer_oidstr, *subject_oidstr;
|
||||
heim_oid issuer_oid, subject_oid;
|
||||
|
||||
if ((issuer_oidstr =
|
||||
strdup(opt->policy_mapping_strings.strings[i])) == NULL)
|
||||
hx509_err(context, 1, ENOMEM, "out of memory");
|
||||
subject_oidstr = strchr(issuer_oidstr, ':');
|
||||
if (subject_oidstr == NULL)
|
||||
subject_oidstr = issuer_oidstr;
|
||||
else
|
||||
*(subject_oidstr++) = '\0';
|
||||
|
||||
parse_oid(issuer_oidstr, NULL, &issuer_oid);
|
||||
parse_oid(subject_oidstr, NULL, &subject_oid);
|
||||
ret = hx509_ca_tbs_add_pol_mapping(context, tbs, &issuer_oid,
|
||||
&subject_oid);
|
||||
if (ret)
|
||||
hx509_err(context, 1, ret, "failed to add policy mapping");
|
||||
der_free_oid(&issuer_oid);
|
||||
der_free_oid(&subject_oid);
|
||||
free(issuer_oidstr);
|
||||
}
|
||||
|
||||
if (opt->issue_ca_flag) {
|
||||
ret = hx509_ca_tbs_set_ca(context, tbs, opt->path_length_integer);
|
||||
if (ret)
|
||||
@@ -2521,8 +2561,7 @@ acert1_sans(struct acert_options *opt,
|
||||
|
||||
s = opt->has_registeredID_san_strings.strings[k];
|
||||
memset(&oid, 0, sizeof(oid));
|
||||
if ((ret = der_parse_heim_oid(s, NULL, &oid)))
|
||||
break;
|
||||
parse_oid(s, NULL, &oid);
|
||||
if (der_heim_oid_cmp(&gn->u.registeredID, &oid) == 0) {
|
||||
der_free_oid(&oid);
|
||||
if (opt->verbose_flag)
|
||||
@@ -2571,8 +2610,7 @@ acert1_ekus(struct acert_options *opt,
|
||||
heim_oid oid;
|
||||
|
||||
memset(&oid, 0, sizeof(oid));
|
||||
if ((ret = der_parse_heim_oid(s, NULL, &oid)))
|
||||
break;
|
||||
parse_oid(s, NULL, &oid);
|
||||
if (der_heim_oid_cmp(&eku.val[i], &oid) == 0) {
|
||||
der_free_oid(&oid);
|
||||
if (opt->verbose_flag)
|
||||
|
@@ -65,6 +65,8 @@ EXPORTS
|
||||
hx509_ca_tbs_add_crl_dp_uri
|
||||
hx509_ca_tbs_add_eku
|
||||
hx509_ca_tbs_add_ku
|
||||
hx509_ca_tbs_add_pol
|
||||
hx509_ca_tbs_add_pol_mapping
|
||||
hx509_ca_tbs_add_san
|
||||
hx509_ca_tbs_add_san_hostname
|
||||
hx509_ca_tbs_add_san_jid
|
||||
|
@@ -698,6 +698,169 @@ get_display_text(DisplayText *dt, char **out)
|
||||
return r < 0 ? errno : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_certificatePolicies(hx509_validate_ctx ctx,
|
||||
struct cert_status *status,
|
||||
enum critical_flag cf,
|
||||
const Extension *e)
|
||||
{
|
||||
CertificatePolicies cp;
|
||||
size_t i, size;
|
||||
int ret = 0;
|
||||
|
||||
check_Null(ctx, status, cf, e);
|
||||
|
||||
if (e->extnValue.length == 0) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"CertificatePolicies empty, not allowed");
|
||||
return 1;
|
||||
}
|
||||
ret = decode_CertificatePolicies(e->extnValue.data, e->extnValue.length,
|
||||
&cp, &size);
|
||||
if (ret) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"\tret = %d while decoding CertificatePolicies\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if (cp.len == 0) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"CertificatePolicies empty, not allowed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; ret == 0 && i < cp.len; i++) {
|
||||
size_t k;
|
||||
char *poid = NULL;
|
||||
char *qoid = NULL;
|
||||
char *dt = NULL;
|
||||
|
||||
ret = der_print_heim_oid(&cp.val[i].policyIdentifier, '.', &poid);
|
||||
if (ret == 0)
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tPolicy: %s", poid);
|
||||
|
||||
for (k = 0;
|
||||
ret == 0 && cp.val[i].policyQualifiers &&
|
||||
k < cp.val[i].policyQualifiers->len;
|
||||
k++) {
|
||||
PolicyQualifierInfo *pi = &cp.val[i].policyQualifiers->val[k];
|
||||
|
||||
if (der_heim_oid_cmp(&pi->policyQualifierId,
|
||||
&asn1_oid_id_pkix_qt_cps) == 0) {
|
||||
CPSuri cps;
|
||||
|
||||
ret = decode_CPSuri(pi->qualifier.data, pi->qualifier.length,
|
||||
&cps, &size);
|
||||
if (ret == 0) {
|
||||
if (cps.length > 4096)
|
||||
cps.length = 4096;
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
":CPSuri:%.*s",
|
||||
(int)cps.length, (char *)cps.data);
|
||||
free_CPSuri(&cps);
|
||||
}
|
||||
} else if (der_heim_oid_cmp(&pi->policyQualifierId,
|
||||
&asn1_oid_id_pkix_qt_unotice) == 0) {
|
||||
UserNotice un;
|
||||
|
||||
ret = decode_UserNotice(pi->qualifier.data,
|
||||
pi->qualifier.length, &un, &size);
|
||||
if (ret == 0) {
|
||||
if (un.explicitText) {
|
||||
/*
|
||||
* get_display_text() will strvis to make it safer to
|
||||
* print.
|
||||
*/
|
||||
ret = get_display_text(un.explicitText, &dt);
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
" UserNotice:DistplayText:%s", dt);
|
||||
} else if (un.noticeRef) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
" UserNotice:NoticeRef:<noticeRef-not-supported>",
|
||||
qoid);
|
||||
} else {
|
||||
ret = der_print_heim_oid(&pi->policyQualifierId, '.',
|
||||
&qoid);
|
||||
if (ret)
|
||||
break;
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
" Unknown:%s", qoid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
", qualifier %s:<unknown>", qoid);
|
||||
}
|
||||
free(qoid);
|
||||
free(dt);
|
||||
qoid = dt = 0;
|
||||
}
|
||||
if (ret == 0) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
|
||||
} else {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"\nOut of memory formatting certificate policy");
|
||||
ret = ENOMEM;
|
||||
}
|
||||
free(poid);
|
||||
free(qoid);
|
||||
free(dt);
|
||||
poid = qoid = dt = 0;
|
||||
}
|
||||
|
||||
free_CertificatePolicies(&cp);
|
||||
|
||||
return ret ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_policyMappings(hx509_validate_ctx ctx,
|
||||
struct cert_status *status,
|
||||
enum critical_flag cf,
|
||||
const Extension *e)
|
||||
{
|
||||
PolicyMappings pm;
|
||||
size_t i, size;
|
||||
int ret = 0;
|
||||
|
||||
check_Null(ctx, status, cf, e);
|
||||
|
||||
if (e->extnValue.length == 0) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"PolicyMappings empty, not allowed");
|
||||
return 1;
|
||||
}
|
||||
ret = decode_PolicyMappings(e->extnValue.data, e->extnValue.length,
|
||||
&pm, &size);
|
||||
if (ret) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"\tret = %d while decoding PolicyMappings\n", ret);
|
||||
return 1;
|
||||
}
|
||||
if (pm.len == 0) {
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"PolicyMappings empty, not allowed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; ret == 0 && i < pm.len; i++) {
|
||||
char *idpoid = NULL;
|
||||
char *sdpoid = NULL;
|
||||
|
||||
ret = der_print_heim_oid(&pm.val[i].issuerDomainPolicy, '.', &idpoid);
|
||||
if (ret == 0)
|
||||
ret = der_print_heim_oid(&pm.val[i].subjectDomainPolicy, '.',
|
||||
&sdpoid);
|
||||
if (ret == 0)
|
||||
validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
|
||||
"\tPolicy mapping %s -> %s\n", idpoid, sdpoid);
|
||||
else
|
||||
validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
|
||||
"ret=%d while decoding PolicyMappings\n", ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
@@ -727,8 +890,8 @@ struct {
|
||||
{ ext(certificateIssuer, Null), M_C },
|
||||
{ ext(nameConstraints, Null), M_C },
|
||||
{ ext(cRLDistributionPoints, CRLDistributionPoints), S_N_C },
|
||||
{ ext(certificatePolicies, Null), 0 },
|
||||
{ ext(policyMappings, Null), M_N_C },
|
||||
{ ext(certificatePolicies, certificatePolicies), 0 },
|
||||
{ ext(policyMappings, policyMappings), M_N_C },
|
||||
{ ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C },
|
||||
{ ext(policyConstraints, Null), D_C },
|
||||
{ ext(extKeyUsage, extKeyUsage), D_C },
|
||||
|
@@ -49,6 +49,8 @@ HEIMDAL_X509_1.2 {
|
||||
hx509_ca_tbs_add_crl_dp_uri;
|
||||
hx509_ca_tbs_add_eku;
|
||||
hx509_ca_tbs_add_ku;
|
||||
hx509_ca_tbs_add_pol;
|
||||
hx509_ca_tbs_add_pol_mapping;
|
||||
hx509_ca_tbs_add_san;
|
||||
hx509_ca_tbs_add_san_hostname;
|
||||
hx509_ca_tbs_add_san_jid;
|
||||
|
Reference in New Issue
Block a user