mirror of
git://git.gnupg.org/gnupg.git
synced 2024-09-07 12:56:21 +02:00
475 lines
13 KiB
C
475 lines
13 KiB
C
/* certreqgen-ui.c - Simple user interface for certreqgen.c
|
|
* Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc.
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (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
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include "gpgsm.h"
|
|
#include <gcrypt.h>
|
|
|
|
#include "../common/i18n.h"
|
|
#include "../common/ttyio.h"
|
|
#include "../common/membuf.h"
|
|
|
|
|
|
/* Prompt for lines and append them to MB. */
|
|
static void
|
|
ask_mb_lines (membuf_t *mb, const char *prefix)
|
|
{
|
|
char *answer = NULL;
|
|
|
|
do
|
|
{
|
|
xfree (answer);
|
|
answer = tty_get ("> ");
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
if (*answer)
|
|
{
|
|
put_membuf_str (mb, prefix);
|
|
put_membuf_str (mb, answer);
|
|
put_membuf (mb, "\n", 1);
|
|
}
|
|
}
|
|
while (*answer);
|
|
xfree (answer);
|
|
}
|
|
|
|
/* Helper to store stuff in a membuf. */
|
|
void
|
|
store_key_value_lf (membuf_t *mb, const char *key, const char *value)
|
|
{
|
|
put_membuf_str (mb, key);
|
|
put_membuf_str (mb, value);
|
|
put_membuf (mb, "\n", 1);
|
|
}
|
|
|
|
/* Helper tp store a membuf create by mb_ask_lines into MB. Returns
|
|
-1 on error. */
|
|
int
|
|
store_mb_lines (membuf_t *mb, membuf_t *lines)
|
|
{
|
|
char *p;
|
|
|
|
if (get_membuf_len (lines))
|
|
{
|
|
put_membuf (lines, "", 1);
|
|
p = get_membuf (lines, NULL);
|
|
if (!p)
|
|
return -1;
|
|
put_membuf_str (mb, p);
|
|
xfree (p);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Check whether we have a key for the key with HEXGRIP. Returns NULL
|
|
if not or a string describing the type of the key (RSA, ELG, DSA,
|
|
etc..). */
|
|
static const char *
|
|
check_keygrip (ctrl_t ctrl, const char *hexgrip)
|
|
{
|
|
gpg_error_t err;
|
|
ksba_sexp_t public;
|
|
size_t publiclen;
|
|
int algo;
|
|
|
|
if (hexgrip[0] == '&')
|
|
hexgrip++;
|
|
|
|
err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public);
|
|
if (err)
|
|
return NULL;
|
|
publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
|
|
|
|
algo = get_pk_algo_from_canon_sexp (public, publiclen);
|
|
xfree (public);
|
|
|
|
switch (algo)
|
|
{
|
|
case GCRY_PK_RSA: return "RSA";
|
|
case GCRY_PK_DSA: return "DSA";
|
|
case GCRY_PK_ELG: return "ELG";
|
|
case GCRY_PK_ECC: return "ECC";
|
|
case GCRY_PK_ECDSA: return "ECDSA";
|
|
case GCRY_PK_EDDSA: return "EdDSA";
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* This function is used to create a certificate request from the
|
|
command line. In the past the similar gpgsm-gencert.sh script has
|
|
been used for it; however that scripts requires a full Unix shell
|
|
and thus is not suitable for the Windows port. So here is the
|
|
re-implementation. */
|
|
void
|
|
gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream)
|
|
{
|
|
gpg_error_t err;
|
|
char *answer;
|
|
int selection;
|
|
estream_t fp = NULL;
|
|
int method;
|
|
char *keytype_buffer = NULL;
|
|
const char *keytype;
|
|
char *keygrip = NULL;
|
|
unsigned int nbits;
|
|
int minbits = 1024;
|
|
int maxbits = 4096;
|
|
int defbits = 3072;
|
|
const char *keyusage;
|
|
char *subject_name;
|
|
membuf_t mb_email, mb_dns, mb_uri, mb_result;
|
|
char *result = NULL;
|
|
const char *s, *s2;
|
|
int selfsigned;
|
|
|
|
answer = NULL;
|
|
init_membuf (&mb_email, 100);
|
|
init_membuf (&mb_dns, 100);
|
|
init_membuf (&mb_uri, 100);
|
|
init_membuf (&mb_result, 512);
|
|
|
|
again:
|
|
/* Get the type of the key. */
|
|
tty_printf (_("Please select what kind of key you want:\n"));
|
|
tty_printf (_(" (%d) RSA\n"), 1 );
|
|
tty_printf (_(" (%d) Existing key\n"), 2 );
|
|
tty_printf (_(" (%d) Existing key from card\n"), 3 );
|
|
|
|
do
|
|
{
|
|
xfree (answer);
|
|
answer = tty_get (_("Your selection? "));
|
|
tty_kill_prompt ();
|
|
selection = *answer? atoi (answer): 1;
|
|
}
|
|
while (!(selection >= 1 && selection <= 3));
|
|
method = selection;
|
|
|
|
/* Get size of the key. */
|
|
if (method == 1)
|
|
{
|
|
keytype = "RSA";
|
|
for (;;)
|
|
{
|
|
xfree (answer);
|
|
answer = tty_getf (_("What keysize do you want? (%u) "), defbits);
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
nbits = *answer? atoi (answer): defbits;
|
|
if (nbits < minbits || nbits > maxbits)
|
|
tty_printf(_("%s keysizes must be in the range %u-%u\n"),
|
|
"RSA", minbits, maxbits);
|
|
else
|
|
break; /* Okay. */
|
|
}
|
|
tty_printf (_("Requested keysize is %u bits\n"), nbits);
|
|
/* We round it up so that it better matches the word size. */
|
|
if (( nbits % 64))
|
|
{
|
|
nbits = ((nbits + 63) / 64) * 64;
|
|
tty_printf (_("rounded up to %u bits\n"), nbits);
|
|
}
|
|
}
|
|
else if (method == 2)
|
|
{
|
|
for (;;)
|
|
{
|
|
xfree (answer);
|
|
answer = tty_get (_("Enter the keygrip: "));
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
|
|
if (!*answer)
|
|
goto again;
|
|
else if (strlen (answer) != 40 &&
|
|
!(answer[0] == '&' && strlen (answer+1) == 40))
|
|
tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n"));
|
|
else if (!(keytype = check_keygrip (ctrl, answer)) )
|
|
tty_printf (_("No key with this keygrip\n"));
|
|
else
|
|
break; /* Okay. */
|
|
}
|
|
xfree (keygrip);
|
|
keygrip = answer;
|
|
answer = NULL;
|
|
nbits = 1024; /* A dummy value is sufficient. */
|
|
}
|
|
else /* method == 3 */
|
|
{
|
|
char *serialno;
|
|
strlist_t keypairlist, sl;
|
|
int count;
|
|
|
|
err = gpgsm_agent_scd_serialno (ctrl, &serialno);
|
|
if (err)
|
|
{
|
|
tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
|
|
goto again;
|
|
}
|
|
tty_printf (_("Serial number of the card: %s\n"), serialno);
|
|
xfree (serialno);
|
|
|
|
err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist);
|
|
if (err)
|
|
{
|
|
tty_printf (_("error reading the card: %s\n"), gpg_strerror (err));
|
|
goto again;
|
|
}
|
|
|
|
do
|
|
{
|
|
tty_printf (_("Available keys:\n"));
|
|
for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
|
|
{
|
|
ksba_sexp_t pkey;
|
|
gcry_sexp_t s_pkey;
|
|
char *algostr = NULL;
|
|
const char *keyref;
|
|
int any = 0;
|
|
|
|
keyref = strchr (sl->d, ' ');
|
|
if (keyref)
|
|
{
|
|
keyref++;
|
|
if (!gpgsm_agent_readkey (ctrl, 1, keyref, &pkey))
|
|
{
|
|
if (!gcry_sexp_new (&s_pkey, pkey, 0, 0))
|
|
algostr = pubkey_algo_string (s_pkey, NULL);
|
|
gcry_sexp_release (s_pkey);
|
|
}
|
|
xfree (pkey);
|
|
}
|
|
tty_printf (" (%d) %s %s", count, sl->d, algostr);
|
|
if ((sl->flags & GCRY_PK_USAGE_CERT))
|
|
{
|
|
tty_printf ("%scert", any?",":" (");
|
|
any = 1;
|
|
}
|
|
if ((sl->flags & GCRY_PK_USAGE_SIGN))
|
|
{
|
|
tty_printf ("%ssign", any?",":" (");
|
|
any = 1;
|
|
}
|
|
if ((sl->flags & GCRY_PK_USAGE_AUTH))
|
|
{
|
|
tty_printf ("%sauth", any?",":" (");
|
|
any = 1;
|
|
}
|
|
if ((sl->flags & GCRY_PK_USAGE_ENCR))
|
|
{
|
|
tty_printf ("%sencr", any?",":" (");
|
|
any = 1;
|
|
}
|
|
tty_printf ("%s\n", any?")":"");
|
|
xfree (algostr);
|
|
}
|
|
xfree (answer);
|
|
answer = tty_get (_("Your selection? "));
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
selection = atoi (answer);
|
|
}
|
|
while (!(selection > 0 && selection < count));
|
|
|
|
for (count=1,sl=keypairlist; sl; sl = sl->next, count++)
|
|
if (count == selection)
|
|
break;
|
|
|
|
s = sl->d;
|
|
while (*s && !spacep (s))
|
|
s++;
|
|
while (spacep (s))
|
|
s++;
|
|
|
|
xfree (keygrip);
|
|
keygrip = NULL;
|
|
xfree (keytype_buffer);
|
|
keytype_buffer = xasprintf ("card:%s", s);
|
|
free_strlist (keypairlist);
|
|
keytype = keytype_buffer;
|
|
nbits = 1024; /* A dummy value is sufficient. */
|
|
}
|
|
|
|
/* Ask for the key usage. */
|
|
tty_printf (_("Possible actions for a %s key:\n"), "RSA");
|
|
tty_printf (_(" (%d) sign, encrypt\n"), 1 );
|
|
tty_printf (_(" (%d) sign\n"), 2 );
|
|
tty_printf (_(" (%d) encrypt\n"), 3 );
|
|
do
|
|
{
|
|
xfree (answer);
|
|
answer = tty_get (_("Your selection? "));
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
selection = *answer? atoi (answer): 1;
|
|
switch (selection)
|
|
{
|
|
case 1: keyusage = "sign, encrypt"; break;
|
|
case 2: keyusage = "sign"; break;
|
|
case 3: keyusage = "encrypt"; break;
|
|
default: keyusage = NULL; break;
|
|
}
|
|
}
|
|
while (!keyusage);
|
|
|
|
/* Get the subject name. */
|
|
do
|
|
{
|
|
size_t erroff, errlen;
|
|
|
|
xfree (answer);
|
|
answer = tty_get (_("Enter the X.509 subject name: "));
|
|
tty_kill_prompt ();
|
|
trim_spaces (answer);
|
|
if (!*answer)
|
|
tty_printf (_("No subject name given\n"));
|
|
else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) )
|
|
{
|
|
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
|
|
tty_printf (_("Invalid subject name label '%.*s'\n"),
|
|
(int)errlen, answer+erroff);
|
|
else
|
|
{
|
|
/* TRANSLATORS: The 22 in the second string is the
|
|
length of the first string up to the "%s". Please
|
|
adjust it do the length of your translation. The
|
|
second string is merely passed to atoi so you can
|
|
drop everything after the number. */
|
|
tty_printf (_("Invalid subject name '%s'\n"), answer);
|
|
tty_printf ("%*s^\n",
|
|
atoi (_("22 translator: see "
|
|
"certreg-ui.c:gpgsm_gencertreq_tty"))
|
|
+ (int)erroff, "");
|
|
}
|
|
*answer = 0;
|
|
}
|
|
}
|
|
while (!*answer);
|
|
subject_name = answer;
|
|
answer = NULL;
|
|
|
|
/* Get the email addresses. */
|
|
tty_printf (_("Enter email addresses"));
|
|
tty_printf (_(" (end with an empty line):\n"));
|
|
ask_mb_lines (&mb_email, "Name-Email: ");
|
|
|
|
/* DNS names. */
|
|
tty_printf (_("Enter DNS names"));
|
|
tty_printf (_(" (optional; end with an empty line):\n"));
|
|
ask_mb_lines (&mb_dns, "Name-DNS: ");
|
|
|
|
/* URIs. */
|
|
tty_printf (_("Enter URIs"));
|
|
tty_printf (_(" (optional; end with an empty line):\n"));
|
|
ask_mb_lines (&mb_uri, "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);
|
|
{
|
|
char numbuf[30];
|
|
snprintf (numbuf, sizeof numbuf, "%u", nbits);
|
|
store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
|
|
}
|
|
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))
|
|
goto mem_error;
|
|
if (store_mb_lines (&mb_result, &mb_uri))
|
|
goto mem_error;
|
|
put_membuf (&mb_result, "", 1);
|
|
result = get_membuf (&mb_result, NULL);
|
|
if (!result)
|
|
goto mem_error;
|
|
|
|
tty_printf (_("These parameters are used:\n"));
|
|
for (s=result; (s2 = strchr (s, '\n')); s = s2+1)
|
|
tty_printf (" %.*s\n", (int)(s2-s), s);
|
|
tty_printf ("\n");
|
|
|
|
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+");
|
|
if (!fp)
|
|
{
|
|
log_error (_("error creating temporary file: %s\n"), strerror (errno));
|
|
goto leave;
|
|
}
|
|
es_fputs (result, fp);
|
|
es_rewind (fp);
|
|
if (selfsigned)
|
|
tty_printf ("%s", _("Now creating self-signed certificate. "));
|
|
else
|
|
tty_printf ("%s", _("Now creating certificate request. "));
|
|
tty_printf ("%s", _("This may take a while ...\n"));
|
|
|
|
{
|
|
int save_pem = ctrl->create_pem;
|
|
ctrl->create_pem = 1; /* Force creation of PEM. */
|
|
err = gpgsm_genkey (ctrl, fp, output_stream);
|
|
ctrl->create_pem = save_pem;
|
|
}
|
|
if (!err)
|
|
{
|
|
if (selfsigned)
|
|
tty_printf (_("Ready.\n"));
|
|
else
|
|
tty_printf
|
|
(_("Ready. You should now send this request to your CA.\n"));
|
|
}
|
|
|
|
|
|
goto leave;
|
|
mem_error:
|
|
log_error (_("resource problem: out of core\n"));
|
|
leave:
|
|
es_fclose (fp);
|
|
xfree (answer);
|
|
xfree (subject_name);
|
|
xfree (keytype_buffer);
|
|
xfree (keygrip);
|
|
xfree (get_membuf (&mb_email, NULL));
|
|
xfree (get_membuf (&mb_dns, NULL));
|
|
xfree (get_membuf (&mb_uri, NULL));
|
|
xfree (get_membuf (&mb_result, NULL));
|
|
xfree (result);
|
|
}
|