gpgsm: New commands --export-secret-key-{p8,raw}

* sm/gpgsm.c: Add new commands.
* sm/minip12.c (build_key_sequence): Add arg mode.
(p12_raw_build): New.
* sm/export.c (export_p12): Add arg rawmode.  Call p12_raw_build.
(gpgsm_p12_export): Ditto.
(print_short_info): Print the keygrip.
This commit is contained in:
Werner Koch 2014-06-03 18:57:33 +02:00
parent 50cd3d40ae
commit 0beec2f0f2
7 changed files with 184 additions and 63 deletions

3
NEWS
View File

@ -42,6 +42,9 @@ Noteworthy changes in version 2.1.0-betaN (unreleased)
* Protect against rogue keyservers sending secret keys. * Protect against rogue keyservers sending secret keys.
* GPGSM can now be used to export a secret RSA key in PKCS#1 or
PKCS#8 format.
Noteworthy changes in version 2.1.0beta3 (2011-12-20) Noteworthy changes in version 2.1.0beta3 (2011-12-20)
----------------------------------------------------- -----------------------------------------------------

View File

@ -259,13 +259,26 @@ certificate are only exported if all @var{pattern} are given as
fingerprints or keygrips. fingerprints or keygrips.
@item --export-secret-key-p12 @var{key-id} @item --export-secret-key-p12 @var{key-id}
@opindex export @opindex export-secret-key-p12
Export the private key and the certificate identified by @var{key-id} in Export the private key and the certificate identified by @var{key-id} in
a PKCS#12 format. When using along with the @code{--armor} option a few a PKCS#12 format. When used with the @code{--armor} option a few
informational lines are prepended to the output. Note, that the PKCS#12 informational lines are prepended to the output. Note, that the PKCS#12
format is not very secure and this command is only provided if there is format is not very secure and this command is only provided if there is
no other way to exchange the private key. (@pxref{option --p12-charset}) no other way to exchange the private key. (@pxref{option --p12-charset})
@ifset gpgtwoone
@item --export-secret-key-p8 @var{key-id}
@itemx --export-secret-key-raw @var{key-id}
@opindex export-secret-key-p8
@opindex export-secret-key-raw
Export the private key of the certificate identified by @var{key-id}
with any encryption stripped. The @code{...-raw} command exports in
PKCS#1 format; the @code{...-p8} command exports in PKCS#8 format.
When used with the @code{--armor} option a few informational lines are
prepended to the output. These commands are useful to prepare a key
for use on a TLS server.
@end ifset
@item --import [@var{files}] @item --import [@var{files}]
@opindex import @opindex import
Import the certificates from the PEM or binary encoded files as well as Import the certificates from the PEM or binary encoded files as well as

View File

