2011-03-01 14:42:56 +01:00
|
|
|
|
/* certreqgen.c - Generate a key and a certification [request]
|
|
|
|
|
* Copyright (C) 2002, 2003, 2005, 2007, 2010,
|
|
|
|
|
* 2011 Free Software Foundation, Inc.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2007-07-04 21:49:40 +02:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-08-05 19:11:04 +02:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG 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.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
2011-03-01 17:08:49 +01:00
|
|
|
|
The format of the parameter file is described in the manual under
|
|
|
|
|
"Unattended Usage".
|
|
|
|
|
|
|
|
|
|
Here is an example:
|
|
|
|
|
$ cat >foo <<EOF
|
|
|
|
|
%echo Generating a standard key
|
|
|
|
|
Key-Type: RSA
|
2017-09-08 00:39:37 +02:00
|
|
|
|
Key-Length: 3072
|
2011-03-01 17:08:49 +01:00
|
|
|
|
Name-DN: CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Ddorf,C=DE
|
|
|
|
|
Name-Email: joe@foo.bar
|
|
|
|
|
# Do a commit here, so that we can later print a "done"
|
|
|
|
|
%commit
|
|
|
|
|
%echo done
|
|
|
|
|
EOF
|
2011-12-06 19:57:27 +01:00
|
|
|
|
|
|
|
|
|
This parameter file was used to create the STEED CA:
|
|
|
|
|
Key-Type: RSA
|
|
|
|
|
Key-Length: 1024
|
|
|
|
|
Key-Grip: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
|
|
|
|
|
Key-Usage: cert
|
|
|
|
|
Serial: 1
|
|
|
|
|
Name-DN: CN=The STEED Self-Signing Nonthority
|
|
|
|
|
Not-Before: 2011-11-11
|
|
|
|
|
Not-After: 2106-02-06
|
|
|
|
|
Subject-Key-Id: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
|
|
|
|
|
Extension: 2.5.29.19 c 30060101ff020101
|
|
|
|
|
Extension: 1.3.6.1.4.1.11591.2.2.2 n 0101ff
|
|
|
|
|
Signing-Key: 68A638998DFABAC510EA645CE34F9686B2EDF7EA
|
|
|
|
|
%commit
|
|
|
|
|
|
2024-03-27 12:12:52 +01:00
|
|
|
|
Commnn extensions:
|
|
|
|
|
ExtKeyUsage: clientAuth (suggested) serverAuth (suggested)
|
|
|
|
|
-> 2.5.29.37 n 301406082B0601050507030206082B06010505070301
|
|
|
|
|
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
2011-02-04 12:57:53 +01:00
|
|
|
|
#include <unistd.h>
|
2003-08-05 19:11:04 +02:00
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
|
#include "gpgsm.h"
|
|
|
|
|
#include <gcrypt.h>
|
|
|
|
|
#include <ksba.h>
|
|
|
|
|
|
|
|
|
|
#include "keydb.h"
|
2017-03-07 12:21:23 +01:00
|
|
|
|
#include "../common/i18n.h"
|
2020-05-18 19:32:30 +02:00
|
|
|
|
#include "../common/membuf.h"
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
enum para_name
|
|
|
|
|
{
|
|
|
|
|
pKEYTYPE,
|
|
|
|
|
pKEYLENGTH,
|
|
|
|
|
pKEYGRIP,
|
2020-03-26 07:54:14 +01:00
|
|
|
|
pKEYCURVE,
|
2011-03-01 14:42:56 +01:00
|
|
|
|
pKEYUSAGE,
|
|
|
|
|
pNAMEDN,
|
|
|
|
|
pNAMEEMAIL,
|
|
|
|
|
pNAMEDNS,
|
|
|
|
|
pNAMEURI,
|
|
|
|
|
pSERIAL,
|
|
|
|
|
pISSUERDN,
|
|
|
|
|
pNOTBEFORE,
|
|
|
|
|
pNOTAFTER,
|
|
|
|
|
pSIGNINGKEY,
|
2011-12-06 19:57:27 +01:00
|
|
|
|
pHASHALGO,
|
2011-12-06 21:43:18 +01:00
|
|
|
|
pAUTHKEYID,
|
2011-12-06 19:57:27 +01:00
|
|
|
|
pSUBJKEYID,
|
|
|
|
|
pEXTENSION
|
2011-03-01 14:42:56 +01:00
|
|
|
|
};
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
struct para_data_s
|
|
|
|
|
{
|
2003-08-05 19:11:04 +02:00
|
|
|
|
struct para_data_s *next;
|
|
|
|
|
int lnr;
|
|
|
|
|
enum para_name key;
|
|
|
|
|
union {
|
2011-02-04 12:57:53 +01:00
|
|
|
|
unsigned int usage;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
char value[1];
|
|
|
|
|
} u;
|
|
|
|
|
};
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
struct reqgen_ctrl_s
|
|
|
|
|
{
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int lnr;
|
|
|
|
|
int dryrun;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2011-12-06 21:43:18 +01:00
|
|
|
|
static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
|
2011-12-06 19:57:27 +01:00
|
|
|
|
static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14";
|
2004-06-06 15:00:59 +02:00
|
|
|
|
static const char oidstr_keyUsage[] = "2.5.29.15";
|
2011-03-01 14:42:56 +01:00
|
|
|
|
static const char oidstr_basicConstraints[] = "2.5.29.19";
|
|
|
|
|
static const char oidstr_standaloneCertificate[] = "1.3.6.1.4.1.11591.2.2.1";
|
2004-06-06 15:00:59 +02:00
|
|
|
|
|
|
|
|
|
|
2004-04-26 15:29:09 +02:00
|
|
|
|
static int proc_parameters (ctrl_t ctrl,
|
|
|
|
|
struct para_data_s *para,
|
2011-03-01 14:42:56 +01:00
|
|
|
|
estream_t out_fp,
|
2003-08-05 19:11:04 +02:00
|
|
|
|
struct reqgen_ctrl_s *outctrl);
|
2004-04-26 15:29:09 +02:00
|
|
|
|
static int create_request (ctrl_t ctrl,
|
|
|
|
|
struct para_data_s *para,
|
2006-10-11 19:52:15 +02:00
|
|
|
|
const char *carddirect,
|
2003-12-17 13:28:24 +01:00
|
|
|
|
ksba_const_sexp_t public,
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_const_sexp_t sigkey,
|
|
|
|
|
ksba_writer_t writer);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
release_parameter_list (struct para_data_s *r)
|
|
|
|
|
{
|
|
|
|
|
struct para_data_s *r2;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
for (; r ; r = r2)
|
|
|
|
|
{
|
|
|
|
|
r2 = r->next;
|
|
|
|
|
xfree(r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct para_data_s *
|
2005-07-21 20:29:13 +02:00
|
|
|
|
get_parameter (struct para_data_s *para, enum para_name key, int seq)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
struct para_data_s *r;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-07-21 20:29:13 +02:00
|
|
|
|
for (r = para; r ; r = r->next)
|
|
|
|
|
if ( r->key == key && !seq--)
|
|
|
|
|
return r;
|
|
|
|
|
return NULL;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
2005-07-21 20:29:13 +02:00
|
|
|
|
get_parameter_value (struct para_data_s *para, enum para_name key, int seq)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
struct para_data_s *r = get_parameter (para, key, seq);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return (r && *r->u.value)? r->u.value : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
get_parameter_algo (struct para_data_s *para, enum para_name key)
|
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
struct para_data_s *r = get_parameter (para, key, 0);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (!r)
|
|
|
|
|
return -1;
|
|
|
|
|
if (digitp (r->u.value))
|
|
|
|
|
return atoi( r->u.value );
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return gcry_pk_map_name (r->u.value);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-06 15:00:59 +02:00
|
|
|
|
/* Parse the usage parameter. Returns 0 on success. Note that we
|
2003-08-05 19:11:04 +02:00
|
|
|
|
only care about sign and encrypt and don't (yet) allow all the
|
|
|
|
|
other X.509 usage to be specified; instead we will use a fixed
|
2004-06-06 15:00:59 +02:00
|
|
|
|
mapping to the X.509 usage flags. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
static int
|
|
|
|
|
parse_parameter_usage (struct para_data_s *para, enum para_name key)
|
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
struct para_data_s *r = get_parameter (para, key, 0);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
char *p, *pn;
|
|
|
|
|
unsigned int use;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (!r)
|
|
|
|
|
return 0; /* none (this is an optional parameter)*/
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
use = 0;
|
|
|
|
|
pn = r->u.value;
|
|
|
|
|
while ( (p = strsep (&pn, " \t,")) )
|
|
|
|
|
{
|
|
|
|
|
if (!*p)
|
|
|
|
|
;
|
|
|
|
|
else if ( !ascii_strcasecmp (p, "sign") )
|
|
|
|
|
use |= GCRY_PK_USAGE_SIGN;
|
2011-12-06 19:57:27 +01:00
|
|
|
|
else if ( !ascii_strcasecmp (p, "encrypt")
|
|
|
|
|
|| !ascii_strcasecmp (p, "encr") )
|
2003-08-05 19:11:04 +02:00
|
|
|
|
use |= GCRY_PK_USAGE_ENCR;
|
2011-12-06 19:57:27 +01:00
|
|
|
|
else if ( !ascii_strcasecmp (p, "cert") )
|
|
|
|
|
use |= GCRY_PK_USAGE_CERT;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error ("line %d: invalid usage list\n", r?r->lnr:0);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return -1; /* error */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
r->u.usage = use;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
|
get_parameter_uint (struct para_data_s *para, enum para_name key)
|
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
struct para_data_s *r = get_parameter (para, key, 0);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
if (!r)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2004-06-06 15:00:59 +02:00
|
|
|
|
if (r->key == pKEYUSAGE)
|
|
|
|
|
return r->u.usage;
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return (unsigned int)strtoul (r->u.value, NULL, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read the certificate generation parameters from FP and generate
|
|
|
|
|
(all) certificate requests. */
|
|
|
|
|
static int
|
2011-03-01 14:42:56 +01:00
|
|
|
|
read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
static struct {
|
|
|
|
|
const char *name;
|
|
|
|
|
enum para_name key;
|
2005-07-21 20:29:13 +02:00
|
|
|
|
int allow_dups;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
} keywords[] = {
|
|
|
|
|
{ "Key-Type", pKEYTYPE},
|
|
|
|
|
{ "Key-Length", pKEYLENGTH },
|
2005-07-25 16:35:04 +02:00
|
|
|
|
{ "Key-Grip", pKEYGRIP },
|
2020-03-26 07:54:14 +01:00
|
|
|
|
{ "Key-Curve", pKEYCURVE },
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{ "Key-Usage", pKEYUSAGE },
|
|
|
|
|
{ "Name-DN", pNAMEDN },
|
2005-07-21 20:29:13 +02:00
|
|
|
|
{ "Name-Email", pNAMEEMAIL, 1 },
|
|
|
|
|
{ "Name-DNS", pNAMEDNS, 1 },
|
|
|
|
|
{ "Name-URI", pNAMEURI, 1 },
|
2011-03-01 14:42:56 +01:00
|
|
|
|
{ "Serial", pSERIAL },
|
|
|
|
|
{ "Issuer-DN", pISSUERDN },
|
|
|
|
|
{ "Creation-Date", pNOTBEFORE },
|
|
|
|
|
{ "Not-Before", pNOTBEFORE },
|
|
|
|
|
{ "Expire-Date", pNOTAFTER },
|
|
|
|
|
{ "Not-After", pNOTAFTER },
|
|
|
|
|
{ "Signing-Key", pSIGNINGKEY },
|
|
|
|
|
{ "Hash-Algo", pHASHALGO },
|
2011-12-06 21:43:18 +01:00
|
|
|
|
{ "Authority-Key-Id", pAUTHKEYID },
|
2011-12-06 19:57:27 +01:00
|
|
|
|
{ "Subject-Key-Id", pSUBJKEYID },
|
|
|
|
|
{ "Extension", pEXTENSION, 1 },
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{ NULL, 0 }
|
|
|
|
|
};
|
|
|
|
|
char line[1024], *p;
|
|
|
|
|
const char *err = NULL;
|
|
|
|
|
struct para_data_s *para, *r;
|
|
|
|
|
int i, rc = 0, any = 0;
|
|
|
|
|
struct reqgen_ctrl_s outctrl;
|
|
|
|
|
|
|
|
|
|
memset (&outctrl, 0, sizeof (outctrl));
|
|
|
|
|
|
|
|
|
|
err = NULL;
|
|
|
|
|
para = NULL;
|
2007-08-22 12:55:07 +02:00
|
|
|
|
while (es_fgets (line, DIM(line)-1, fp) )
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
char *keyword, *value;
|
|
|
|
|
|
|
|
|
|
outctrl.lnr++;
|
|
|
|
|
if (*line && line[strlen(line)-1] != '\n')
|
|
|
|
|
{
|
|
|
|
|
err = "line too long";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
for (p=line; spacep (p); p++)
|
|
|
|
|
;
|
|
|
|
|
if (!*p || *p == '#')
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
keyword = p;
|
|
|
|
|
if (*keyword == '%')
|
|
|
|
|
{
|
2010-04-23 03:59:08 +02:00
|
|
|
|
for (; *p && !ascii_isspace (*p); p++)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
;
|
|
|
|
|
if (*p)
|
|
|
|
|
*p++ = 0;
|
2010-04-23 03:59:08 +02:00
|
|
|
|
for (; ascii_isspace (*p); p++)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
;
|
|
|
|
|
value = p;
|
|
|
|
|
trim_trailing_spaces (value);
|
|
|
|
|
|
|
|
|
|
if (!ascii_strcasecmp (keyword, "%echo"))
|
|
|
|
|
log_info ("%s\n", value);
|
|
|
|
|
else if (!ascii_strcasecmp (keyword, "%dry-run"))
|
|
|
|
|
outctrl.dryrun = 1;
|
|
|
|
|
else if (!ascii_strcasecmp( keyword, "%commit"))
|
|
|
|
|
{
|
2011-03-01 14:42:56 +01:00
|
|
|
|
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
any = 1;
|
|
|
|
|
release_parameter_list (para);
|
|
|
|
|
para = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_info ("skipping control '%s' (%s)\n", keyword, value);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!(p = strchr (p, ':')) || p == keyword)
|
|
|
|
|
{
|
|
|
|
|
err = "missing colon";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (*p)
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
for (; spacep (p); p++)
|
|
|
|
|
;
|
|
|
|
|
if (!*p)
|
|
|
|
|
{
|
|
|
|
|
err = "missing argument";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
value = p;
|
|
|
|
|
trim_trailing_spaces (value);
|
|
|
|
|
|
|
|
|
|
for (i=0; (keywords[i].name
|
|
|
|
|
&& ascii_strcasecmp (keywords[i].name, keyword)); i++)
|
|
|
|
|
;
|
|
|
|
|
if (!keywords[i].name)
|
|
|
|
|
{
|
|
|
|
|
err = "unknown keyword";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (keywords[i].key != pKEYTYPE && !para)
|
|
|
|
|
{
|
|
|
|
|
err = "parameter block does not start with \"Key-Type\"";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (keywords[i].key == pKEYTYPE && para)
|
|
|
|
|
{
|
2011-03-01 14:42:56 +01:00
|
|
|
|
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
any = 1;
|
|
|
|
|
release_parameter_list (para);
|
|
|
|
|
para = NULL;
|
|
|
|
|
}
|
2005-07-21 20:29:13 +02:00
|
|
|
|
else if (!keywords[i].allow_dups)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
for (r = para; r && r->key != keywords[i].key; r = r->next)
|
|
|
|
|
;
|
|
|
|
|
if (r)
|
|
|
|
|
{
|
|
|
|
|
err = "duplicate keyword";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r = xtrycalloc (1, sizeof *r + strlen( value ));
|
|
|
|
|
if (!r)
|
|
|
|
|
{
|
|
|
|
|
err = "out of core";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
r->lnr = outctrl.lnr;
|
|
|
|
|
r->key = keywords[i].key;
|
|
|
|
|
strcpy (r->u.value, value);
|
|
|
|
|
r->next = para;
|
|
|
|
|
para = r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("line %d: %s\n", outctrl.lnr, err);
|
|
|
|
|
rc = gpg_error (GPG_ERR_GENERAL);
|
|
|
|
|
}
|
2007-08-22 12:55:07 +02:00
|
|
|
|
else if (es_ferror(fp))
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) );
|
|
|
|
|
rc = gpg_error (GPG_ERR_GENERAL);
|
|
|
|
|
}
|
|
|
|
|
else if (para)
|
|
|
|
|
{
|
2011-03-01 14:42:56 +01:00
|
|
|
|
rc = proc_parameters (ctrl, para, out_fp, &outctrl);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
any = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rc && !any)
|
|
|
|
|
rc = gpg_error (GPG_ERR_NO_DATA);
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
release_parameter_list (para);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check whether there are invalid characters in the email address S */
|
|
|
|
|
static int
|
|
|
|
|
has_invalid_email_chars (const char *s)
|
|
|
|
|
{
|
|
|
|
|
int at_seen=0;
|
|
|
|
|
static char valid_chars[] = "01234567890_-."
|
|
|
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
|
for (; *s; s++)
|
|
|
|
|
{
|
|
|
|
|
if (*s & 0x80)
|
|
|
|
|
return 1;
|
|
|
|
|
if (*s == '@')
|
|
|
|
|
at_seen++;
|
|
|
|
|
else if (!at_seen && !( !!strchr (valid_chars, *s) || *s == '+'))
|
|
|
|
|
return 1;
|
|
|
|
|
else if (at_seen && !strchr (valid_chars, *s))
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return at_seen != 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Check that all required parameters are given and perform the action */
|
|
|
|
|
static int
|
2011-03-01 14:42:56 +01:00
|
|
|
|
proc_parameters (ctrl_t ctrl, struct para_data_s *para,
|
|
|
|
|
estream_t out_fp, struct reqgen_ctrl_s *outctrl)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2006-07-03 15:26:19 +02:00
|
|
|
|
gpg_error_t err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
struct para_data_s *r;
|
2011-03-01 14:42:56 +01:00
|
|
|
|
const char *s, *string;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int i;
|
2020-03-26 03:30:58 +01:00
|
|
|
|
int algo;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
unsigned int nbits;
|
|
|
|
|
char numbuf[20];
|
|
|
|
|
unsigned char keyparms[100];
|
2011-06-01 21:43:30 +02:00
|
|
|
|
int rc = 0;
|
2014-09-18 15:17:44 +02:00
|
|
|
|
ksba_sexp_t public = NULL;
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_sexp_t sigkey = NULL;
|
2005-07-21 20:29:13 +02:00
|
|
|
|
int seq;
|
2006-07-03 15:26:19 +02:00
|
|
|
|
size_t erroff, errlen;
|
2006-10-11 19:52:15 +02:00
|
|
|
|
char *cardkeyid = NULL;
|
2006-07-03 15:26:19 +02:00
|
|
|
|
|
|
|
|
|
/* Check that we have all required parameters; */
|
2020-07-08 14:40:34 +02:00
|
|
|
|
log_assert (get_parameter (para, pKEYTYPE, 0));
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2020-03-26 03:30:58 +01:00
|
|
|
|
/* There is a problem with pkcs-10 on how to use ElGamal because it
|
|
|
|
|
is expected that a PK algorithm can always be used for
|
|
|
|
|
signing. Another problem is that on-card generated encryption
|
|
|
|
|
keys may not be used for signing. */
|
|
|
|
|
algo = get_parameter_algo (para, pKEYTYPE);
|
|
|
|
|
if (!algo && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
|
2006-10-11 19:52:15 +02:00
|
|
|
|
{
|
|
|
|
|
/* Hack to allow creation of certificates directly from a smart
|
|
|
|
|
card. For example: "Key-Type: card:OPENPGP.3". */
|
|
|
|
|
if (!strncmp (s, "card:", 5) && s[5])
|
|
|
|
|
cardkeyid = xtrystrdup (s+5);
|
|
|
|
|
}
|
2020-03-26 03:30:58 +01:00
|
|
|
|
if (algo < 1 && !cardkeyid)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
r = get_parameter (para, pKEYTYPE, 0);
|
2020-03-26 07:54:14 +01:00
|
|
|
|
if (r)
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0);
|
2020-03-26 07:54:14 +01:00
|
|
|
|
else
|
|
|
|
|
log_error ("No Key-Type specified\n");
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2009-11-23 20:18:04 +01:00
|
|
|
|
/* Check the keylength. NOTE: If you change this make sure that it
|
2024-05-13 00:09:23 +02:00
|
|
|
|
matches the gpgconflist item in gpgsm.c */
|
2005-07-21 20:29:13 +02:00
|
|
|
|
if (!get_parameter (para, pKEYLENGTH, 0))
|
2017-09-08 00:39:37 +02:00
|
|
|
|
nbits = 3072;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
else
|
|
|
|
|
nbits = get_parameter_uint (para, pKEYLENGTH);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
if ((nbits < 1024 || nbits > 4096) && !cardkeyid)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2004-08-05 11:24:36 +02:00
|
|
|
|
/* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */
|
2005-07-21 20:29:13 +02:00
|
|
|
|
r = get_parameter (para, pKEYLENGTH, 0);
|
2004-08-05 11:24:36 +02:00
|
|
|
|
log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, nbits, 1024, 4096);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2006-08-16 12:47:53 +02:00
|
|
|
|
/* Check the usage. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (parse_parameter_usage (para, pKEYUSAGE))
|
2006-10-11 19:52:15 +02:00
|
|
|
|
{
|
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2006-07-03 15:26:19 +02:00
|
|
|
|
/* Check that there is a subject name and that this DN fits our
|
|
|
|
|
requirements. */
|
2005-07-21 20:29:13 +02:00
|
|
|
|
if (!(s=get_parameter_value (para, pNAMEDN, 0)))
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2006-07-03 15:26:19 +02:00
|
|
|
|
r = get_parameter (para, pNAMEDN, 0);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: no subject name given\n"), r?r->lnr:0);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2006-07-03 15:26:19 +02:00
|
|
|
|
err = ksba_dn_teststr (s, 0, &erroff, &errlen);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pNAMEDN, 0);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: invalid subject name label '%.*s'\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, (int)errlen, s+erroff);
|
2006-07-03 15:26:19 +02:00
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: invalid subject name '%s' at pos %d\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, s, (int)erroff);
|
2006-07-03 15:26:19 +02:00
|
|
|
|
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2006-07-03 15:26:19 +02:00
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2006-07-03 15:26:19 +02:00
|
|
|
|
/* Check that the optional email address is okay. */
|
2005-07-21 20:29:13 +02:00
|
|
|
|
for (seq=0; (s=get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (has_invalid_email_chars (s)
|
|
|
|
|
|| *s == '@'
|
|
|
|
|
|| s[strlen(s)-1] == '@'
|
|
|
|
|
|| s[strlen(s)-1] == '.'
|
|
|
|
|
|| strstr(s, ".."))
|
|
|
|
|
{
|
2005-07-21 20:29:13 +02:00
|
|
|
|
r = get_parameter (para, pNAMEEMAIL, seq);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: not a valid email address\n"), r?r->lnr:0);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
/* 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);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid serial number\n"), r?r->lnr:0);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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)
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: invalid issuer name label '%.*s'\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, (int)errlen, string+erroff);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: invalid issuer name '%s' at pos %d\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, string, (int)erroff);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid date given\n"), r?r->lnr:0);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid date given\n"), r?r->lnr:0);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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);
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: error getting signing key by keygrip '%s'"
|
2020-03-30 17:16:30 +02:00
|
|
|
|
": %s\n"), r?r->lnr:0, s, gpg_strerror (rc));
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid hash algorithm given\n"), r?r->lnr:0);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-06 21:43:18 +01:00
|
|
|
|
/* Check the optional AuthorityKeyId. */
|
|
|
|
|
string = get_parameter_value (para, pAUTHKEYID, 0);
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (string && strcmp (string, "none"))
|
2011-12-06 21:43:18 +01:00
|
|
|
|
{
|
|
|
|
|
for (s=string, i=0; hexdigitp (s); s++, i++)
|
|
|
|
|
;
|
|
|
|
|
if (*s || (i&1))
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pAUTHKEYID, 0);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid authority-key-id\n"), r?r->lnr:0);
|
2011-12-06 21:43:18 +01:00
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-06 19:57:27 +01:00
|
|
|
|
/* Check the optional SubjectKeyId. */
|
|
|
|
|
string = get_parameter_value (para, pSUBJKEYID, 0);
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (string && strcmp (string, "none"))
|
2011-12-06 19:57:27 +01:00
|
|
|
|
{
|
|
|
|
|
for (s=string, i=0; hexdigitp (s); s++, i++)
|
|
|
|
|
;
|
|
|
|
|
if (*s || (i&1))
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pSUBJKEYID, 0);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid subject-key-id\n"), r?r->lnr:0);
|
2011-12-06 19:57:27 +01:00
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check the optional extensions. */
|
|
|
|
|
for (seq=0; (string=get_parameter_value (para, pEXTENSION, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
int okay = 0;
|
|
|
|
|
|
|
|
|
|
s = strpbrk (string, " \t:");
|
|
|
|
|
if (s)
|
|
|
|
|
{
|
|
|
|
|
s++;
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
if (*s && strchr ("nNcC", *s))
|
|
|
|
|
{
|
|
|
|
|
s++;
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
if (*s == ':')
|
|
|
|
|
s++;
|
|
|
|
|
if (*s)
|
|
|
|
|
{
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
for (i=0; hexdigitp (s); s++, i++)
|
|
|
|
|
;
|
|
|
|
|
if (!((*s && *s != ':') || !i || (i&1)))
|
|
|
|
|
okay = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!okay)
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pEXTENSION, seq);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid extension syntax\n"), r? r->lnr:0);
|
2011-12-06 19:57:27 +01:00
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
/* Create or retrieve the public key. */
|
2006-10-11 19:52:15 +02:00
|
|
|
|
if (cardkeyid) /* Take the key from the current smart card. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2006-10-11 19:52:15 +02:00
|
|
|
|
rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pKEYTYPE, 0);
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: error reading key '%s' from card: %s\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, cardkeyid, gpg_strerror (rc));
|
2011-03-01 14:42:56 +01:00
|
|
|
|
xfree (sigkey);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/
|
|
|
|
|
{
|
|
|
|
|
rc = gpgsm_agent_readkey (ctrl, 0, s, &public);
|
2005-07-25 16:35:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pKEYTYPE, 0);
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("line %d: error getting key by keygrip '%s': %s\n"),
|
2005-07-25 16:35:04 +02:00
|
|
|
|
r->lnr, s, gpg_strerror (rc));
|
2011-03-01 14:42:56 +01:00
|
|
|
|
xfree (sigkey);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2005-07-25 16:35:04 +02:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-01 14:42:56 +01:00
|
|
|
|
else if (!outctrl->dryrun) /* Generate new key. */
|
2005-07-25 16:35:04 +02:00
|
|
|
|
{
|
2020-03-26 07:54:14 +01:00
|
|
|
|
if (algo == GCRY_PK_RSA)
|
|
|
|
|
{
|
|
|
|
|
sprintf (numbuf, "%u", nbits);
|
|
|
|
|
snprintf ((char*)keyparms, DIM (keyparms),
|
|
|
|
|
"(6:genkey(3:rsa(5:nbits%d:%s)))",
|
|
|
|
|
(int)strlen (numbuf), numbuf);
|
|
|
|
|
}
|
2020-03-27 11:41:03 +01:00
|
|
|
|
else if (algo == GCRY_PK_ECC)
|
2020-03-26 07:54:14 +01:00
|
|
|
|
{
|
|
|
|
|
const char *curve = get_parameter_value (para, pKEYCURVE, 0);
|
|
|
|
|
const char *flags;
|
|
|
|
|
|
2020-03-27 11:41:03 +01:00
|
|
|
|
if (!strcmp (curve, "Ed25519"))
|
|
|
|
|
flags = "(5:flags5:eddsa)";
|
2020-03-26 07:54:14 +01:00
|
|
|
|
else if (!strcmp (curve, "Curve25519"))
|
2020-03-27 11:41:03 +01:00
|
|
|
|
flags = "(5:flags9:djb-tweak)";
|
2020-03-26 07:54:14 +01:00
|
|
|
|
else
|
|
|
|
|
flags = "";
|
|
|
|
|
|
|
|
|
|
snprintf ((char*)keyparms, DIM (keyparms),
|
2020-03-27 11:41:03 +01:00
|
|
|
|
"(6:genkey(3:ecc(5:curve%zu:%s)%s))",
|
2020-03-26 07:54:14 +01:00
|
|
|
|
strlen (curve), curve, flags);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pKEYTYPE, 0);
|
2020-03-30 17:16:30 +02:00
|
|
|
|
log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0);
|
2020-03-26 07:54:14 +01:00
|
|
|
|
xfree (sigkey);
|
|
|
|
|
xfree (cardkeyid);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_PARAMETER);
|
|
|
|
|
}
|
2005-07-25 16:35:04 +02:00
|
|
|
|
rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
r = get_parameter (para, pKEYTYPE, 0);
|
2007-06-25 13:54:43 +02:00
|
|
|
|
log_error (_("line %d: key generation failed: %s <%s>\n"),
|
2020-03-30 17:16:30 +02:00
|
|
|
|
r?r->lnr:0, gpg_strerror (rc), gpg_strsource (rc));
|
2011-03-01 14:42:56 +01:00
|
|
|
|
xfree (sigkey);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2005-07-25 16:35:04 +02:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
|
|
|
|
|
if (!outctrl->dryrun)
|
|
|
|
|
{
|
2017-02-16 17:11:38 +01:00
|
|
|
|
gnupg_ksba_io_t b64writer = NULL;
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_writer_t writer;
|
|
|
|
|
int create_cert ;
|
|
|
|
|
|
|
|
|
|
create_cert = !!get_parameter_value (para, pSERIAL, 0);
|
|
|
|
|
|
|
|
|
|
ctrl->pem_name = create_cert? "CERTIFICATE" : "CERTIFICATE REQUEST";
|
2017-02-16 15:16:48 +01:00
|
|
|
|
|
|
|
|
|
rc = gnupg_ksba_create_writer
|
|
|
|
|
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
|
|
|
|
|
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
|
|
|
|
|
ctrl->pem_name, out_fp, &writer);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
2017-02-16 17:11:38 +01:00
|
|
|
|
rc = gnupg_ksba_finish_writer (b64writer);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-16 17:11:38 +01:00
|
|
|
|
gnupg_ksba_destroy_writer (b64writer);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (sigkey);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
xfree (public);
|
2006-10-11 19:52:15 +02:00
|
|
|
|
xfree (cardkeyid);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Parameters are checked, the key pair has been created. Now
|
|
|
|
|
generate the request and write it out */
|
|
|
|
|
static int
|
2011-02-04 12:57:53 +01:00
|
|
|
|
create_request (ctrl_t ctrl,
|
|
|
|
|
struct para_data_s *para,
|
2006-10-11 19:52:15 +02:00
|
|
|
|
const char *carddirect,
|
|
|
|
|
ksba_const_sexp_t public,
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_const_sexp_t sigkey,
|
|
|
|
|
ksba_writer_t writer)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2003-12-17 13:28:24 +01:00
|
|
|
|
ksba_certreq_t cr;
|
|
|
|
|
gpg_error_t err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
gcry_md_hd_t md;
|
2003-12-17 13:28:24 +01:00
|
|
|
|
ksba_stop_reason_t stopreason;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int rc = 0;
|
2011-03-01 14:42:56 +01:00
|
|
|
|
const char *s, *string;
|
2004-06-06 15:00:59 +02:00
|
|
|
|
unsigned int use;
|
2005-07-21 20:29:13 +02:00
|
|
|
|
int seq;
|
|
|
|
|
char *buf, *p;
|
|
|
|
|
size_t len;
|
|
|
|
|
char numbuf[30];
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_isotime_t atime;
|
|
|
|
|
int certmode = 0;
|
|
|
|
|
int mdalgo;
|
2020-05-18 19:32:30 +02:00
|
|
|
|
membuf_t tbsbuffer;
|
|
|
|
|
membuf_t *tbsmb = NULL;
|
2020-05-19 14:30:24 +02:00
|
|
|
|
size_t publiclen;
|
|
|
|
|
size_t sigkeylen;
|
|
|
|
|
int publicpkalgo; /* The gcrypt public key algo of the public key. */
|
|
|
|
|
int sigkeypkalgo; /* The gcrypt public key algo of the signing key. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2003-11-12 16:17:44 +01:00
|
|
|
|
err = ksba_certreq_new (&cr);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
|
|
|
|
|
sigkeylen = sigkey? gcry_sexp_canon_len (sigkey, 0, NULL, NULL) : 0;
|
|
|
|
|
|
|
|
|
|
publicpkalgo = get_pk_algo_from_canon_sexp (public, publiclen);
|
|
|
|
|
sigkeypkalgo = sigkey? get_pk_algo_from_canon_sexp (public, publiclen) : 0;
|
|
|
|
|
|
|
|
|
|
if (publicpkalgo == GCRY_PK_EDDSA)
|
2020-05-18 19:32:30 +02:00
|
|
|
|
{
|
|
|
|
|
mdalgo = GCRY_MD_SHA512;
|
|
|
|
|
md = NULL; /* We sign the data and not a hash. */
|
|
|
|
|
init_membuf (&tbsbuffer, 2048);
|
|
|
|
|
tbsmb = &tbsbuffer;
|
|
|
|
|
ksba_certreq_set_hash_function
|
|
|
|
|
(cr, (void (*)(void *, const void*,size_t))put_membuf, tbsmb);
|
|
|
|
|
}
|
2011-03-01 14:42:56 +01:00
|
|
|
|
else
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2020-05-18 19:32:30 +02:00
|
|
|
|
if ((string = get_parameter_value (para, pHASHALGO, 0)))
|
|
|
|
|
mdalgo = gcry_md_map_name (string);
|
|
|
|
|
else
|
|
|
|
|
mdalgo = GCRY_MD_SHA256;
|
|
|
|
|
rc = gcry_md_open (&md, mdalgo, 0);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error ("md_open failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (DBG_HASHING)
|
|
|
|
|
gcry_md_debug (md, "cr.cri");
|
|
|
|
|
ksba_certreq_set_hash_function (cr, HASH_FNC, md);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
ksba_certreq_set_writer (cr, writer);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-07-21 20:29:13 +02:00
|
|
|
|
err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0));
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the subject's name: %s\n",
|
2003-11-12 16:17:44 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-21 20:29:13 +02:00
|
|
|
|
for (seq=0; (s = get_parameter_value (para, pNAMEEMAIL, seq)); seq++)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
buf = xtrymalloc (strlen (s) + 3);
|
|
|
|
|
if (!buf)
|
|
|
|
|
{
|
2006-09-06 18:35:52 +02:00
|
|
|
|
rc = out_of_core ();
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
*buf = '<';
|
|
|
|
|
strcpy (buf+1, s);
|
|
|
|
|
strcat (buf+1, ">");
|
|
|
|
|
err = ksba_certreq_add_subject (cr, buf);
|
|
|
|
|
xfree (buf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the subject's alternate name: %s\n",
|
2003-11-12 16:17:44 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-21 20:29:13 +02:00
|
|
|
|
for (seq=0; (s = get_parameter_value (para, pNAMEDNS, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
len = strlen (s);
|
2020-07-08 14:40:34 +02:00
|
|
|
|
log_assert (len);
|
2005-07-21 20:29:13 +02:00
|
|
|
|
snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
|
|
|
|
|
buf = p = xtrymalloc (11 + strlen (numbuf) + len + 3);
|
|
|
|
|
if (!buf)
|
|
|
|
|
{
|
2006-09-06 18:35:52 +02:00
|
|
|
|
rc = out_of_core ();
|
2005-07-21 20:29:13 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
p = stpcpy (p, "(8:dns-name");
|
|
|
|
|
p = stpcpy (p, numbuf);
|
|
|
|
|
p = stpcpy (p, s);
|
|
|
|
|
strcpy (p, ")");
|
|
|
|
|
|
|
|
|
|
err = ksba_certreq_add_subject (cr, buf);
|
|
|
|
|
xfree (buf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the subject's alternate name: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (seq=0; (s = get_parameter_value (para, pNAMEURI, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
len = strlen (s);
|
2020-07-08 14:40:34 +02:00
|
|
|
|
log_assert (len);
|
2005-07-21 20:29:13 +02:00
|
|
|
|
snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len);
|
|
|
|
|
buf = p = xtrymalloc (6 + strlen (numbuf) + len + 3);
|
|
|
|
|
if (!buf)
|
|
|
|
|
{
|
2006-09-06 18:35:52 +02:00
|
|
|
|
rc = out_of_core ();
|
2005-07-21 20:29:13 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
p = stpcpy (p, "(3:uri");
|
|
|
|
|
p = stpcpy (p, numbuf);
|
|
|
|
|
p = stpcpy (p, s);
|
|
|
|
|
strcpy (p, ")");
|
|
|
|
|
|
|
|
|
|
err = ksba_certreq_add_subject (cr, buf);
|
|
|
|
|
xfree (buf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the subject's alternate name: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
err = ksba_certreq_set_public_key (cr, public);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2020-05-19 14:30:24 +02:00
|
|
|
|
log_error ("error setting the public key: %s\n", gpg_strerror (err));
|
2003-11-12 16:17:44 +01:00
|
|
|
|
rc = err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2004-06-06 15:00:59 +02:00
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
/* Set key usage flags. */
|
2004-06-06 15:00:59 +02:00
|
|
|
|
use = get_parameter_uint (para, pKEYUSAGE);
|
2015-10-28 18:57:53 +01:00
|
|
|
|
if (use)
|
2004-06-06 15:00:59 +02:00
|
|
|
|
{
|
2015-10-28 18:57:53 +01:00
|
|
|
|
unsigned int mask, pos;
|
|
|
|
|
unsigned char der[4];
|
|
|
|
|
|
|
|
|
|
der[0] = 0x03;
|
|
|
|
|
der[1] = 0x02;
|
|
|
|
|
der[2] = 0;
|
|
|
|
|
der[3] = 0;
|
|
|
|
|
if ((use & GCRY_PK_USAGE_SIGN))
|
|
|
|
|
{
|
|
|
|
|
/* For signing only we encode the bits:
|
|
|
|
|
KSBA_KEYUSAGE_DIGITAL_SIGNATURE
|
|
|
|
|
KSBA_KEYUSAGE_NON_REPUDIATION = 0b11 -> 0b11000000 */
|
|
|
|
|
der[3] |= 0xc0;
|
|
|
|
|
}
|
|
|
|
|
if ((use & GCRY_PK_USAGE_ENCR))
|
|
|
|
|
{
|
|
|
|
|
/* For encrypt only we encode the bits:
|
|
|
|
|
KSBA_KEYUSAGE_KEY_ENCIPHERMENT
|
|
|
|
|
KSBA_KEYUSAGE_DATA_ENCIPHERMENT = 0b1100 -> 0b00110000 */
|
|
|
|
|
der[3] |= 0x30;
|
|
|
|
|
}
|
|
|
|
|
if ((use & GCRY_PK_USAGE_CERT))
|
|
|
|
|
{
|
|
|
|
|
/* For certify only we encode the bits:
|
|
|
|
|
KSBA_KEYUSAGE_KEY_CERT_SIGN
|
|
|
|
|
KSBA_KEYUSAGE_CRL_SIGN = 0b1100000 -> 0b00000110 */
|
|
|
|
|
der[3] |= 0x06;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Count number of unused bits. */
|
|
|
|
|
for (mask=1, pos=0; pos < 8 * sizeof mask; pos++, mask <<= 1)
|
|
|
|
|
{
|
|
|
|
|
if ((der[3] & mask))
|
|
|
|
|
break;
|
|
|
|
|
der[2]++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1, der, 4);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the key usage: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2004-06-06 15:00:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
/* 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)
|
2011-12-06 16:45:46 +01:00
|
|
|
|
((unsigned char*)hexbuf)[len++] = xtoi_2 (p);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
/* 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)
|
2020-05-19 14:30:24 +02:00
|
|
|
|
{
|
|
|
|
|
sigkey = public;
|
|
|
|
|
sigkeylen = publiclen;
|
|
|
|
|
sigkeypkalgo = publicpkalgo;
|
|
|
|
|
}
|
2011-03-01 14:42:56 +01:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
/* Set the the digestinfo aka siginfo. */
|
2011-03-01 14:42:56 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *siginfo;
|
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
err = transform_sigval (sigkey, sigkeylen, mdalgo, &siginfo, NULL);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-12-06 19:57:27 +01:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
|
2011-12-06 21:43:18 +01:00
|
|
|
|
/* Insert the AuthorityKeyId. */
|
|
|
|
|
string = get_parameter_value (para, pAUTHKEYID, 0);
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (string && !strcmp (string, "none"))
|
|
|
|
|
; /* Do not issue an AKI. */
|
|
|
|
|
else if (string)
|
2011-12-06 21:43:18 +01:00
|
|
|
|
{
|
|
|
|
|
char *hexbuf;
|
|
|
|
|
|
|
|
|
|
/* Allocate a buffer for in-place conversion. We also add 4
|
|
|
|
|
extra bytes space for the tags and lengths fields. */
|
|
|
|
|
hexbuf = xtrymalloc (4 + strlen (string) + 1);
|
|
|
|
|
if (!hexbuf)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
strcpy (hexbuf+4, string);
|
|
|
|
|
for (p=hexbuf+4, len=0; p[0] && p[1]; p += 2)
|
|
|
|
|
((unsigned char*)hexbuf)[4+len++] = xtoi_2 (p);
|
|
|
|
|
if (len > 125)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
|
|
|
|
xfree (hexbuf);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
hexbuf[0] = 0x30; /* Tag for a Sequence. */
|
|
|
|
|
hexbuf[1] = len+2;
|
|
|
|
|
hexbuf[2] = 0x80; /* Context tag for an implicit Octet string. */
|
|
|
|
|
hexbuf[3] = len;
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier,
|
2020-05-19 14:30:24 +02:00
|
|
|
|
0, hexbuf, 4+len);
|
2011-12-06 21:43:18 +01:00
|
|
|
|
xfree (hexbuf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2020-05-19 14:30:24 +02:00
|
|
|
|
log_error ("error setting the AKI: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (publicpkalgo == GCRY_PK_EDDSA || publicpkalgo == GCRY_PK_ECC)
|
|
|
|
|
{
|
|
|
|
|
/* For EdDSA and ECC we add the public key as default identifier. */
|
|
|
|
|
const unsigned char *q;
|
|
|
|
|
size_t qlen, derlen;
|
|
|
|
|
unsigned char *der;
|
|
|
|
|
|
|
|
|
|
err = get_ecc_q_from_canon_sexp (public, publiclen, &q, &qlen);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error getting Q from public key: %s\n",
|
2011-12-06 21:43:18 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (publicpkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40)
|
|
|
|
|
{
|
|
|
|
|
/* Skip our optional native encoding octet. */
|
|
|
|
|
q++;
|
|
|
|
|
qlen--;
|
|
|
|
|
}
|
|
|
|
|
/* FIXME: For plain ECC we should better use a compressed
|
|
|
|
|
* point. That requires an updated Libgcrypt. Without that
|
|
|
|
|
* using nistp521 won't work due to the length check below. */
|
|
|
|
|
if (qlen > 125)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
derlen = 4 + qlen;
|
|
|
|
|
der = xtrymalloc (derlen);
|
|
|
|
|
if (!der)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
der[0] = 0x30; /* Sequence */
|
|
|
|
|
der[1] = qlen + 2;
|
2024-05-13 00:09:23 +02:00
|
|
|
|
der[2] = 0x80; /* Context tag for an implicit Octet String. */
|
2020-05-19 14:30:24 +02:00
|
|
|
|
der[3] = qlen;
|
|
|
|
|
memcpy (der+4, q, qlen);
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier,
|
|
|
|
|
0, der, derlen);
|
|
|
|
|
xfree (der);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the AKI: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-12-06 21:43:18 +01:00
|
|
|
|
}
|
2011-12-06 19:57:27 +01:00
|
|
|
|
|
|
|
|
|
/* Insert the SubjectKeyId. */
|
|
|
|
|
string = get_parameter_value (para, pSUBJKEYID, 0);
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (string && !strcmp (string, "none"))
|
|
|
|
|
; /* Do not issue an SKI. */
|
|
|
|
|
else if (string)
|
2011-12-06 19:57:27 +01:00
|
|
|
|
{
|
|
|
|
|
char *hexbuf;
|
|
|
|
|
|
|
|
|
|
/* Allocate a buffer for in-place conversion. We also add 2
|
|
|
|
|
extra bytes space for the tag and length field. */
|
|
|
|
|
hexbuf = xtrymalloc (2 + strlen (string) + 1);
|
|
|
|
|
if (!hexbuf)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
strcpy (hexbuf+2, string);
|
|
|
|
|
for (p=hexbuf+2, len=0; p[0] && p[1]; p += 2)
|
|
|
|
|
((unsigned char*)hexbuf)[2+len++] = xtoi_2 (p);
|
|
|
|
|
if (len > 127)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
|
|
|
|
xfree (hexbuf);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
hexbuf[0] = 0x04; /* Tag for an Octet string. */
|
|
|
|
|
hexbuf[1] = len;
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0,
|
|
|
|
|
hexbuf, 2+len);
|
|
|
|
|
xfree (hexbuf);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2020-05-19 14:30:24 +02:00
|
|
|
|
log_error ("error setting SKI: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (sigkeypkalgo == GCRY_PK_EDDSA || sigkeypkalgo == GCRY_PK_ECC)
|
|
|
|
|
{
|
|
|
|
|
/* For EdDSA and ECC we add the public key as default identifier. */
|
|
|
|
|
const unsigned char *q;
|
|
|
|
|
size_t qlen, derlen;
|
|
|
|
|
unsigned char *der;
|
|
|
|
|
|
2020-07-08 14:40:34 +02:00
|
|
|
|
/* FIXME: This assumes that the to-be-certified key uses the
|
|
|
|
|
* same algorithm as the certification key - this is not
|
|
|
|
|
* always the case; in fact it is common that they
|
|
|
|
|
* differ. */
|
2020-05-19 14:30:24 +02:00
|
|
|
|
err = get_ecc_q_from_canon_sexp (sigkey, sigkeylen, &q, &qlen);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error getting Q from signature key: %s\n",
|
2011-12-06 19:57:27 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (sigkeypkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40)
|
|
|
|
|
{
|
|
|
|
|
/* Skip our optional native encoding octet. */
|
|
|
|
|
q++;
|
|
|
|
|
qlen--;
|
|
|
|
|
}
|
|
|
|
|
if (qlen > 127)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
derlen = 2 + qlen;
|
|
|
|
|
der = xtrymalloc (derlen);
|
|
|
|
|
if (!der)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
der[0] = 0x04; /* Octet String */
|
|
|
|
|
der[1] = qlen;
|
|
|
|
|
memcpy (der+2, q, qlen);
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0,
|
|
|
|
|
der, derlen);
|
|
|
|
|
xfree (der);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting the SKI: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-12-06 19:57:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert additional extensions. */
|
|
|
|
|
for (seq=0; (string = get_parameter_value (para, pEXTENSION, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
char *hexbuf;
|
|
|
|
|
char *oidstr;
|
|
|
|
|
int crit = 0;
|
|
|
|
|
|
|
|
|
|
s = strpbrk (string, " \t:");
|
|
|
|
|
if (!s)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oidstr = xtrymalloc (s - string + 1);
|
|
|
|
|
if (!oidstr)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
memcpy (oidstr, string, (s-string));
|
|
|
|
|
oidstr[(s-string)] = 0;
|
|
|
|
|
|
|
|
|
|
s++;
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
if (!*s)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
xfree (oidstr);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strchr ("cC", *s))
|
|
|
|
|
crit = 1;
|
|
|
|
|
s++;
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
if (*s == ':')
|
|
|
|
|
s++;
|
|
|
|
|
while (spacep (s))
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
hexbuf = xtrystrdup (s);
|
|
|
|
|
if (!hexbuf)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
xfree (oidstr);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
for (p=hexbuf, len=0; p[0] && p[1]; p += 2)
|
|
|
|
|
((unsigned char*)hexbuf)[len++] = xtoi_2 (p);
|
|
|
|
|
err = ksba_certreq_add_extension (cr, oidstr, crit,
|
|
|
|
|
hexbuf, len);
|
|
|
|
|
xfree (oidstr);
|
|
|
|
|
xfree (hexbuf);
|
|
|
|
|
}
|
2011-03-01 14:42:56 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
2020-06-03 11:21:10 +02:00
|
|
|
|
{
|
|
|
|
|
sigkey = public;
|
|
|
|
|
sigkeylen = publiclen;
|
|
|
|
|
sigkeypkalgo = publicpkalgo;
|
|
|
|
|
}
|
2011-03-01 14:42:56 +01:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
err = ksba_certreq_build (cr, &stopreason);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2003-11-12 16:17:44 +01:00
|
|
|
|
log_error ("ksba_certreq_build failed: %s\n", gpg_strerror (err));
|
|
|
|
|
rc = err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (stopreason == KSBA_SR_NEED_SIG)
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t s_pkey;
|
2005-06-16 10:12:03 +02:00
|
|
|
|
unsigned char grip[20];
|
|
|
|
|
char hexgrip[41];
|
2011-03-01 14:42:56 +01:00
|
|
|
|
unsigned char *sigval, *newsigval;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
size_t siglen;
|
2020-05-18 19:32:30 +02:00
|
|
|
|
void *tbsdata;
|
|
|
|
|
size_t tbsdatalen;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, sigkeylen);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if ( !gcry_pk_get_keygrip (s_pkey, grip) )
|
|
|
|
|
{
|
|
|
|
|
rc = gpg_error (GPG_ERR_GENERAL);
|
|
|
|
|
log_error ("can't figure out the keygrip\n");
|
|
|
|
|
gcry_sexp_release (s_pkey);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
gcry_sexp_release (s_pkey);
|
2007-08-22 22:36:33 +02:00
|
|
|
|
bin2hex (grip, 20, hexgrip);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
if (!opt.quiet)
|
|
|
|
|
log_info ("about to sign the %s for key: &%s\n",
|
|
|
|
|
certmode? "certificate":"CSR", hexgrip);
|
2008-12-05 17:31:39 +01:00
|
|
|
|
|
2019-02-21 17:32:39 +01:00
|
|
|
|
if (carddirect && !certmode)
|
2020-05-18 19:32:30 +02:00
|
|
|
|
{
|
|
|
|
|
if (tbsmb)
|
|
|
|
|
{
|
|
|
|
|
tbsdata = get_membuf (tbsmb, &tbsdatalen);
|
|
|
|
|
tbsmb = NULL;
|
|
|
|
|
if (!tbsdata)
|
|
|
|
|
rc = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
|
|
|
|
|
tbsdata, tbsdatalen, 0,
|
|
|
|
|
&sigval, &siglen);
|
|
|
|
|
xfree (tbsdata);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
|
|
|
|
|
gcry_md_read (md, mdalgo),
|
|
|
|
|
gcry_md_get_algo_dlen (mdalgo),
|
|
|
|
|
mdalgo,
|
|
|
|
|
&sigval, &siglen);
|
|
|
|
|
}
|
2006-10-11 19:52:15 +02:00
|
|
|
|
else
|
2008-12-05 17:31:39 +01:00
|
|
|
|
{
|
|
|
|
|
char *orig_codeset;
|
|
|
|
|
char *desc;
|
|
|
|
|
|
|
|
|
|
orig_codeset = i18n_switchto_utf8 ();
|
2011-02-04 12:57:53 +01:00
|
|
|
|
desc = percent_plus_escape
|
2008-12-05 17:31:39 +01:00
|
|
|
|
(_("To complete this certificate request please enter"
|
|
|
|
|
" the passphrase for the key you just created once"
|
|
|
|
|
" more.\n"));
|
|
|
|
|
i18n_switchback (orig_codeset);
|
2020-05-18 19:32:30 +02:00
|
|
|
|
if (tbsmb)
|
|
|
|
|
{
|
|
|
|
|
tbsdata = get_membuf (tbsmb, &tbsdatalen);
|
|
|
|
|
tbsmb = NULL;
|
|
|
|
|
if (!tbsdata)
|
|
|
|
|
rc = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
rc = gpgsm_agent_pksign (ctrl, hexgrip, desc,
|
|
|
|
|
tbsdata, tbsdatalen, 0,
|
|
|
|
|
|
|
|
|
|
&sigval, &siglen);
|
|
|
|
|
xfree (tbsdata);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
rc = gpgsm_agent_pksign (ctrl, hexgrip, desc,
|
|
|
|
|
gcry_md_read(md, mdalgo),
|
|
|
|
|
gcry_md_get_algo_dlen (mdalgo),
|
|
|
|
|
mdalgo,
|
|
|
|
|
&sigval, &siglen);
|
2008-12-05 17:31:39 +01:00
|
|
|
|
xfree (desc);
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
log_error ("signing failed: %s\n", gpg_strerror (rc));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2020-05-19 14:30:24 +02:00
|
|
|
|
err = transform_sigval (sigval, siglen, mdalgo, &newsigval, NULL);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
xfree (sigval);
|
2011-03-01 14:42:56 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
err = ksba_certreq_set_sig_val (cr, newsigval);
|
|
|
|
|
xfree (newsigval);
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("failed to store the sig_val: %s\n",
|
2003-11-12 16:17:44 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
rc = err;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
while (stopreason != KSBA_SR_READY);
|
2008-12-05 17:31:39 +01:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
leave:
|
2020-05-18 19:32:30 +02:00
|
|
|
|
if (tbsmb)
|
|
|
|
|
xfree (get_membuf (tbsmb, NULL));
|
2003-08-05 19:11:04 +02:00
|
|
|
|
gcry_md_close (md);
|
|
|
|
|
ksba_certreq_release (cr);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return rc;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-08-22 12:55:07 +02:00
|
|
|
|
/* Create a new key by reading the parameters from IN_FP. Multiple
|
|
|
|
|
keys may be created */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int
|
2010-03-08 19:19:21 +01:00
|
|
|
|
gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
|
2011-03-01 14:42:56 +01:00
|
|
|
|
rc = read_parameters (ctrl, in_stream, out_stream);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2007-06-25 13:54:43 +02:00
|
|
|
|
log_error ("error creating certificate request: %s <%s>\n",
|
|
|
|
|
gpg_strerror (rc), gpg_strsource (rc));
|
2003-08-05 19:11:04 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
return rc;
|
|
|
|
|
}
|