Support X.509 certificate creation.

Using "gpgsm --genkey" allows the creation of a self-signed
certificate via a new prompt.

Using "gpgsm --genkey --batch" should allow the creation of arbitrary
certificates controlled by a parameter file.  An example parameter file
is

    Key-Type: RSA
    Key-Length: 1024
    Key-Grip: 2C50DC6101C10C9C643E315FE3EADCCBC24F4BEA
    Key-Usage: sign, encrypt
    Serial: random
    Name-DN: CN=some test key
    Name-Email: foo@example.org
    Name-Email: bar@exmaple.org
    Hash-Algo: SHA384
    not-after: 2038-01-16 12:44

This creates a self-signed X.509 certificate using the key given by
the keygrip and using SHA-384 as hash algorithm.  The keyword
signing-key can be used to sign the certificate with a different key.
See sm/certreggen.c for details.
This commit is contained in:
Werner Koch 2011-03-01 14:42:56 +01:00
parent bb6d1b48f6
commit 28c157b55c
8 changed files with 615 additions and 81 deletions

4
NEWS
View File

@ -19,9 +19,13 @@ Noteworthy changes in version 2.1.0beta2 (unreleased)
* Dirmngr has taken over the function of the keyserver helpers. Thus
we now have a specified direct interface to keyservers via Dirmngr.
LDAP, DNS and mail backends are not yet implemented.
* ECC support for GPG as described by draft-jivsov-openpgp-ecc-06.txt.
* New GPGSM feature to create certificates from a parameter file.
Add prompt to the --gen-key UI to create self-signed certificates.
Noteworthy changes in version 2.1.0beta1 (2010-10-26)
-----------------------------------------------------

View File

@ -793,7 +793,8 @@ Unattended key generation
This feature allows unattended generation of keys controlled by a
parameter file. To use this feature, you use --gen-key together with
--batch and feed the parameters either from stdin or from a file given
on the commandline.
on the commandline. The description below is only for GPG; GPGSM has
a similar feature, see the file sm/certreqgen.c for a description.
The format of this file is as follows:
o Text only, line length is limited to about 1000 chars.
@ -1220,6 +1221,8 @@ OIDs below the GnuPG arc:
1.3.6.1.4.1.11591.2 GnuPG
1.3.6.1.4.1.11591.2.1 notation
1.3.6.1.4.1.11591.2.1.1 pkaAddress
1.3.6.1.4.1.11591.2.2 X.509 extensions
1.3.6.1.4.1.11591.2.2.1 standaloneCertificate
1.3.6.1.4.1.11591.2.12242973 invalid encoded OID

View File

@ -1,3 +1,24 @@
2011-03-01 Werner Koch <wk@g10code.com>
* certreqgen.c (pSERIAL, pISSUERDN, pNOTBEFORE, pNOTAFTER)
(pSIGNINGKEY, pHASHALGO): New.
(reqgen_ctrl_s): Remove field WRITER.
(read_parameters): Support new keywords. Change arg WRITER to
OUT_FP; pass that to proc_parameters.
(proc_parameters): Add arg WRITER. Check values of new keywords.
Create writer object here. Support generation of certificates.
(create_request): Take new arg SIGKEY. Allow for hash algorithms
other than SHA-1. Set serialno and other values for certificate
creation.
(gpgsm_genkey): Do not create writer object but pass output stream
to read_parameters.
* certreqgen-ui.c (gpgsm_gencertreq_tty): Ask for self-signed.
* misc.c (transform_sigval): New.
2011-02-25 Werner Koch <wk@g10code.com>
* certreqgen.c (create_request): Add arg SIGKEY.
2010-11-25 Werner Koch <wk@g10code.com>
* base64.c (gpgsm_create_writer): Remove arg FP which is not used
@ -2876,7 +2897,7 @@ h2007-11-22 Werner Koch <wk@g10code.com>
Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
2010, 2011 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without

View File

