From 28c157b55cf6db6b6988def5c9512e388c512b10 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 1 Mar 2011 14:42:56 +0100 Subject: [PATCH] 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. --- NEWS | 4 + doc/DETAILS | 5 +- sm/ChangeLog | 23 ++- sm/certreqgen-ui.c | 34 +++- sm/certreqgen.c | 493 ++++++++++++++++++++++++++++++++++++++------- sm/gpgsm.h | 4 + sm/keylist.c | 1 + sm/misc.c | 132 +++++++++++- 8 files changed, 615 insertions(+), 81 deletions(-) diff --git a/NEWS b/NEWS index f58871a67..1255b3486 100644 --- a/NEWS +++ b/NEWS @@ -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) ----------------------------------------------------- diff --git a/doc/DETAILS b/doc/DETAILS index 8998d875e..587092757 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -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 diff --git a/sm/ChangeLog b/sm/ChangeLog index 22db032af..44e4eb3a1 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,24 @@ +2011-03-01 Werner Koch + + * 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 + + * certreqgen.c (create_request): Add arg SIGKEY. + 2010-11-25 Werner Koch * base64.c (gpgsm_create_writer): Remove arg FP which is not used @@ -2876,7 +2897,7 @@ h2007-11-22 Werner Koch 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 diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c index 526a182bb..b5b421965 100644 --- a/sm/certreqgen-ui.c +++ b/sm/certreqgen-ui.c @@ -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; diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 4218908d2..7d0bfbd6f 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -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 + [Not yet implemented!] Do not write the certificate to the keyDB but to . 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: 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 of the key in bits. Default is 2048. - Key-Grip: hexstring + + Key-Grip: This is optional and used to generate a request for an already existing key. Key-Length will be ignored when given, + Key-Usage: 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: This is the DN name of the subject in rfc2253 format. + Name-Email: The is an email address for the altSubjectName + Name-DNS: The is an DNS name for the altSubjectName + Name-URI: 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: + 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: + 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: + 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: + 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: + 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: + 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 <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; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 54d4aff93..31cd95150 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -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); diff --git a/sm/keylist.c b/sm/keylist.c index 1d6ce6e89..fc903bab8 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -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" }, diff --git a/sm/misc.c b/sm/misc.c index 2d6cdd6b5..4c6293fc2 100644 --- a/sm/misc.c +++ b/sm/misc.c @@ -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; +}