@ -60,6 +60,7 @@ static void print_short_info (ksba_cert_t cert, estream_t stream);
static gpg_error_t export_p12 (ctrl_t ctrl, static gpg_error_t export_p12 (ctrl_t ctrl,
const unsigned char *certimg, size_t certimglen, const unsigned char *certimg, size_t certimglen,
const char *prompt, const char *keygrip, const char *prompt, const char *keygrip,
int rawmode,
void **r_result, size_t *r_resultlen); void **r_result, size_t *r_resultlen);
@ -315,9 +316,14 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream)
} }
/* Export a certificate and its private key. */ /* Export a certificate and its private key. RAWMODE controls the
actual output:
0 - Private key and certifciate in PKCS#12 format
1 - Only unencrypted private key in PKCS#8 format
2 - Only unencrypted private key in PKCS#1 format
*/
void void
gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream) gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
KEYDB_HANDLE hd; KEYDB_HANDLE hd;
@ -416,13 +422,18 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream)
es_putc ('\n', stream); es_putc ('\n', stream);
} }
if (opt.p12_charset && ctrl->create_pem) if (opt.p12_charset && ctrl->create_pem && !rawmode)
{ {
es_fprintf (stream, "The passphrase is %s encoded.\n\n", es_fprintf (stream, "The passphrase is %s encoded.\n\n",
opt.p12_charset); opt.p12_charset);
} }
ctrl->pem_name = "PKCS12"; if (rawmode == 0)
ctrl->pem_name = "PKCS12";
else if (rawmode == 1)
ctrl->pem_name = "PRIVATE KEY";
else
ctrl->pem_name = "RSA PRIVATE KEY";
err = gpgsm_create_writer (&b64writer, ctrl, stream, &writer); err = gpgsm_create_writer (&b64writer, ctrl, stream, &writer);
if (err) if (err)
{ {
@ -431,7 +442,8 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream)
} }
prompt = gpgsm_format_keydesc (cert); prompt = gpgsm_format_keydesc (cert);
err = export_p12 (ctrl, image, imagelen, prompt, keygrip, &data, &datalen); err = export_p12 (ctrl, image, imagelen, prompt, keygrip, rawmode,
&data, &datalen);
xfree (prompt); xfree (prompt);
if (err) if (err)
goto leave; goto leave;
@ -513,12 +525,19 @@ print_short_info (ksba_cert_t cert, estream_t stream)
xfree (p); xfree (p);
} }
es_putc ('\n', stream); es_putc ('\n', stream);
p = gpgsm_get_keygrip_hexstring (cert);
if (p)
{
es_fprintf (stream, "Keygrip ..: %s\n", p);
xfree (p);
}
} }
/* Parse a private key S-expression and retutn a malloced array with /* Parse a private key S-expression and return a malloced array with
the RSA paramaters in pkcs#12 order. The caller needs to the RSA parameters in pkcs#12 order. The caller needs to
deep-release this array. */ deep-release this array. */
static gcry_mpi_t * static gcry_mpi_t *
sexp_to_kparms (gcry_sexp_t sexp) sexp_to_kparms (gcry_sexp_t sexp)
@ -587,7 +606,7 @@ sexp_to_kparms (gcry_sexp_t sexp)
static gpg_error_t static gpg_error_t
export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
const char *prompt, const char *keygrip, const char *prompt, const char *keygrip, int rawmode,
void **r_result, size_t *r_resultlen) void **r_result, size_t *r_resultlen)
{ {
gpg_error_t err = 0; gpg_error_t err = 0;
@ -671,20 +690,30 @@ export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
goto leave; goto leave;
} }
err = gpgsm_agent_ask_passphrase if (rawmode)
(ctrl, {
i18n_utf8 ("Please enter the passphrase to protect the " /* Export in raw mode, that is only the pkcs#1/#8 private key. */
"new PKCS#12 object."), result = p12_raw_build (kparms, rawmode, &resultlen);
1, &passphrase); if (!result)
if (err) err = gpg_error (GPG_ERR_GENERAL);
goto leave; }
else
{
err = gpgsm_agent_ask_passphrase
(ctrl,
i18n_utf8 ("Please enter the passphrase to protect the "
"new PKCS#12 object."),
1, &passphrase);
if (err)
goto leave;
result = p12_build (kparms, certimg, certimglen, passphrase, result = p12_build (kparms, certimg, certimglen, passphrase,
opt.p12_charset, &resultlen); opt.p12_charset, &resultlen);
xfree (passphrase); xfree (passphrase);
passphrase = NULL; passphrase = NULL;
if (!result) if (!result)
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
}
leave: leave:
xfree (key); xfree (key);

View File