@ -1,5 +1,5 @@
/* certreqgen-ui.c - Simple user interface for certreqgen.c
* Copyright (C) 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -145,6 +145,7 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
char *result = NULL;
int i;
const char *s, *s2;
int selfsigned;
answer = NULL;
init_membuf (&mb_email, 100);
@ -346,6 +347,11 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
ask_mb_lines (&mb_email, "Name-URI: ");
/* Want a self-signed certificate? */
selfsigned = tty_get_answer_is_yes
(_("Create self-signed certificate? (y/N) "));
/* Put it all together. */
store_key_value_lf (&mb_result, "Key-Type: ", keytype);
{
@ -353,10 +359,12 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
snprintf (numbuf, sizeof numbuf, "%u", nbits);
store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
}
store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
if (keygrip)
store_key_value_lf (&mb_result, "Key-Grip: ", keygrip);
store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
if (selfsigned)
store_key_value_lf (&mb_result, "Serial: ", "random");
store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
if (store_mb_lines (&mb_result, &mb_email))
goto mem_error;
if (store_mb_lines (&mb_result, &mb_dns))
@ -368,14 +376,13 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
if (!result)
goto mem_error;
tty_printf (_("Parameters to be used for the certificate request:\n"));
tty_printf (_("These parameters are used:\n"));
for (s=result; (s2 = strchr (s, '\n')); s = s2+1, i++)
tty_printf (" %.*s\n", (int)(s2-s), s);
tty_printf ("\n");
if (!tty_get_answer_is_yes ("Really create request? (y/N) "))
goto leave;
if (!tty_get_answer_is_yes ("Proceed with creation? (y/N) "))
goto leave;
/* Now create a parameter file and generate the key. */
fp = es_fopenmem (0, "w+");
@ -386,8 +393,9 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
}
es_fputs (result, fp);
es_rewind (fp);
tty_printf (_("Now creating certificate request. "
"This may take a while ...\n"));
tty_printf (_("Now creating %s. "
"This may take a while ...\n"),
selfsigned?_("self-signed certificate"):_("certificate request"));
{
int save_pem = ctrl->create_pem;
ctrl->create_pem = 1; /* Force creation of PEM. */
@ -395,7 +403,13 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
ctrl->create_pem = save_pem;
}
if (!err)
tty_printf (_("Ready. You should now send this request to your CA.\n"));
{
if (selfsigned)
tty_printf (_("Ready.\n"));
else
tty_printf
(_("Ready. You should now send this request to your CA.\n"));
}
goto leave;

View File

