From 2b632bbb78eee2b94c122f66d171a7c80e9c4fb0 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 24 Aug 2015 12:41:24 +0200 Subject: [PATCH] Add an export secret key feature. * src/gpgme.h.in (GPGME_EXPORT_MODE_SECRET): New. (GPGME_EXPORT_MODE_RAW): New. (GPGME_EXPORT_MODE_PKCS12): New. * src/export.c (export_start, export_ext_start): Allow new flags. * src/engine-gpg.c (export_common): Support secret key export. * src/engine-gpgsm.c (gpgsm_export, gpgsm_export_ext): Ditto. * src/gpgme-tool.c (cmd_export): Add options --secret, --raw, and --pkcs12. * tests/run-export.c (main): Likewise. -- Note that exporting secret X.509 keys requires GnuPG 2.1.8. Signed-off-by: Werner Koch --- doc/gpgme.texi | 15 +++++++++++++++ src/engine-gpg.c | 8 ++++++-- src/engine-gpgsm.c | 33 ++++++++++++++++++++++----------- src/export.c | 35 +++++++++++++++++++++++++++++++++-- src/gpgme-tool.c | 8 +++++++- src/gpgme.h.in | 3 +++ tests/run-export.c | 41 +++++++++++++++++++++++++++++++++++++---- 7 files changed, 123 insertions(+), 20 deletions(-) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 010b914d..20e1912b 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -3700,6 +3700,21 @@ keys it removes all signatures except for the latest self-signatures. For X.509 keys it has no effect. +@item GPGME_EXPORT_MODE_SECRET +Instead of exporting the public key, the secret key is exported. This +may not be combined with @code{GPGME_EXPORT_MODE_EXTERN}. For X.509 +the export format is PKCS#8. + +@item GPGME_EXPORT_MODE_RAW +If this flag is used with @code{GPGME_EXPORT_MODE_SECRET} for an X.509 +key the export format will be changed to PKCS#1. This flag may not be +used with OpenPGP. + +@item GPGME_EXPORT_MODE_PKCS12 +If this flag is used with @code{GPGME_EXPORT_MODE_SECRET} for an X.509 +key the export format will be changed to PKCS#12 which also includes +the certificate. This flag may not be used with OpenPGP. + @end table diff --git a/src/engine-gpg.c b/src/engine-gpg.c index d1385926..ffae2fe4 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1793,7 +1793,8 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, gpgme_error_t err = 0; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN - |GPGME_EXPORT_MODE_MINIMAL))) + |GPGME_EXPORT_MODE_MINIMAL + |GPGME_EXPORT_MODE_SECRET))) return gpg_error (GPG_ERR_NOT_SUPPORTED); if ((mode & GPGME_EXPORT_MODE_MINIMAL)) @@ -1807,7 +1808,10 @@ export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, } else { - err = add_arg (gpg, "--export"); + if ((mode & GPGME_EXPORT_MODE_SECRET)) + err = add_arg (gpg, "--export-secret-keys"); + else + err = add_arg (gpg, "--export"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 37711574..24d3b2a8 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1289,17 +1289,23 @@ gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode, if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); - if (mode) - return gpg_error (GPG_ERR_NOT_SUPPORTED); - if (!pattern) pattern = ""; - cmd = malloc (7 + strlen (pattern) + 1); + cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1); if (!cmd) return gpg_error_from_syserror (); + strcpy (cmd, "EXPORT "); - strcpy (&cmd[7], pattern); + if ((mode & GPGME_EXPORT_MODE_SECRET)) + { + strcat (cmd, "--secret "); + if ((mode & GPGME_EXPORT_MODE_RAW)) + strcat (cmd, "--raw "); + else if ((mode & GPGME_EXPORT_MODE_PKCS12)) + strcat (cmd, "--pkcs12 "); + } + strcat (cmd, pattern); gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" @@ -1323,16 +1329,13 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *line; - /* Length is "EXPORT " + p + '\0'. */ - int length = 7 + 1; + /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'. */ + int length = 7 + 9 + 9 + 1; char *linep; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); - if (mode) - return gpg_error (GPG_ERR_NOT_SUPPORTED); - if (pattern && *pattern) { const char **pat = pattern; @@ -1357,7 +1360,15 @@ gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, return gpg_error_from_syserror (); strcpy (line, "EXPORT "); - linep = &line[7]; + if ((mode & GPGME_EXPORT_MODE_SECRET)) + { + strcat (line, "--secret "); + if ((mode & GPGME_EXPORT_MODE_RAW)) + strcat (line, "--raw "); + else if ((mode & GPGME_EXPORT_MODE_PKCS12)) + strcat (line, "--pkcs12 "); + } + linep = &line[strlen (line)]; if (pattern && *pattern) { diff --git a/src/export.c b/src/export.c index 8930aa68..a29fbde8 100644 --- a/src/export.c +++ b/src/export.c @@ -120,9 +120,24 @@ export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern, op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN - |GPGME_EXPORT_MODE_MINIMAL))) + |GPGME_EXPORT_MODE_MINIMAL + |GPGME_EXPORT_MODE_SECRET + |GPGME_EXPORT_MODE_RAW + |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ + if ((mode & GPGME_EXPORT_MODE_SECRET)) + { + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ + if ((mode & GPGME_EXPORT_MODE_RAW) + && (mode & GPGME_EXPORT_MODE_PKCS12)) + return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ + + if (ctx->protocol != GPGME_PROTOCOL_CMS + && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) + return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ + } if ((mode & GPGME_EXPORT_MODE_EXTERN)) { @@ -199,9 +214,25 @@ export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[], op_data_t opd; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN - |GPGME_EXPORT_MODE_MINIMAL))) + |GPGME_EXPORT_MODE_MINIMAL + |GPGME_EXPORT_MODE_SECRET + |GPGME_EXPORT_MODE_RAW + |GPGME_EXPORT_MODE_PKCS12))) return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */ + if ((mode & GPGME_EXPORT_MODE_SECRET)) + { + if ((mode & GPGME_EXPORT_MODE_EXTERN)) + return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ + if ((mode & GPGME_EXPORT_MODE_RAW) + && (mode & GPGME_EXPORT_MODE_PKCS12)) + return gpg_error (GPG_ERR_INV_FLAG); /* Combination not allowed. */ + + if (ctx->protocol != GPGME_PROTOCOL_CMS + && (mode & (GPGME_EXPORT_MODE_RAW|GPGME_EXPORT_MODE_PKCS12))) + return gpg_error (GPG_ERR_INV_FLAG); /* Only supported for X.509. */ + } + if ((mode & GPGME_EXPORT_MODE_EXTERN)) { if (keydata) diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c index 94d11248..e5e57073 100644 --- a/src/gpgme-tool.c +++ b/src/gpgme-tool.c @@ -3054,7 +3054,7 @@ cmd_import (assuan_context_t ctx, char *line) static const char hlp_export[] = - "EXPORT [--extern] [--minimal] []\n" + "EXPORT [--extern] [--minimal] [--secret [--pkcs12] [--raw]] []\n" "\n" "Export the keys described by PATTERN. Write the\n" "the output to the object set by the last OUTPUT command."; @@ -3082,6 +3082,12 @@ cmd_export (assuan_context_t ctx, char *line) mode |= GPGME_EXPORT_MODE_EXTERN; if (has_option (line, "--minimal")) mode |= GPGME_EXPORT_MODE_MINIMAL; + if (has_option (line, "--secret")) + mode |= GPGME_EXPORT_MODE_SECRET; + if (has_option (line, "--raw")) + mode |= GPGME_EXPORT_MODE_RAW; + if (has_option (line, "--pkcs12")) + mode |= GPGME_EXPORT_MODE_PKCS12; line = skip_options (line); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 8255e637..76055708 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -392,6 +392,9 @@ gpgme_pinentry_mode_t; /* The available export mode flags. */ #define GPGME_EXPORT_MODE_EXTERN 2 #define GPGME_EXPORT_MODE_MINIMAL 4 +#define GPGME_EXPORT_MODE_SECRET 16 +#define GPGME_EXPORT_MODE_RAW 32 +#define GPGME_EXPORT_MODE_PKCS12 64 typedef unsigned int gpgme_export_mode_t; diff --git a/tests/run-export.c b/tests/run-export.c index 43332087..b133f130 100644 --- a/tests/run-export.c +++ b/tests/run-export.c @@ -43,7 +43,12 @@ show_usage (int ex) fputs ("usage: " PGM " [options] USERIDS\n\n" "Options:\n" " --verbose run in verbose mode\n" + " --openpgp use OpenPGP protocol (default)\n" + " --cms use X.509 protocol\n" " --extern send keys to the keyserver (TAKE CARE!)\n" + " --secret export secret keys instead of public keys\n" + " --raw use PKCS#1 as secret key format\n" + " --pkcs12 use PKCS#12 as secret key format\n" , stderr); exit (ex); } @@ -59,6 +64,7 @@ main (int argc, char **argv) gpgme_key_t keyarray[100]; int keyidx = 0; gpgme_data_t out; + gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_export_mode_t mode = 0; if (argc) @@ -79,9 +85,34 @@ main (int argc, char **argv) verbose = 1; argc--; argv++; } + else if (!strcmp (*argv, "--openpgp")) + { + protocol = GPGME_PROTOCOL_OpenPGP; + argc--; argv++; + } + else if (!strcmp (*argv, "--cms")) + { + protocol = GPGME_PROTOCOL_CMS; + argc--; argv++; + } else if (!strcmp (*argv, "--extern")) { - mode |= GPGME_KEYLIST_MODE_EXTERN; + mode |= GPGME_EXPORT_MODE_EXTERN; + argc--; argv++; + } + else if (!strcmp (*argv, "--secret")) + { + mode |= GPGME_EXPORT_MODE_SECRET; + argc--; argv++; + } + else if (!strcmp (*argv, "--raw")) + { + mode |= GPGME_EXPORT_MODE_RAW; + argc--; argv++; + } + else if (!strcmp (*argv, "--pkcs12")) + { + mode |= GPGME_EXPORT_MODE_PKCS12; argc--; argv++; } else if (!strncmp (*argv, "--", 2)) @@ -92,11 +123,11 @@ main (int argc, char **argv) if (!argc) show_usage (1); - init_gpgme (GPGME_PROTOCOL_OpenPGP); + init_gpgme (protocol); err = gpgme_new (&ctx); fail_if_err (err); - gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); + gpgme_set_protocol (ctx, protocol); /* Lookup the keys. */ err = gpgme_op_keylist_ext_start (ctx, (const char**)argv, 0, 0); @@ -131,8 +162,10 @@ main (int argc, char **argv) } /* Now for the actual export. */ - if ((mode & GPGME_KEYLIST_MODE_EXTERN)) + if ((mode & GPGME_EXPORT_MODE_EXTERN)) printf ("sending keys to keyserver\n"); + if ((mode & GPGME_EXPORT_MODE_SECRET)) + printf ("exporting secret keys!\n"); err = gpgme_data_new (&out); fail_if_err (err);