@ -74,6 +74,8 @@ enum cmd_and_opt_values {
aRecvKeys, aRecvKeys,
aExport, aExport,
aExportSecretKeyP12, aExportSecretKeyP12,
aExportSecretKeyP8,
aExportSecretKeyRaw,
aServer, aServer,
aLearnCard, aLearnCard,
aCallDirmngr, aCallDirmngr,
@ -208,7 +210,13 @@ static ARGPARSE_OPTS opts[] = {
/*ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a key server")),*/ /*ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a key server")),*/
ARGPARSE_c (aImport, "import", N_("import certificates")), ARGPARSE_c (aImport, "import", N_("import certificates")),
ARGPARSE_c (aExport, "export", N_("export certificates")), ARGPARSE_c (aExport, "export", N_("export certificates")),
/* We use -raw and not -p1 for pkcs#1 secret key export so that it
won't accidently be used in case -p12 was intended. */
ARGPARSE_c (aExportSecretKeyP12, "export-secret-key-p12", "@"), ARGPARSE_c (aExportSecretKeyP12, "export-secret-key-p12", "@"),
ARGPARSE_c (aExportSecretKeyP8, "export-secret-key-p8", "@"),
ARGPARSE_c (aExportSecretKeyRaw, "export-secret-key-raw", "@"),
ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")), ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")),
ARGPARSE_c (aServer, "server", N_("run in server mode")), ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aCallDirmngr, "call-dirmngr", ARGPARSE_c (aCallDirmngr, "call-dirmngr",
@ -1084,6 +1092,8 @@ main ( int argc, char **argv)
case aRecvKeys: case aRecvKeys:
case aExport: case aExport:
case aExportSecretKeyP12: case aExportSecretKeyP12:
case aExportSecretKeyP8:
case aExportSecretKeyRaw:
case aDumpKeys: case aDumpKeys:
case aDumpChain: case aDumpChain:
case aDumpExternalKeys: case aDumpExternalKeys:
@ -1888,7 +1898,7 @@ main ( int argc, char **argv)
estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
if (argc == 1) if (argc == 1)
gpgsm_p12_export (&ctrl, *argv, fp); gpgsm_p12_export (&ctrl, *argv, fp, 0);
else else
wrong_args ("--export-secret-key-p12 KEY-ID"); wrong_args ("--export-secret-key-p12 KEY-ID");
if (fp != es_stdout) if (fp != es_stdout)
@ -1896,6 +1906,32 @@ main ( int argc, char **argv)
} }
break; break;
case aExportSecretKeyP8:
{
estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
if (argc == 1)
gpgsm_p12_export (&ctrl, *argv, fp, 1);
else
wrong_args ("--export-secret-key-p8 KEY-ID");
if (fp != es_stdout)
es_fclose (fp);
}
break;
case aExportSecretKeyRaw:
{
estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
if (argc == 1)
gpgsm_p12_export (&ctrl, *argv, fp, 2);
else
wrong_args ("--export-secret-key-raw KEY-ID");
if (fp != es_stdout)
es_fclose (fp);
}
break;
case aSendKeys: case aSendKeys:
case aRecvKeys: case aRecvKeys:
log_error ("this command has not yet been implemented\n"); log_error ("this command has not yet been implemented\n");

View File

@ -348,7 +348,8 @@ int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
/*-- export.c --*/ /*-- export.c --*/
void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream);
void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream); void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream,
int rawmode);
/*-- delete.c --*/ /*-- delete.c --*/
int gpgsm_delete (ctrl_t ctrl, strlist_t names); int gpgsm_delete (ctrl_t ctrl, strlist_t names);

View File