@ -1,6 +1,6 @@
/* certreqgen.c - Generate a key and a certification request
* Copyright (C) 2002, 2003, 2005, 2007,
* 2010 Free Software Foundation, Inc.
/* certreqgen.c - Generate a key and a certification [request]
* Copyright (C) 2002, 2003, 2005, 2007, 2010,
* 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -43,6 +43,7 @@ The format of the native parameter file is follows:
Perform the key generation. Note that an implicit commit is done
at the next "Key-Type" parameter.
%certfile <filename>
[Not yet implemented!]
Do not write the certificate to the keyDB but to <filename>.
This must be given before the first
commit to take place, duplicate specification of the same filename
@ -51,35 +52,83 @@ The format of the native parameter file is follows:
and all keys are written to that file. If a new filename is given,
this file is created (and overwrites an existing one).
Both control statements must be given.
o The order of the parameters does not matter except for "Key-Type"
which must be the first parameter. The parameters are only for the
generated keyblock and parameters from previous key generations are not
used. Some syntactically checks may be performed.
The currently defined parameters are:
Key-Type: <algo>
Starts a new parameter block by giving the type of the
primary key. The algorithm must be capable of signing.
This is a required parameter. For now the only supported
algorithm is "rsa".
Key-Length: <length-in-bits>
Length of the key in bits. Default is 2048.
Key-Grip: hexstring
Key-Grip: <hexstring>
This is optional and used to generate a request for an already
existing key. Key-Length will be ignored when given,
Key-Usage: <usage-list>
Space or comma delimited list of key usage, allowed values are
"encrypt" and "sign". This is used to generate the KeyUsage extension.
Please make sure that the algorithm is capable of this usage. Default
is to allow encrypt and sign.
Name-DN: subject name
Name-DN: <subject_name>
This is the DN name of the subject in rfc2253 format.
Name-Email: <string>
The is an email address for the altSubjectName
Name-DNS: <string>
The is an DNS name for the altSubjectName
Name-URI: <string>
The is an URI for the altSubjectName
The following parameters are only used if a certificate (and not
a certificate signing request) is requested:
Serial: <sn>
If this parameter is given an X.509 certificate will be
generated. SN is expected to be a hex string representing an
unsigned integer of arbitary length. The special value
"random" can be used to crete a 64 bit random serial number.
Issuer-DN: <issuer_name>
This is the DN name of the issuer in rfc2253 format. If it is
not set the subject DN will be used instead. This creates a
self-signed certificate. Only in this case a special GnuPG
extension will then be included in the certificate to mark it
as a standalone certificate.
Creation-Date: <iso-date>
Set the notBefore date of the certificate. Either a date like
"1986-04-26" or a full timestamp like "19860426T042640" may be
used. The time is considered to be UTC. If it is not given
the current date is used.
Expire-Date: <iso-date>
Set the notBefore date of the certificate. Either a date like
"1986-04-26" or a full timestamp like "19860426T042640" may be
used. The time is considered to be UTC. If it is not given a
default value is used.
Signing-Key: <keygrip>
This gives the keygrip of the key used to sign the
certificate. If it is not given a self-signed certificate
will be created.
Hash-Algo: <hash-algo>
Use HASH-ALGO for this certificate. The supported hash
algorithms are: "sha-1", "sha-256", "sha-384" and "sha-512".
"sha-1" is the default.
Here is an example:
$ cat >foo <<EOF
%echo Generating a standard key
@ -111,18 +160,26 @@ EOF
#include "i18n.h"
enum para_name {
pKEYTYPE,
pKEYLENGTH,
pKEYGRIP,
pKEYUSAGE,
pNAMEDN,
pNAMEEMAIL,
pNAMEDNS,
pNAMEURI
};
enum para_name
{
pKEYTYPE,
pKEYLENGTH,
pKEYGRIP,
pKEYUSAGE,
pNAMEDN,
pNAMEEMAIL,
pNAMEDNS,
pNAMEURI,
pSERIAL,
pISSUERDN,
pNOTBEFORE,
pNOTAFTER,
pSIGNINGKEY,
pHASHALGO
};
struct para_data_s {
struct para_data_s
{
struct para_data_s *next;
int lnr;
enum para_name key;
@ -132,24 +189,28 @@ struct para_data_s {
} u;
};
struct reqgen_ctrl_s {
struct reqgen_ctrl_s
{
int lnr;
int dryrun;
ksba_writer_t writer;
};
static const char oidstr_keyUsage[] = "2.5.29.15";
static const char oidstr_basicConstraints[] = "2.5.29.19";
static const char oidstr_standaloneCertificate[] = "1.3.6.1.4.1.11591.2.2.1";
static int proc_parameters (ctrl_t ctrl,
struct para_data_s *para,
estream_t out_fp,
struct reqgen_ctrl_s *outctrl);
static int create_request (ctrl_t ctrl,
struct para_data_s *para,
const char *carddirect,
ksba_const_sexp_t public,
struct reqgen_ctrl_s *outctrl);
ksba_const_sexp_t sigkey,
ksba_writer_t writer);
@ -248,7 +309,7 @@ get_parameter_uint (struct para_data_s *para, enum para_name key)
/* Read the certificate generation parameters from FP and generate
(all) certificate requests. */
static int
read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp)
{
static struct {
const char *name;
@ -263,6 +324,14 @@ read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
{ "Name-Email", pNAMEEMAIL, 1 },
{ "Name-DNS", pNAMEDNS, 1 },
{ "Name-URI", pNAMEURI, 1 },
{ "Serial", pSERIAL },
{ "Issuer-DN", pISSUERDN },
{ "Creation-Date", pNOTBEFORE },
{ "Not-Before", pNOTBEFORE },
{ "Expire-Date", pNOTAFTER },
{ "Not-After", pNOTAFTER },
{ "Signing-Key", pSIGNINGKEY },
{ "Hash-Algo", pHASHALGO },
{ NULL, 0 }
};
char line[1024], *p;
@ -272,7 +341,6 @@ read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
struct reqgen_ctrl_s outctrl;
memset (&outctrl, 0, sizeof (outctrl));
outctrl.writer = writer;
err = NULL;
para = NULL;
@ -309,7 +377,7 @@ read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
outctrl.dryrun = 1;
else if (!ascii_strcasecmp( keyword, "%commit"))
{
rc = proc_parameters (ctrl, para, &outctrl);
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
if (rc)
goto leave;
any = 1;
@ -356,7 +424,7 @@ read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
if (keywords[i].key == pKEYTYPE && para)
{
rc = proc_parameters (ctrl, para, &outctrl);
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
if (rc)
goto leave;
any = 1;
@ -399,7 +467,7 @@ read_parameters (ctrl_t ctrl, estream_t fp, ksba_writer_t writer)
}
else if (para)
{
rc = proc_parameters (ctrl, para, &outctrl);
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
if (rc)
goto leave;
any = 1;
@ -438,18 +506,19 @@ has_invalid_email_chars (const char *s)
/* Check that all required parameters are given and perform the action */
static int
proc_parameters (ctrl_t ctrl,
struct para_data_s *para, struct reqgen_ctrl_s *outctrl)
proc_parameters (ctrl_t ctrl, struct para_data_s *para,
estream_t out_fp, struct reqgen_ctrl_s *outctrl)
{
gpg_error_t err;
struct para_data_s *r;
const char *s;
const char *s, *string;
int i;
unsigned int nbits;
char numbuf[20];
unsigned char keyparms[100];
int rc;
ksba_sexp_t public;
ksba_sexp_t sigkey = NULL;
int seq;
size_t erroff, errlen;
char *cardkeyid = NULL;
@ -539,6 +608,100 @@ proc_parameters (ctrl_t ctrl,
}
}
/* Check the optional serial number. */
string = get_parameter_value (para, pSERIAL, 0);
if (string)
{
if (!strcmp (string, "random"))
; /* Okay. */
else
{
for (s=string, i=0; hexdigitp (s); s++, i++)
;
if (*s)
{
r = get_parameter (para, pSERIAL, 0);
log_error (_("line %d: invalid serial number\n"), r->lnr);
xfree (cardkeyid);
return gpg_error (GPG_ERR_INV_PARAMETER);
}
}
}
/* Check the optional issuer DN. */
string = get_parameter_value (para, pISSUERDN, 0);
if (string)
{
err = ksba_dn_teststr (string, 0, &erroff, &errlen);
if (err)
{
r = get_parameter (para, pISSUERDN, 0);
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
log_error (_("line %d: invalid issuer name label `%.*s'\n"),
r->lnr, (int)errlen, string+erroff);
else
log_error (_("line %d: invalid issuer name `%s' at pos %d\n"),
r->lnr, string, (int)erroff);
xfree (cardkeyid);
return gpg_error (GPG_ERR_INV_PARAMETER);
}
}
/* Check the optional creation date. */
string = get_parameter_value (para, pNOTBEFORE, 0);
if (string && !string2isotime (NULL, string))
{
r = get_parameter (para, pNOTBEFORE, 0);
log_error (_("line %d: invalid date given\n"), r->lnr);
xfree (cardkeyid);
return gpg_error (GPG_ERR_INV_PARAMETER);
}
/* Check the optional expire date. */
string = get_parameter_value (para, pNOTAFTER, 0);
if (string && !string2isotime (NULL, string))
{
r = get_parameter (para, pNOTAFTER, 0);
log_error (_("line %d: invalid date given\n"), r->lnr);
xfree (cardkeyid);
return gpg_error (GPG_ERR_INV_PARAMETER);
}
/* Get the optional signing key. */
string = get_parameter_value (para, pSIGNINGKEY, 0);
if (string)
{
rc = gpgsm_agent_readkey (ctrl, 0, string, &sigkey);
if (rc)
{
r = get_parameter (para, pKEYTYPE, 0);
log_error (_("line %d: error getting signing key by keygrip `%s'"
": %s\n"), r->lnr, s, gpg_strerror (rc));
xfree (cardkeyid);
return rc;
}
}
/* Check the optional hash-algo. */
{
int mdalgo;
string = get_parameter_value (para, pHASHALGO, 0);
if (string && !((mdalgo = gcry_md_map_name (string))
&& (mdalgo == GCRY_MD_SHA1
|| mdalgo == GCRY_MD_SHA256
|| mdalgo == GCRY_MD_SHA384
|| mdalgo == GCRY_MD_SHA512)))
{
r = get_parameter (para, pHASHALGO, 0);
log_error (_("line %d: invalid hash algorithm given\n"), r->lnr);
xfree (cardkeyid);
return gpg_error (GPG_ERR_INV_PARAMETER);
}
}
/* Create or retrieve the public key. */
if (cardkeyid) /* Take the key from the current smart card. */
{
rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public);
@ -547,6 +710,7 @@ proc_parameters (ctrl_t ctrl,
r = get_parameter (para, pKEYTYPE, 0);
log_error (_("line %d: error reading key `%s' from card: %s\n"),
r->lnr, cardkeyid, gpg_strerror (rc));
xfree (sigkey);
xfree (cardkeyid);
return rc;
}
@ -559,11 +723,12 @@ proc_parameters (ctrl_t ctrl,
r = get_parameter (para, pKEYTYPE, 0);
log_error (_("line %d: error getting key by keygrip `%s': %s\n"),
r->lnr, s, gpg_strerror (rc));
xfree (sigkey);
xfree (cardkeyid);
return rc;
}
}
else /* Generate new key. */
else if (!outctrl->dryrun) /* Generate new key. */
{
sprintf (numbuf, "%u", nbits);
snprintf ((char*)keyparms, DIM (keyparms)-1,
@ -575,12 +740,45 @@ proc_parameters (ctrl_t ctrl,
r = get_parameter (para, pKEYTYPE, 0);
log_error (_("line %d: key generation failed: %s <%s>\n"),
r->lnr, gpg_strerror (rc), gpg_strsource (rc));
xfree (sigkey);
xfree (cardkeyid);
return rc;
}
}
rc = create_request (ctrl, para, cardkeyid, public, outctrl);
if (!outctrl->dryrun)
{
Base64Context b64writer = NULL;
ksba_writer_t writer;
int create_cert ;
create_cert = !!get_parameter_value (para, pSERIAL, 0);
ctrl->pem_name = create_cert? "CERTIFICATE" : "CERTIFICATE REQUEST";
rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, &writer);
if (rc)
log_error ("can't create writer: %s\n", gpg_strerror (rc));
else
{
rc = create_request (ctrl, para, cardkeyid, public, sigkey, writer);
if (!rc)
{
rc = gpgsm_finish_writer (b64writer);
if (rc)
log_error ("write failed: %s\n", gpg_strerror (rc));
else
{
gpgsm_status (ctrl, STATUS_KEY_CREATED, "P");
log_info ("certificate%s created\n",
create_cert?"":" request");
}
}
gpgsm_destroy_writer (b64writer);
}
}
xfree (sigkey);
xfree (public);
xfree (cardkeyid);
@ -595,25 +793,34 @@ create_request (ctrl_t ctrl,
struct para_data_s *para,
const char *carddirect,
ksba_const_sexp_t public,
struct reqgen_ctrl_s *outctrl)
ksba_const_sexp_t sigkey,
ksba_writer_t writer)
{
ksba_certreq_t cr;
gpg_error_t err;
gcry_md_hd_t md;
ksba_stop_reason_t stopreason;
int rc = 0;
const char *s;
const char *s, *string;
unsigned int use;
int seq;
char *buf, *p;
size_t len;
char numbuf[30];
ksba_isotime_t atime;
int certmode = 0;
int mdalgo;
err = ksba_certreq_new (&cr);
if (err)
return err;
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
string = get_parameter_value (para, pHASHALGO, 0);
if (string)
mdalgo = gcry_md_map_name (string);
else
mdalgo = GCRY_MD_SHA1;
rc = gcry_md_open (&md, mdalgo, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
@ -623,7 +830,7 @@ create_request (ctrl_t ctrl,
gcry_md_start_debug (md, "cr.cri");
ksba_certreq_set_hash_function (cr, HASH_FNC, md);
ksba_certreq_set_writer (cr, outctrl->writer);
ksba_certreq_set_writer (cr, writer);
err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0));
if (err)
@ -720,7 +927,7 @@ create_request (ctrl_t ctrl,
goto leave;
}
/* Set key usage flags. */
use = get_parameter_uint (para, pKEYUSAGE);
if (use == GCRY_PK_USAGE_SIGN)
{
@ -749,6 +956,170 @@ create_request (ctrl_t ctrl,
}
/* See whether we want to create an X.509 certificate. */
string = get_parameter_value (para, pSERIAL, 0);
if (string)
{
certmode = 1;
/* Store the serial number. */
if (!strcmp (string, "random"))
{
char snbuf[3+8+1];
memcpy (snbuf, "(8:", 3);
gcry_create_nonce (snbuf+3, 8);
/* Clear high bit to guarantee a positive integer. */
snbuf[3] &= 0x7f;
snbuf[3+8] = ')';
err = ksba_certreq_set_serial (cr, snbuf);
}
else
{
char *hexbuf;
/* Allocate a buffer large enough to prefix the string with
a '0' so to have an even number of digits. Prepend two
further '0' so that the binary result will have a leading
0 byte and thus can't be the representation of a negative
number. Note that ksba_certreq_set_serial strips all
unneeded leading 0 bytes. */
hexbuf = p = xtrymalloc (2 + 1 + strlen (string) + 1);
if (!hexbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
if ((strlen (string) & 1))
*p++ = '0';
*p++ = '0';
*p++ = '0';
strcpy (p, string);
for (p=hexbuf, len=0; p[0] && p[1]; p += 2)
((unsigned char*)hexbuf)[len++] = xtoi_2 (s);
/* Now build the S-expression. */
snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
buf = p = xtrymalloc (1 + strlen (numbuf) + len + 1 + 1);
if (!buf)
{
err = gpg_error_from_syserror ();
xfree (hexbuf);
goto leave;
}
p = stpcpy (stpcpy (buf, "("), numbuf);
memcpy (p, hexbuf, len);
p += len;
strcpy (p, ")");
xfree (hexbuf);
err = ksba_certreq_set_serial (cr, buf);
xfree (buf);
}
if (err)
{
log_error ("error setting the serial number: %s\n",
gpg_strerror (err));
goto leave;
}
/* Store the issuer DN. If no issuer DN is given and no signing
key has been set we add the standalone extension and the
basic constraints to mark it as a self-signed CA
certificate. */
string = get_parameter_value (para, pISSUERDN, 0);
if (string)
{
/* Issuer DN given. Note that this may be the same as the
subject DN and thus this could as well be a self-signed
certificate. However the caller needs to explicitly
specify basicConstraints and so forth. */
err = ksba_certreq_set_issuer (cr, string);
if (err)
{
log_error ("error setting the issuer DN: %s\n",
gpg_strerror (err));
goto leave;
}
}
else if (!string && !sigkey)
{
/* Self-signed certificate requested. Add basicConstraints
and the custom GnuPG standalone extension. */
err = ksba_certreq_add_extension (cr, oidstr_basicConstraints, 1,
"\x30\x03\x01\x01\xff", 5);
if (err)
goto leave;
err = ksba_certreq_add_extension (cr, oidstr_standaloneCertificate, 0,
"\x01\x01\xff", 3);
if (err)
goto leave;
}
/* Store the creation date. */
string = get_parameter_value (para, pNOTBEFORE, 0);
if (string)
{
if (!string2isotime (atime, string))
BUG (); /* We already checked the value. */
}
else
gnupg_get_isotime (atime);
err = ksba_certreq_set_validity (cr, 0, atime);
if (err)
{
log_error ("error setting the creation date: %s\n",
gpg_strerror (err));
goto leave;
}
/* Store the expire date. If it is not given, libksba inserts a
default value. */
string = get_parameter_value (para, pNOTAFTER, 0);
if (string)
{
if (!string2isotime (atime, string))
BUG (); /* We already checked the value. */
err = ksba_certreq_set_validity (cr, 1, atime);
if (err)
{
log_error ("error setting the expire date: %s\n",
gpg_strerror (err));
goto leave;
}
}
/* Figure out the signing algorithm. If no sigkey has been
given we set it to the public key to create a self-signed
certificate. */
if (!sigkey)
sigkey = public;
{
unsigned char *siginfo;
err = transform_sigval (sigkey,
gcry_sexp_canon_len (sigkey, 0, NULL, NULL),
mdalgo, &siginfo, NULL);
if (!err)
{
err = ksba_certreq_set_siginfo (cr, siginfo);
xfree (siginfo);
}
if (err)
{
log_error ("error setting the siginfo: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
}
else
sigkey = public;
do
{
err = ksba_certreq_build (cr, &stopreason);
@ -764,17 +1135,17 @@ create_request (ctrl_t ctrl,
size_t n;
unsigned char grip[20];
char hexgrip[41];
unsigned char *sigval;
unsigned char *sigval, *newsigval;
size_t siglen;
n = gcry_sexp_canon_len (public, 0, NULL, NULL);
n = gcry_sexp_canon_len (sigkey, 0, NULL, NULL);
if (!n)
{
log_error ("libksba did not return a proper S-Exp\n");
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)public, n);
rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, n);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
@ -790,14 +1161,15 @@ create_request (ctrl_t ctrl,
gcry_sexp_release (s_pkey);
bin2hex (grip, 20, hexgrip);
log_info ("about to sign CSR for key: &%s\n", hexgrip);
log_info ("about to sign the %s for key: &%s\n",
certmode? "certificate":"CSR", hexgrip);
if (carddirect)
rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
gcry_md_read(md, GCRY_MD_SHA1),
gcry_md_get_algo_dlen (GCRY_MD_SHA1),
GCRY_MD_SHA1,
&sigval, &siglen);
gcry_md_read (md, mdalgo),
gcry_md_get_algo_dlen (mdalgo),
mdalgo,
&sigval, &siglen);
else
{
char *orig_codeset;
@ -810,9 +1182,9 @@ create_request (ctrl_t ctrl,
" more.\n"));
i18n_switchback (orig_codeset);
rc = gpgsm_agent_pksign (ctrl, hexgrip, desc,
gcry_md_read(md, GCRY_MD_SHA1),
gcry_md_get_algo_dlen (GCRY_MD_SHA1),
GCRY_MD_SHA1,
gcry_md_read(md, mdalgo),
gcry_md_get_algo_dlen (mdalgo),
mdalgo,
&sigval, &siglen);
xfree (desc);
}
@ -822,8 +1194,14 @@ create_request (ctrl_t ctrl,
goto leave;
}
err = ksba_certreq_set_sig_val (cr, sigval);
err = transform_sigval (sigval, siglen, mdalgo,
&newsigval, NULL);
xfree (sigval);
if (!err)
{
err = ksba_certreq_set_sig_val (cr, newsigval);
xfree (newsigval);
}
if (err)
{
log_error ("failed to store the sig_val: %s\n",
@ -850,18 +1228,8 @@ int
gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream)
{
int rc;
Base64Context b64writer = NULL;
ksba_writer_t writer;
ctrl->pem_name = "CERTIFICATE REQUEST";
rc = gpgsm_create_writer (&b64writer, ctrl, out_stream, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
rc = read_parameters (ctrl, in_stream, writer);
rc = read_parameters (ctrl, in_stream, out_stream);
if (rc)
{
log_error ("error creating certificate request: %s <%s>\n",
@ -869,17 +1237,6 @@ gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream)
goto leave;
}
rc = gpgsm_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
gpgsm_status (ctrl, STATUS_KEY_CREATED, "P");
log_info ("certificate request created\n");
leave:
gpgsm_destroy_writer (b64writer);
return rc;
}

View File

@ -429,6 +429,10 @@ int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
/*-- misc.c --*/
void setup_pinentry_env (void);
gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen,
int mdalgo,
unsigned char **r_newsigval,
size_t *r_newsigvallen);

View File

@ -186,6 +186,7 @@ static struct
/* GnuPG extensions */
{ "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
{ "1.3.6.1.4.1.11591.2.2.1", "standaloneCertificate" },
/* Extensions used by the Bundesnetzagentur. */
{ "1.3.6.1.4.1.8301.3.5", "validityModel" },

132
sm/misc.c
View File

@ -1,5 +1,5 @@
/* misc.c - Miscellaneous fucntions
* Copyright (C) 2004, 2009 Free Software Foundation, Inc.
* Copyright (C) 2004, 2009, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -31,6 +31,9 @@
#include "gpgsm.h"
#include "i18n.h"
#include "sysutils.h"
#include "../common/tlv.h"
#include "../common/sexp-parse.h"
/* Setup the environment so that the pinentry is able to get all
required information. This is used prior to an exec of the
@ -86,3 +89,130 @@ setup_pinentry_env (void)
#endif /*!HAVE_W32_SYSTEM*/
}
/* Transform a sig-val style s-expression as returned by Libgcrypt to
one which includes an algorithm identifier encoding the public key
and the hash algorithm. The public key algorithm is taken directly
from SIGVAL and the hash algorithm is given by MDALGO. This is
required because X.509 merges the public key algorithm and the hash
algorithm into one OID but Libgcrypt is not aware of that. The
function ignores missing parameters so that it can also be used to
create an siginfo value as expected by ksba_certreq_set_siginfo.
To create a siginfo s-expression a public-key s-expression may be
used instead of a sig-val. We only support RSA for now. */
gpg_error_t
transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
unsigned char **r_newsigval, size_t *r_newsigvallen)
{
gpg_error_t err;
const unsigned char *buf, *tok;
size_t buflen, toklen;
int depth, last_depth1, last_depth2;
int is_pubkey = 0;
const unsigned char *rsa_s = NULL;
size_t rsa_s_len;
const char *oid;
gcry_sexp_t sexp;
*r_newsigval = NULL;
if (r_newsigvallen)
*r_newsigvallen = 0;
buf = sigval;
buflen = sigvallen;
depth = 0;
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if (tok && toklen == 7 && !memcmp ("sig-val", tok, toklen))
;
else if (tok && toklen == 10 && !memcmp ("public-key", tok, toklen))
is_pubkey = 1;
else
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
last_depth1 = depth;
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
&& depth && depth >= last_depth1)
{
if (tok)
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if (tok && toklen == 1)
{
const unsigned char **mpi;
size_t *mpi_len;
switch (*tok)
{
case 's': mpi = &rsa_s; mpi_len = &rsa_s_len; break;
default: mpi = NULL; mpi_len = NULL; break;
}
if (mpi && *mpi)
return gpg_error (GPG_ERR_DUP_VALUE);
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if (tok && mpi)
{
*mpi = tok;
*mpi_len = toklen;
}
}
/* Skip to the end of the list. */
last_depth2 = depth;
while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
&& depth && depth >= last_depth2)
;
if (err)
return err;
}
if (err)
return err;
/* Map the hash algorithm to an OID. */
switch (mdalgo)
{
case GCRY_MD_SHA1:
oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */
break;
case GCRY_MD_SHA256:
oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */
break;
case GCRY_MD_SHA384:
oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */
break;
case GCRY_MD_SHA512:
oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */
break;
default:
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
if (rsa_s && !is_pubkey)
err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))",
oid, (int)rsa_s_len, rsa_s);
else
err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid);
if (err)
return err;
err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen);
gcry_sexp_release (sexp);
return err;
}