gpgsm: New commands --export-secret-key-{p8,raw}
authorWerner Koch <wk@gnupg.org>
Tue, 3 Jun 2014 16:57:33 +0000 (18:57 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 3 Jun 2014 16:57:33 +0000 (18:57 +0200)
* 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.

NEWS
doc/gpgsm.texi
sm/export.c
sm/gpgsm.c
sm/gpgsm.h
sm/minip12.c
sm/minip12.h

diff --git a/NEWS b/NEWS
index da771f1..ba14079 100644 (file)
--- 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)
 -----------------------------------------------------
index 3d2594f..b38ad4d 100644 (file)
@@ -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
index 0403fe2..1dce106 100644 (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,
                                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);
+    }
 }
 
 
 \f
-/* 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);
index 3822717..01f33e3 100644 (file)
@@ -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");
index 6c68af7..7c7ca7a 100644 (file)
@@ -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);
index c91ef22..01b91b7 100644 (file)
@@ -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
index 27f24f5..7a1950f 100644 (file)
@@ -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*/