1519 lines
37 KiB
C
1519 lines
37 KiB
C
/*
|
|
* Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
|
|
* (Royal Institute of Technology, Stockholm, Sweden).
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "hx_locl.h"
|
|
#include <pkinit_asn1.h>
|
|
RCSID("$Id: ca.c 22456 2008-01-15 20:22:53Z lha $");
|
|
|
|
/**
|
|
* @page page_ca Hx509 CA functions
|
|
*
|
|
* See the library functions here: @ref hx509_ca
|
|
*/
|
|
|
|
struct hx509_ca_tbs {
|
|
hx509_name subject;
|
|
SubjectPublicKeyInfo spki;
|
|
ExtKeyUsage eku;
|
|
GeneralNames san;
|
|
unsigned key_usage;
|
|
heim_integer serial;
|
|
struct {
|
|
unsigned int proxy:1;
|
|
unsigned int ca:1;
|
|
unsigned int key:1;
|
|
unsigned int serial:1;
|
|
unsigned int domaincontroller:1;
|
|
} flags;
|
|
time_t notBefore;
|
|
time_t notAfter;
|
|
int pathLenConstraint; /* both for CA and Proxy */
|
|
CRLDistributionPoints crldp;
|
|
};
|
|
|
|
/**
|
|
* Allocate an to-be-signed certificate object that will be converted
|
|
* into an certificate.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs returned to-be-signed certicate object, free with
|
|
* hx509_ca_tbs_free().
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs)
|
|
{
|
|
*tbs = calloc(1, sizeof(**tbs));
|
|
if (*tbs == NULL)
|
|
return ENOMEM;
|
|
|
|
(*tbs)->subject = NULL;
|
|
(*tbs)->san.len = 0;
|
|
(*tbs)->san.val = NULL;
|
|
(*tbs)->eku.len = 0;
|
|
(*tbs)->eku.val = NULL;
|
|
(*tbs)->pathLenConstraint = 0;
|
|
(*tbs)->crldp.len = 0;
|
|
(*tbs)->crldp.val = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Free an To Be Signed object.
|
|
*
|
|
* @param tbs object to free.
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
void
|
|
hx509_ca_tbs_free(hx509_ca_tbs *tbs)
|
|
{
|
|
if (tbs == NULL || *tbs == NULL)
|
|
return;
|
|
|
|
free_SubjectPublicKeyInfo(&(*tbs)->spki);
|
|
free_GeneralNames(&(*tbs)->san);
|
|
free_ExtKeyUsage(&(*tbs)->eku);
|
|
der_free_heim_integer(&(*tbs)->serial);
|
|
free_CRLDistributionPoints(&(*tbs)->crldp);
|
|
|
|
hx509_name_free(&(*tbs)->subject);
|
|
|
|
memset(*tbs, 0, sizeof(**tbs));
|
|
free(*tbs);
|
|
*tbs = NULL;
|
|
}
|
|
|
|
/**
|
|
* Set the absolute time when the certificate is valid from. If not
|
|
* set the current time will be used.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param t time the certificated will start to be valid
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_notBefore(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
time_t t)
|
|
{
|
|
tbs->notBefore = t;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the absolute time when the certificate is valid to.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param t time when the certificate will expire
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_notAfter(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
time_t t)
|
|
{
|
|
tbs->notAfter = t;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the relative time when the certificiate is going to expire.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param delta seconds to the certificate is going to expire.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_notAfter_lifetime(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
time_t delta)
|
|
{
|
|
return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta);
|
|
}
|
|
|
|
static const struct units templatebits[] = {
|
|
{ "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU },
|
|
{ "KeyUsage", HX509_CA_TEMPLATE_KU },
|
|
{ "SPKI", HX509_CA_TEMPLATE_SPKI },
|
|
{ "notAfter", HX509_CA_TEMPLATE_NOTAFTER },
|
|
{ "notBefore", HX509_CA_TEMPLATE_NOTBEFORE },
|
|
{ "serial", HX509_CA_TEMPLATE_SERIAL },
|
|
{ "subject", HX509_CA_TEMPLATE_SUBJECT },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
/**
|
|
* Make of template units, use to build flags argument to
|
|
* hx509_ca_tbs_set_template() with parse_units().
|
|
*
|
|
* @return an units structure.
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
const struct units *
|
|
hx509_ca_tbs_template_units(void)
|
|
{
|
|
return templatebits;
|
|
}
|
|
|
|
/**
|
|
* Initialize the to-be-signed certificate object from a template certifiate.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param flags bit field selecting what to copy from the template
|
|
* certifiate.
|
|
* @param cert template certificate.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_template(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
int flags,
|
|
hx509_cert cert)
|
|
{
|
|
int ret;
|
|
|
|
if (flags & HX509_CA_TEMPLATE_SUBJECT) {
|
|
if (tbs->subject)
|
|
hx509_name_free(&tbs->subject);
|
|
ret = hx509_cert_get_subject(cert, &tbs->subject);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret,
|
|
"Failed to get subject from template");
|
|
return ret;
|
|
}
|
|
}
|
|
if (flags & HX509_CA_TEMPLATE_SERIAL) {
|
|
der_free_heim_integer(&tbs->serial);
|
|
ret = hx509_cert_get_serialnumber(cert, &tbs->serial);
|
|
tbs->flags.serial = !ret;
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret,
|
|
"Failed to copy serial number");
|
|
return ret;
|
|
}
|
|
}
|
|
if (flags & HX509_CA_TEMPLATE_NOTBEFORE)
|
|
tbs->notBefore = hx509_cert_get_notBefore(cert);
|
|
if (flags & HX509_CA_TEMPLATE_NOTAFTER)
|
|
tbs->notAfter = hx509_cert_get_notAfter(cert);
|
|
if (flags & HX509_CA_TEMPLATE_SPKI) {
|
|
free_SubjectPublicKeyInfo(&tbs->spki);
|
|
ret = hx509_cert_get_SPKI(context, cert, &tbs->spki);
|
|
tbs->flags.key = !ret;
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
if (flags & HX509_CA_TEMPLATE_KU) {
|
|
KeyUsage ku;
|
|
ret = _hx509_cert_get_keyusage(context, cert, &ku);
|
|
if (ret)
|
|
return ret;
|
|
tbs->key_usage = KeyUsage2int(ku);
|
|
}
|
|
if (flags & HX509_CA_TEMPLATE_EKU) {
|
|
ExtKeyUsage eku;
|
|
int i;
|
|
ret = _hx509_cert_get_eku(context, cert, &eku);
|
|
if (ret)
|
|
return ret;
|
|
for (i = 0; i < eku.len; i++) {
|
|
ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]);
|
|
if (ret) {
|
|
free_ExtKeyUsage(&eku);
|
|
return ret;
|
|
}
|
|
}
|
|
free_ExtKeyUsage(&eku);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Make the to-be-signed certificate object a CA certificate. If the
|
|
* pathLenConstraint is negative path length constraint is used.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param pathLenConstraint path length constraint, negative, no
|
|
* constraint.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_ca(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
int pathLenConstraint)
|
|
{
|
|
tbs->flags.ca = 1;
|
|
tbs->pathLenConstraint = pathLenConstraint;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Make the to-be-signed certificate object a proxy certificate. If the
|
|
* pathLenConstraint is negative path length constraint is used.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param pathLenConstraint path length constraint, negative, no
|
|
* constraint.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_proxy(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
int pathLenConstraint)
|
|
{
|
|
tbs->flags.proxy = 1;
|
|
tbs->pathLenConstraint = pathLenConstraint;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Make the to-be-signed certificate object a windows domain controller certificate.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_domaincontroller(hx509_context context,
|
|
hx509_ca_tbs tbs)
|
|
{
|
|
tbs->flags.domaincontroller = 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set the subject public key info (SPKI) in the to-be-signed certificate
|
|
* object. SPKI is the public key and key related parameters in the
|
|
* certificate.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param spki subject public key info to use for the to-be-signed certificate object.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_spki(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const SubjectPublicKeyInfo *spki)
|
|
{
|
|
int ret;
|
|
free_SubjectPublicKeyInfo(&tbs->spki);
|
|
ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki);
|
|
tbs->flags.key = !ret;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Set the serial number to use for to-be-signed certificate object.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param serialNumber serial number to use for the to-be-signed
|
|
* certificate object.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_serialnumber(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const heim_integer *serialNumber)
|
|
{
|
|
int ret;
|
|
der_free_heim_integer(&tbs->serial);
|
|
ret = der_copy_heim_integer(serialNumber, &tbs->serial);
|
|
tbs->flags.serial = !ret;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* An an extended key usage 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 extended key usage to add.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_eku(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const heim_oid *oid)
|
|
{
|
|
void *ptr;
|
|
int ret;
|
|
unsigned i;
|
|
|
|
/* search for duplicates */
|
|
for (i = 0; i < tbs->eku.len; i++) {
|
|
if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0)
|
|
return 0;
|
|
}
|
|
|
|
ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1));
|
|
if (ptr == NULL) {
|
|
hx509_set_error_string(context, 0, ENOMEM, "out of memory");
|
|
return ENOMEM;
|
|
}
|
|
tbs->eku.val = ptr;
|
|
ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "out of memory");
|
|
return ret;
|
|
}
|
|
tbs->eku.len += 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add CRL distribution point URI to the to-be-signed certificate
|
|
* object.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param uri uri to the CRL.
|
|
* @param issuername name of the issuer.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_crl_dp_uri(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *uri,
|
|
hx509_name issuername)
|
|
{
|
|
DistributionPoint dp;
|
|
int ret;
|
|
|
|
memset(&dp, 0, sizeof(dp));
|
|
|
|
dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint));
|
|
|
|
{
|
|
DistributionPointName name;
|
|
GeneralName gn;
|
|
size_t size;
|
|
|
|
name.element = choice_DistributionPointName_fullName;
|
|
name.u.fullName.len = 1;
|
|
name.u.fullName.val = &gn;
|
|
|
|
gn.element = choice_GeneralName_uniformResourceIdentifier;
|
|
gn.u.uniformResourceIdentifier = rk_UNCONST(uri);
|
|
|
|
ASN1_MALLOC_ENCODE(DistributionPointName,
|
|
dp.distributionPoint->data,
|
|
dp.distributionPoint->length,
|
|
&name, &size, ret);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret,
|
|
"Failed to encoded DistributionPointName");
|
|
goto out;
|
|
}
|
|
if (dp.distributionPoint->length != size)
|
|
_hx509_abort("internal ASN.1 encoder error");
|
|
}
|
|
|
|
if (issuername) {
|
|
#if 1
|
|
/**
|
|
* issuername not supported
|
|
*/
|
|
hx509_set_error_string(context, 0, EINVAL,
|
|
"CRLDistributionPoints.name.issuername not yet supported");
|
|
return EINVAL;
|
|
#else
|
|
GeneralNames *crlissuer;
|
|
GeneralName gn;
|
|
Name n;
|
|
|
|
crlissuer = calloc(1, sizeof(*crlissuer));
|
|
if (crlissuer == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
memset(&gn, 0, sizeof(gn));
|
|
|
|
gn.element = choice_GeneralName_directoryName;
|
|
ret = hx509_name_to_Name(issuername, &n);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "out of memory");
|
|
goto out;
|
|
}
|
|
|
|
gn.u.directoryName.element = n.element;
|
|
gn.u.directoryName.u.rdnSequence = n.u.rdnSequence;
|
|
|
|
ret = add_GeneralNames(&crlissuer, &gn);
|
|
free_Name(&n);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "out of memory");
|
|
goto out;
|
|
}
|
|
|
|
dp.cRLIssuer = &crlissuer;
|
|
#endif
|
|
}
|
|
|
|
ret = add_CRLDistributionPoints(&tbs->crldp, &dp);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "out of memory");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free_DistributionPoint(&dp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Add Subject Alternative Name otherName to the to-be-signed
|
|
* certificate object.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param oid the oid of the OtherName.
|
|
* @param os data in the other name.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_otherName(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const heim_oid *oid,
|
|
const heim_octet_string *os)
|
|
{
|
|
GeneralName gn;
|
|
|
|
memset(&gn, 0, sizeof(gn));
|
|
gn.element = choice_GeneralName_otherName;
|
|
gn.u.otherName.type_id = *oid;
|
|
gn.u.otherName.value = *os;
|
|
|
|
return add_GeneralNames(&tbs->san, &gn);
|
|
}
|
|
|
|
/**
|
|
* Add Kerberos Subject Alternative Name to the to-be-signed
|
|
* certificate object. The principal string is a UTF8 string.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param principal Kerberos principal to add to the certificate.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_pkinit(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *principal)
|
|
{
|
|
heim_octet_string os;
|
|
KRB5PrincipalName p;
|
|
size_t size;
|
|
int ret;
|
|
char *s = NULL;
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
/* parse principal */
|
|
{
|
|
const char *str;
|
|
char *q;
|
|
int n;
|
|
|
|
/* count number of component */
|
|
n = 1;
|
|
for(str = principal; *str != '\0' && *str != '@'; str++){
|
|
if(*str=='\\'){
|
|
if(str[1] == '\0' || str[1] == '@') {
|
|
ret = HX509_PARSING_NAME_FAILED;
|
|
hx509_set_error_string(context, 0, ret,
|
|
"trailing \\ in principal name");
|
|
goto out;
|
|
}
|
|
str++;
|
|
} else if(*str == '/')
|
|
n++;
|
|
}
|
|
p.principalName.name_string.val =
|
|
calloc(n, sizeof(*p.principalName.name_string.val));
|
|
if (p.principalName.name_string.val == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
|
|
goto out;
|
|
}
|
|
p.principalName.name_string.len = n;
|
|
|
|
p.principalName.name_type = KRB5_NT_PRINCIPAL;
|
|
q = s = strdup(principal);
|
|
if (q == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
|
|
goto out;
|
|
}
|
|
p.realm = strrchr(q, '@');
|
|
if (p.realm == NULL) {
|
|
ret = HX509_PARSING_NAME_FAILED;
|
|
hx509_set_error_string(context, 0, ret, "Missing @ in principal");
|
|
goto out;
|
|
};
|
|
*p.realm++ = '\0';
|
|
|
|
n = 0;
|
|
while (q) {
|
|
p.principalName.name_string.val[n++] = q;
|
|
q = strchr(q, '/');
|
|
if (q)
|
|
*q++ = '\0';
|
|
}
|
|
}
|
|
|
|
ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
if (size != os.length)
|
|
_hx509_abort("internal ASN.1 encoder error");
|
|
|
|
ret = hx509_ca_tbs_add_san_otherName(context,
|
|
tbs,
|
|
oid_id_pkinit_san(),
|
|
&os);
|
|
free(os.data);
|
|
out:
|
|
if (p.principalName.name_string.val)
|
|
free (p.principalName.name_string.val);
|
|
if (s)
|
|
free(s);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
static int
|
|
add_utf8_san(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const heim_oid *oid,
|
|
const char *string)
|
|
{
|
|
const PKIXXmppAddr ustring = (const PKIXXmppAddr)string;
|
|
heim_octet_string os;
|
|
size_t size;
|
|
int ret;
|
|
|
|
os.length = 0;
|
|
os.data = NULL;
|
|
|
|
ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
if (size != os.length)
|
|
_hx509_abort("internal ASN.1 encoder error");
|
|
|
|
ret = hx509_ca_tbs_add_san_otherName(context,
|
|
tbs,
|
|
oid,
|
|
&os);
|
|
free(os.data);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Add Microsoft UPN Subject Alternative Name to the to-be-signed
|
|
* certificate object. The principal string is a UTF8 string.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param principal Microsoft UPN string.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_ms_upn(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *principal)
|
|
{
|
|
return add_utf8_san(context, tbs, oid_id_pkinit_ms_san(), principal);
|
|
}
|
|
|
|
/**
|
|
* Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed
|
|
* certificate object. The jid is an UTF8 string.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param jid string of an a jabber id in UTF8.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_jid(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *jid)
|
|
{
|
|
return add_utf8_san(context, tbs, oid_id_pkix_on_xmppAddr(), jid);
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a Subject Alternative Name hostname to to-be-signed certificate
|
|
* object. A domain match starts with ., an exact match does not.
|
|
*
|
|
* Example of a an domain match: .domain.se matches the hostname
|
|
* host.domain.se.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param dnsname a hostame.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_hostname(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *dnsname)
|
|
{
|
|
GeneralName gn;
|
|
|
|
memset(&gn, 0, sizeof(gn));
|
|
gn.element = choice_GeneralName_dNSName;
|
|
gn.u.dNSName = rk_UNCONST(dnsname);
|
|
|
|
return add_GeneralNames(&tbs->san, &gn);
|
|
}
|
|
|
|
/**
|
|
* Add a Subject Alternative Name rfc822 (email address) to
|
|
* to-be-signed certificate object.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param rfc822Name a string to a email address.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_add_san_rfc822name(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
const char *rfc822Name)
|
|
{
|
|
GeneralName gn;
|
|
|
|
memset(&gn, 0, sizeof(gn));
|
|
gn.element = choice_GeneralName_rfc822Name;
|
|
gn.u.rfc822Name = rk_UNCONST(rfc822Name);
|
|
|
|
return add_GeneralNames(&tbs->san, &gn);
|
|
}
|
|
|
|
/**
|
|
* Set the subject name of a to-be-signed certificate object.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param subject the name to set a subject.
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_set_subject(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
hx509_name subject)
|
|
{
|
|
if (tbs->subject)
|
|
hx509_name_free(&tbs->subject);
|
|
return hx509_name_copy(context, subject, &tbs->subject);
|
|
}
|
|
|
|
/**
|
|
* Expand the the subject name in the to-be-signed certificate object
|
|
* using hx509_name_expand().
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param env enviroment variable to expand variables in the subject
|
|
* name, see hx509_env_init().
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_tbs_subject_expand(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
hx509_env env)
|
|
{
|
|
return hx509_name_expand(context, tbs->subject, env);
|
|
}
|
|
|
|
static int
|
|
add_extension(hx509_context context,
|
|
TBSCertificate *tbsc,
|
|
int critical_flag,
|
|
const heim_oid *oid,
|
|
const heim_octet_string *data)
|
|
{
|
|
Extension ext;
|
|
int ret;
|
|
|
|
memset(&ext, 0, sizeof(ext));
|
|
|
|
if (critical_flag) {
|
|
ext.critical = malloc(sizeof(*ext.critical));
|
|
if (ext.critical == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
*ext.critical = TRUE;
|
|
}
|
|
|
|
ret = der_copy_oid(oid, &ext.extnID);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
ret = der_copy_octet_string(data, &ext.extnValue);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
ret = add_Extensions(tbsc->extensions, &ext);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
out:
|
|
free_Extension(&ext);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject)
|
|
{
|
|
char *tstr;
|
|
time_t t;
|
|
int ret;
|
|
|
|
ret = copy_Name(issuer, subject);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret,
|
|
"Failed to copy subject name");
|
|
return ret;
|
|
}
|
|
|
|
t = time(NULL);
|
|
asprintf(&tstr, "ts-%lu", (unsigned long)t);
|
|
if (tstr == NULL) {
|
|
hx509_set_error_string(context, 0, ENOMEM,
|
|
"Failed to copy subject name");
|
|
return ENOMEM;
|
|
}
|
|
/* prefix with CN=<ts>,...*/
|
|
ret = _hx509_name_modify(context, subject, 1, oid_id_at_commonName(), tstr);
|
|
free(tstr);
|
|
if (ret)
|
|
free_Name(subject);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ca_sign(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
hx509_private_key signer,
|
|
const AuthorityKeyIdentifier *ai,
|
|
const Name *issuername,
|
|
hx509_cert *certificate)
|
|
{
|
|
heim_octet_string data;
|
|
Certificate c;
|
|
TBSCertificate *tbsc;
|
|
size_t size;
|
|
int ret;
|
|
const AlgorithmIdentifier *sigalg;
|
|
time_t notBefore;
|
|
time_t notAfter;
|
|
unsigned key_usage;
|
|
|
|
sigalg = _hx509_crypto_default_sig_alg;
|
|
|
|
memset(&c, 0, sizeof(c));
|
|
|
|
/*
|
|
* Default values are: Valid since 24h ago, valid one year into
|
|
* the future, KeyUsage digitalSignature and keyEncipherment set,
|
|
* and keyCertSign for CA certificates.
|
|
*/
|
|
notBefore = tbs->notBefore;
|
|
if (notBefore == 0)
|
|
notBefore = time(NULL) - 3600 * 24;
|
|
notAfter = tbs->notAfter;
|
|
if (notAfter == 0)
|
|
notAfter = time(NULL) + 3600 * 24 * 365;
|
|
|
|
key_usage = tbs->key_usage;
|
|
if (key_usage == 0) {
|
|
KeyUsage ku;
|
|
memset(&ku, 0, sizeof(ku));
|
|
ku.digitalSignature = 1;
|
|
ku.keyEncipherment = 1;
|
|
key_usage = KeyUsage2int(ku);
|
|
}
|
|
|
|
if (tbs->flags.ca) {
|
|
KeyUsage ku;
|
|
memset(&ku, 0, sizeof(ku));
|
|
ku.keyCertSign = 1;
|
|
ku.cRLSign = 1;
|
|
key_usage |= KeyUsage2int(ku);
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
tbsc = &c.tbsCertificate;
|
|
|
|
if (tbs->flags.key == 0) {
|
|
ret = EINVAL;
|
|
hx509_set_error_string(context, 0, ret, "No public key set");
|
|
return ret;
|
|
}
|
|
/*
|
|
* Don't put restrictions on proxy certificate's subject name, it
|
|
* will be generated below.
|
|
*/
|
|
if (!tbs->flags.proxy) {
|
|
if (tbs->subject == NULL) {
|
|
hx509_set_error_string(context, 0, EINVAL, "No subject name set");
|
|
return EINVAL;
|
|
}
|
|
if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) {
|
|
hx509_set_error_string(context, 0, EINVAL,
|
|
"NULL subject and no SubjectAltNames");
|
|
return EINVAL;
|
|
}
|
|
}
|
|
if (tbs->flags.ca && tbs->flags.proxy) {
|
|
hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA "
|
|
"at the same time");
|
|
return EINVAL;
|
|
}
|
|
if (tbs->flags.proxy) {
|
|
if (tbs->san.len > 0) {
|
|
hx509_set_error_string(context, 0, EINVAL,
|
|
"Proxy certificate is not allowed "
|
|
"to have SubjectAltNames");
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
/* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */
|
|
tbsc->version = calloc(1, sizeof(*tbsc->version));
|
|
if (tbsc->version == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
*tbsc->version = rfc3280_version_3;
|
|
/* serialNumber CertificateSerialNumber, */
|
|
if (tbs->flags.serial) {
|
|
ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
} else {
|
|
tbsc->serialNumber.length = 20;
|
|
tbsc->serialNumber.data = malloc(tbsc->serialNumber.length);
|
|
if (tbsc->serialNumber.data == NULL){
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
/* XXX diffrent */
|
|
RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length);
|
|
((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f;
|
|
}
|
|
/* signature AlgorithmIdentifier, */
|
|
ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg");
|
|
goto out;
|
|
}
|
|
/* issuer Name, */
|
|
if (issuername)
|
|
ret = copy_Name(issuername, &tbsc->issuer);
|
|
else
|
|
ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Failed to copy issuer name");
|
|
goto out;
|
|
}
|
|
/* validity Validity, */
|
|
tbsc->validity.notBefore.element = choice_Time_generalTime;
|
|
tbsc->validity.notBefore.u.generalTime = notBefore;
|
|
tbsc->validity.notAfter.element = choice_Time_generalTime;
|
|
tbsc->validity.notAfter.u.generalTime = notAfter;
|
|
/* subject Name, */
|
|
if (tbs->flags.proxy) {
|
|
ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject);
|
|
if (ret)
|
|
goto out;
|
|
} else {
|
|
ret = hx509_name_to_Name(tbs->subject, &tbsc->subject);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret,
|
|
"Failed to copy subject name");
|
|
goto out;
|
|
}
|
|
}
|
|
/* subjectPublicKeyInfo SubjectPublicKeyInfo, */
|
|
ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Failed to copy spki");
|
|
goto out;
|
|
}
|
|
/* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */
|
|
/* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */
|
|
/* extensions [3] EXPLICIT Extensions OPTIONAL */
|
|
tbsc->extensions = calloc(1, sizeof(*tbsc->extensions));
|
|
if (tbsc->extensions == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
/* Add the text BMP string Domaincontroller to the cert */
|
|
if (tbs->flags.domaincontroller) {
|
|
data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d"
|
|
"\x00\x61\x00\x69\x00\x6e\x00\x43"
|
|
"\x00\x6f\x00\x6e\x00\x74\x00\x72"
|
|
"\x00\x6f\x00\x6c\x00\x6c\x00\x65"
|
|
"\x00\x72");
|
|
data.length = 34;
|
|
|
|
ret = add_extension(context, tbsc, 0,
|
|
oid_id_ms_cert_enroll_domaincontroller(),
|
|
&data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* add KeyUsage */
|
|
{
|
|
KeyUsage ku;
|
|
|
|
ku = int2KeyUsage(key_usage);
|
|
ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &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, 1,
|
|
oid_id_x509_ce_keyUsage(), &data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* add ExtendedKeyUsage */
|
|
if (tbs->eku.len > 0) {
|
|
ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length,
|
|
&tbs->eku, &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, 0,
|
|
oid_id_x509_ce_extKeyUsage(), &data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* add Subject Alternative Name */
|
|
if (tbs->san.len > 0) {
|
|
ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length,
|
|
&tbs->san, &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, 0,
|
|
oid_id_x509_ce_subjectAltName(),
|
|
&data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Add Authority Key Identifier */
|
|
if (ai) {
|
|
ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length,
|
|
ai, &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, 0,
|
|
oid_id_x509_ce_authorityKeyIdentifier(),
|
|
&data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Add Subject Key Identifier */
|
|
{
|
|
SubjectKeyIdentifier si;
|
|
unsigned char hash[SHA_DIGEST_LENGTH];
|
|
|
|
{
|
|
SHA_CTX m;
|
|
|
|
SHA1_Init(&m);
|
|
SHA1_Update(&m, tbs->spki.subjectPublicKey.data,
|
|
tbs->spki.subjectPublicKey.length / 8);
|
|
SHA1_Final (hash, &m);
|
|
}
|
|
|
|
si.data = hash;
|
|
si.length = sizeof(hash);
|
|
|
|
ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length,
|
|
&si, &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, 0,
|
|
oid_id_x509_ce_subjectKeyIdentifier(),
|
|
&data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* Add BasicConstraints */
|
|
{
|
|
BasicConstraints bc;
|
|
int aCA = 1;
|
|
uint32_t path;
|
|
|
|
memset(&bc, 0, sizeof(bc));
|
|
|
|
if (tbs->flags.ca) {
|
|
bc.cA = &aCA;
|
|
if (tbs->pathLenConstraint >= 0) {
|
|
path = tbs->pathLenConstraint;
|
|
bc.pathLenConstraint = &path;
|
|
}
|
|
}
|
|
|
|
ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length,
|
|
&bc, &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");
|
|
/* Critical if this is a CA */
|
|
ret = add_extension(context, tbsc, tbs->flags.ca,
|
|
oid_id_x509_ce_basicConstraints(),
|
|
&data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* add Proxy */
|
|
if (tbs->flags.proxy) {
|
|
ProxyCertInfo info;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
if (tbs->pathLenConstraint >= 0) {
|
|
info.pCPathLenConstraint =
|
|
malloc(sizeof(*info.pCPathLenConstraint));
|
|
if (info.pCPathLenConstraint == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
*info.pCPathLenConstraint = tbs->pathLenConstraint;
|
|
}
|
|
|
|
ret = der_copy_oid(oid_id_pkix_ppl_inheritAll(),
|
|
&info.proxyPolicy.policyLanguage);
|
|
if (ret) {
|
|
free_ProxyCertInfo(&info);
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length,
|
|
&info, &size, ret);
|
|
free_ProxyCertInfo(&info);
|
|
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, 0,
|
|
oid_id_pkix_pe_proxyCertInfo(),
|
|
&data);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (tbs->crldp.len) {
|
|
|
|
ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
|
|
&tbs->crldp, &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,
|
|
oid_id_x509_ce_cRLDistributionPoints(),
|
|
&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");
|
|
goto out;
|
|
}
|
|
if (data.length != size)
|
|
_hx509_abort("internal ASN.1 encoder error");
|
|
|
|
ret = _hx509_create_signature_bitstring(context,
|
|
signer,
|
|
sigalg,
|
|
&data,
|
|
&c.signatureAlgorithm,
|
|
&c.signatureValue);
|
|
free(data.data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = hx509_cert_init(context, &c, certificate);
|
|
if (ret)
|
|
goto out;
|
|
|
|
free_Certificate(&c);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
free_Certificate(&c);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
get_AuthorityKeyIdentifier(hx509_context context,
|
|
const Certificate *certificate,
|
|
AuthorityKeyIdentifier *ai)
|
|
{
|
|
SubjectKeyIdentifier si;
|
|
int ret;
|
|
|
|
ret = _hx509_find_extension_subject_key_id(certificate, &si);
|
|
if (ret == 0) {
|
|
ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier));
|
|
if (ai->keyIdentifier == NULL) {
|
|
free_SubjectKeyIdentifier(&si);
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
ret = der_copy_octet_string(&si, ai->keyIdentifier);
|
|
free_SubjectKeyIdentifier(&si);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
} else {
|
|
GeneralNames gns;
|
|
GeneralName gn;
|
|
Name name;
|
|
|
|
memset(&gn, 0, sizeof(gn));
|
|
memset(&gns, 0, sizeof(gns));
|
|
memset(&name, 0, sizeof(name));
|
|
|
|
ai->authorityCertIssuer =
|
|
calloc(1, sizeof(*ai->authorityCertIssuer));
|
|
if (ai->authorityCertIssuer == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
ai->authorityCertSerialNumber =
|
|
calloc(1, sizeof(*ai->authorityCertSerialNumber));
|
|
if (ai->authorityCertSerialNumber == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* XXX unbreak when asn1 compiler handle IMPLICIT
|
|
*
|
|
* This is so horrible.
|
|
*/
|
|
|
|
ret = copy_Name(&certificate->tbsCertificate.subject, &name);
|
|
if (ai->authorityCertSerialNumber == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
memset(&gn, 0, sizeof(gn));
|
|
gn.element = choice_GeneralName_directoryName;
|
|
gn.u.directoryName.element =
|
|
choice_GeneralName_directoryName_rdnSequence;
|
|
gn.u.directoryName.u.rdnSequence = name.u.rdnSequence;
|
|
|
|
ret = add_GeneralNames(&gns, &gn);
|
|
if (ret) {
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
|
|
ai->authorityCertIssuer->val = gns.val;
|
|
ai->authorityCertIssuer->len = gns.len;
|
|
|
|
ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber,
|
|
ai->authorityCertSerialNumber);
|
|
if (ai->authorityCertSerialNumber == NULL) {
|
|
ret = ENOMEM;
|
|
hx509_set_error_string(context, 0, ret, "Out of memory");
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
if (ret)
|
|
free_AuthorityKeyIdentifier(ai);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sign a to-be-signed certificate object with a issuer certificate.
|
|
*
|
|
* The caller needs to at least have called the following functions on the
|
|
* to-be-signed certificate object:
|
|
* - hx509_ca_tbs_init()
|
|
* - hx509_ca_tbs_set_subject()
|
|
* - hx509_ca_tbs_set_spki()
|
|
*
|
|
* When done the to-be-signed certificate object should be freed with
|
|
* hx509_ca_tbs_free().
|
|
*
|
|
* When creating self-signed certificate use hx509_ca_sign_self() instead.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param signer the CA certificate object to sign with (need private key).
|
|
* @param certificate return cerificate, free with hx509_cert_free().
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_sign(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
hx509_cert signer,
|
|
hx509_cert *certificate)
|
|
{
|
|
const Certificate *signer_cert;
|
|
AuthorityKeyIdentifier ai;
|
|
int ret;
|
|
|
|
memset(&ai, 0, sizeof(ai));
|
|
|
|
signer_cert = _hx509_get_cert(signer);
|
|
|
|
ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = ca_sign(context,
|
|
tbs,
|
|
_hx509_cert_private_key(signer),
|
|
&ai,
|
|
&signer_cert->tbsCertificate.subject,
|
|
certificate);
|
|
|
|
out:
|
|
free_AuthorityKeyIdentifier(&ai);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Work just like hx509_ca_sign() but signs it-self.
|
|
*
|
|
* @param context A hx509 context.
|
|
* @param tbs object to be signed.
|
|
* @param signer private key to sign with.
|
|
* @param certificate return cerificate, free with hx509_cert_free().
|
|
*
|
|
* @return An hx509 error code, see hx509_get_error_string().
|
|
*
|
|
* @ingroup hx509_ca
|
|
*/
|
|
|
|
int
|
|
hx509_ca_sign_self(hx509_context context,
|
|
hx509_ca_tbs tbs,
|
|
hx509_private_key signer,
|
|
hx509_cert *certificate)
|
|
{
|
|
return ca_sign(context,
|
|
tbs,
|
|
signer,
|
|
NULL,
|
|
NULL,
|
|
certificate);
|
|
}
|