summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--doc/gpgsm.texi17
-rw-r--r--sm/export.c71
-rw-r--r--sm/gpgsm.c38
-rw-r--r--sm/gpgsm.h3
-rw-r--r--sm/minip12.c112
-rw-r--r--sm/minip12.h3
7 files changed, 184 insertions, 63 deletions
diff --git a/NEWS b/NEWS
index da771f115..ba140796d 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,9 @@ Noteworthy changes in version 2.1.0-betaN (unreleased)
* 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)
-----------------------------------------------------
diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
index 3d2594f68..b38ad4d72 100644
--- a/doc/gpgsm.texi
+++ b/doc/gpgsm.texi
@@ -259,13 +259,26 @@ certificate are only exported if all @var{pattern} are given as
fingerprints or keygrips.
@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
-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
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})
+@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}]
@opindex import
Import the certificates from the PEM or binary encoded files as well as
diff --git a/sm/export.c b/sm/export.c
index 0403fe2f5..1dce106a1 100644
--- a/sm/export.c
+++ b/sm/export.c
@@ -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,
const unsigned char *certimg, size_t certimglen,
const char *prompt, const char *keygrip,
+ int rawmode,
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
-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;
KEYDB_HANDLE hd;
@@ -416,13 +422,18 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t 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",
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);
if (err)
{
@@ -431,7 +442,8 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream)
}
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);
if (err)
goto leave;
@@ -513,12 +525,19 @@ print_short_info (ksba_cert_t cert, estream_t stream)
xfree (p);
}
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
- the RSA paramaters in pkcs#12 order. The caller needs to
+/* Parse a private key S-expression and return a malloced array with
+ the RSA parameters in pkcs#12 order. The caller needs to
deep-release this array. */
static gcry_mpi_t *
sexp_to_kparms (gcry_sexp_t sexp)
@@ -587,7 +606,7 @@ sexp_to_kparms (gcry_sexp_t sexp)
static gpg_error_t
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)
{
gpg_error_t err = 0;
@@ -671,20 +690,30 @@ export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen,
goto leave;
}
- 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;
+ if (rawmode)
+ {
+ /* Export in raw mode, that is only the pkcs#1/#8 private key. */
+ result = p12_raw_build (kparms, rawmode, &resultlen);
+ if (!result)
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ 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,
- opt.p12_charset, &resultlen);
- xfree (passphrase);
- passphrase = NULL;
- if (!result)
- err = gpg_error (GPG_ERR_GENERAL);
+ result = p12_build (kparms, certimg, certimglen, passphrase,
+ opt.p12_charset, &resultlen);
+ xfree (passphrase);
+ passphrase = NULL;
+ if (!result)
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
leave:
xfree (key);
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 3822717ca..01f33e3ae 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -74,6 +74,8 @@ enum cmd_and_opt_values {
aRecvKeys,
aExport,
aExportSecretKeyP12,
+ aExportSecretKeyP8,
+ aExportSecretKeyRaw,
aServer,
aLearnCard,
aCallDirmngr,
@@ -208,7 +210,13 @@ static ARGPARSE_OPTS opts[] = {
/*ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a key server")),*/
ARGPARSE_c (aImport, "import", N_("import 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 (aExportSecretKeyP8, "export-secret-key-p8", "@"),
+ ARGPARSE_c (aExportSecretKeyRaw, "export-secret-key-raw", "@"),
+
ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")),
ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aCallDirmngr, "call-dirmngr",
@@ -1084,6 +1092,8 @@ main ( int argc, char **argv)
case aRecvKeys:
case aExport:
case aExportSecretKeyP12:
+ case aExportSecretKeyP8:
+ case aExportSecretKeyRaw:
case aDumpKeys:
case aDumpChain:
case aDumpExternalKeys:
@@ -1888,7 +1898,7 @@ main ( int argc, char **argv)
estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
if (argc == 1)
- gpgsm_p12_export (&ctrl, *argv, fp);
+ gpgsm_p12_export (&ctrl, *argv, fp, 0);
else
wrong_args ("--export-secret-key-p12 KEY-ID");
if (fp != es_stdout)
@@ -1896,6 +1906,32 @@ main ( int argc, char **argv)
}
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 aRecvKeys:
log_error ("this command has not yet been implemented\n");
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 6c68af746..7c7ca7a20 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -348,7 +348,8 @@ int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
/*-- export.c --*/
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 --*/
int gpgsm_delete (ctrl_t ctrl, strlist_t names);
diff --git a/sm/minip12.c b/sm/minip12.c
index c91ef226a..01b91b710 100644
--- a/sm/minip12.c
+++ b/sm/minip12.c
@@ -1,5 +1,6 @@
/* minip12.c - A minimal pkcs-12 implementation.
* Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
*
* 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 *
-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;
size_t needed, n;
@@ -1902,7 +1908,7 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
size_t plainlen;
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++)
{
n = 0;
@@ -1929,23 +1935,27 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
if (!n)
return NULL;
needed += n;
- /* Encapsulate all into an octet string. */
- octstrlen = needed;
- n = compute_tag_length (needed);
- if (!n)
- return NULL;
- needed += n;
- /* Prepend the object identifier sequence. */
- oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
- needed += 2 + oidseqlen;
- /* The version number. */
- needed += 3;
- /* And finally put the whole thing into a sequence. */
- outseqlen = needed;
- n = compute_tag_length (needed);
- if (!n)
- return NULL;
- needed += n;
+
+ if (mode != 2)
+ {
+ /* Encapsulate all into an octet string. */
+ octstrlen = needed;
+ n = compute_tag_length (needed);
+ if (!n)
+ return NULL;
+ needed += n;
+ /* Prepend the object identifier sequence. */
+ oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
+ needed += 2 + oidseqlen;
+ /* The version number. */
+ needed += 3;
+ /* And finally put the whole thing into a sequence. */
+ outseqlen = needed;
+ n = compute_tag_length (needed);
+ if (!n)
+ return NULL;
+ needed += n;
+ }
/* allocate 8 extra bytes for padding */
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. */
p = plain;
- p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
- /* Store version. */
- *p++ = TAG_INTEGER;
- *p++ = 1;
- *p++ = 0;
- /* Store object identifier sequence. */
- p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
- p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
- memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
- p += DIM (oid_rsaEncryption);
- *p++ = TAG_NULL;
- *p++ = 0;
- /* Start with the octet string. */
- p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
+ if (mode != 2)
+ {
+ p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
+ /* Store version. */
+ *p++ = TAG_INTEGER;
+ *p++ = 1;
+ *p++ = 0;
+ /* Store object identifier sequence. */
+ p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
+ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
+ memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
+ p += DIM (oid_rsaEncryption);
+ *p++ = TAG_NULL;
+ *p++ = 0;
+ /* Start with the octet string. */
+ p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
+ }
+
p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
/* Store the key parameters. */
*p++ = TAG_INTEGER;
@@ -2003,10 +2017,14 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
plainlen = p - plain;
assert (needed == plainlen);
- /* Append some pad characters; we already allocated extra space. */
- n = 8 - plainlen % 8;
- for (i=0; i < n; i++, plainlen++)
- *p++ = n;
+
+ if (!mode)
+ {
+ /* 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;
return plain;
@@ -2459,7 +2477,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
if (kparms)
{
/* Encode the key. */
- buffer = build_key_sequence (kparms, &buflen);
+ buffer = build_key_sequence (kparms, 0, &buflen);
if (!buffer)
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
static void
diff --git a/sm/minip12.h b/sm/minip12.h
index 27f24f5a1..7a1950fb0 100644
--- a/sm/minip12.h
+++ b/sm/minip12.h
@@ -31,6 +31,9 @@ unsigned char *p12_build (gcry_mpi_t *kparms,
const void *cert, size_t certlen,
const char *pw, const char *charset,
size_t *r_length);
+unsigned char *p12_raw_build (gcry_mpi_t *kparms,
+ int rawmode,
+ size_t *r_length);
#endif /*MINIP12_H*/