@ -1,5 +1,6 @@
/* minip12.c - A minimal pkcs-12 implementation. /* minip12.c - A minimal pkcs-12 implementation.
* Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -1891,10 +1892,15 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
} }
} }
} }
MODE controls what is being generated:
0 - As described above
1 - Ditto but without the padding
2 - Only the inner part (pkcs#1)
*/ */
static unsigned char * static unsigned char *
build_key_sequence (gcry_mpi_t *kparms, size_t *r_length) build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{ {
int rc, i; int rc, i;
size_t needed, n; size_t needed, n;
@ -1902,7 +1908,7 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
size_t plainlen; size_t plainlen;
size_t outseqlen, oidseqlen, octstrlen, inseqlen; size_t outseqlen, oidseqlen, octstrlen, inseqlen;
needed = 3; /* The version(?) integer of value 0. */ needed = 3; /* The version integer with value 0. */
for (i=0; kparms[i]; i++) for (i=0; kparms[i]; i++)
{ {
n = 0; n = 0;
@ -1929,23 +1935,27 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
if (!n) if (!n)
return NULL; return NULL;
needed += n; needed += n;
/* Encapsulate all into an octet string. */
octstrlen = needed; if (mode != 2)
n = compute_tag_length (needed); {
if (!n) /* Encapsulate all into an octet string. */
return NULL; octstrlen = needed;
needed += n; n = compute_tag_length (needed);
/* Prepend the object identifier sequence. */ if (!n)
oidseqlen = 2 + DIM (oid_rsaEncryption) + 2; return NULL;
needed += 2 + oidseqlen; needed += n;
/* The version number. */ /* Prepend the object identifier sequence. */
needed += 3; oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
/* And finally put the whole thing into a sequence. */ needed += 2 + oidseqlen;
outseqlen = needed; /* The version number. */
n = compute_tag_length (needed); needed += 3;
if (!n) /* And finally put the whole thing into a sequence. */
return NULL; outseqlen = needed;
needed += n; n = compute_tag_length (needed);
if (!n)
return NULL;
needed += n;
}
/* allocate 8 extra bytes for padding */ /* allocate 8 extra bytes for padding */
plain = gcry_malloc_secure (needed+8); plain = gcry_malloc_secure (needed+8);
@ -1957,20 +1967,24 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
/* And now fill the plaintext buffer. */ /* And now fill the plaintext buffer. */
p = plain; p = plain;
p = store_tag_length (p, TAG_SEQUENCE, outseqlen); if (mode != 2)
/* Store version. */ {
*p++ = TAG_INTEGER; p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
*p++ = 1; /* Store version. */
*p++ = 0; *p++ = TAG_INTEGER;
/* Store object identifier sequence. */ *p++ = 1;
p = store_tag_length (p, TAG_SEQUENCE, oidseqlen); *p++ = 0;
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption)); /* Store object identifier sequence. */
memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption)); p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
p += DIM (oid_rsaEncryption); p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
*p++ = TAG_NULL; memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
*p++ = 0; p += DIM (oid_rsaEncryption);
/* Start with the octet string. */ *p++ = TAG_NULL;
p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); *p++ = 0;
/* Start with the octet string. */
p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
}
p = store_tag_length (p, TAG_SEQUENCE, inseqlen); p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
/* Store the key parameters. */ /* Store the key parameters. */
*p++ = TAG_INTEGER; *p++ = TAG_INTEGER;
@ -2003,10 +2017,14 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
plainlen = p - plain; plainlen = p - plain;
assert (needed == plainlen); assert (needed == plainlen);
/* Append some pad characters; we already allocated extra space. */
n = 8 - plainlen % 8; if (!mode)
for (i=0; i < n; i++, plainlen++) {
*p++ = n; /* Append some pad characters; we already allocated extra space. */
n = 8 - plainlen % 8;
for (i=0; i < n; i++, plainlen++)
*p++ = n;
}
*r_length = plainlen; *r_length = plainlen;
return plain; return plain;
@ -2459,7 +2477,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
if (kparms) if (kparms)
{ {
/* Encode the key. */ /* Encode the key. */
buffer = build_key_sequence (kparms, &buflen); buffer = build_key_sequence (kparms, 0, &buflen);
if (!buffer) if (!buffer)
goto failure; goto failure;
@ -2502,6 +2520,24 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
} }
/* This is actually not a pkcs#12 function but one which creates an
unencrypted a pkcs#1 private key. */
unsigned char *
p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length)
{
unsigned char *buffer;
size_t buflen;
assert (rawmode == 1 || rawmode == 2);
buffer = build_key_sequence (kparms, rawmode, &buflen);
if (!buffer)
return NULL;
*r_length = buflen;
return buffer;
}
#ifdef TEST #ifdef TEST
static void static void

View File

@ -31,6 +31,9 @@ unsigned char *p12_build (gcry_mpi_t *kparms,
const void *cert, size_t certlen, const void *cert, size_t certlen,
const char *pw, const char *charset, const char *pw, const char *charset,
size_t *r_length); size_t *r_length);
unsigned char *p12_raw_build (gcry_mpi_t *kparms,
int rawmode,
size_t *r_length);
#endif /*MINIP12_H*/ #endif /*MINIP12_H*/