From fd628ffda1baf5a8b1a7264ae9900801a7174269 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 20 Mar 2007 10:00:55 +0000 Subject: [PATCH] Allow setting of the passphrase encoding of pkcs#12 files. New option --p12-charset. --- NEWS | 2 +- agent/ChangeLog | 5 ++++ agent/minip12.c | 71 ++++++++++++++++++++++++++++++++++++++++---- agent/minip12.h | 3 +- agent/protect-tool.c | 12 ++++++-- doc/gpgsm.texi | 23 ++++++++++---- sm/ChangeLog | 6 ++++ sm/export.c | 11 +++++++ sm/gpgsm.c | 11 +++++-- sm/gpgsm.h | 4 +++ 10 files changed, 130 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index 42facb376..59afa35ea 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,7 @@ Noteworthy changes in version 2.0.4 without the funopen/fopencookie API. * PKCS#12 import now tries several encodings in case the passphrase - was not utf-8 encoded. + was not utf-8 encoded. New option --p12-charset for gpgsm. Noteworthy changes in version 2.0.3 (2007-03-08) diff --git a/agent/ChangeLog b/agent/ChangeLog index 3afb61dc4..ea0fbe6e5 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,8 @@ +2007-03-20 Werner Koch + + * protect-tool.c: New option --p12-charset. + * minip12.c (p12_build): Implement it. + 2007-03-19 Werner Koch * minip12.c: Include iconv.h. diff --git a/agent/minip12.c b/agent/minip12.c index d6029f7b0..6958e5e1d 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -28,11 +28,11 @@ #include #include #include +#include #ifdef TEST #include #include -#include #endif #include "../jnlib/logging.h" @@ -518,6 +518,10 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, "ISO-8859-8", "ISO-8859-9", "KOI8-R", + "IBM437", + "IBM850", + "EUC-JP", + "BIG5", NULL }; int charsetidx = 0; @@ -2139,25 +2143,75 @@ build_cert_sequence (unsigned char *buffer, size_t buflen, } -/* Expect the RSA key parameters in KPARMS and a password in - PW. Create a PKCS structure from it and return it as well as the - length in R_LENGTH; return NULL in case of an error. */ +/* Expect the RSA key parameters in KPARMS and a password in PW. + Create a PKCS structure from it and return it as well as the length + in R_LENGTH; return NULL in case of an error. If CHARSET is not + NULL, re-encode PW to that character set. */ unsigned char * p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen, - const char *pw, size_t *r_length) + const char *pw, const char *charset, size_t *r_length) { - unsigned char *buffer; + unsigned char *buffer = NULL; size_t n, buflen; char salt[8]; struct buffer_s seqlist[3]; int seqlistidx = 0; unsigned char sha1hash[20]; char keyidstr[8+1]; + char *pwbuf = NULL; + size_t pwbufsize = 0; n = buflen = 0; /* (avoid compiler warning). */ memset (sha1hash, 0, 20); *keyidstr = 0; + if (charset && pw && *pw) + { + iconv_t cd; + const char *inptr; + char *outptr; + size_t inbytes, outbytes; + + /* We assume that the converted passphrase is at max 2 times + longer than its utf-8 encoding. */ + pwbufsize = strlen (pw)*2 + 1; + pwbuf = gcry_malloc_secure (pwbufsize); + if (!pwbuf) + { + log_error ("out of secure memory while converting passphrase\n"); + goto failure; + } + + cd = iconv_open (charset, "utf-8"); + if (cd == (iconv_t)(-1)) + { + log_error ("can't convert passphrase to" + " requested charset `%s': %s\n", + charset, strerror (errno)); + gcry_free (pwbuf); + goto failure; + } + + inptr = pw; + inbytes = strlen (pw); + outptr = pwbuf; + outbytes = pwbufsize - 1; + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + log_error ("error converting passphrase to" + " requested charset `%s': %s\n", + charset, strerror (errno)); + gcry_free (pwbuf); + iconv_close (cd); + goto failure; + } + *outptr = 0; + iconv_close (cd); + pw = pwbuf; + } + + if (cert && certlen) { /* Calculate the hash value we need for the bag attributes. */ @@ -2219,6 +2273,11 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen, buffer = create_final (seqlist, pw, &buflen); failure: + if (pwbuf) + { + wipememory (pwbuf, pwbufsize); + gcry_free (pwbuf); + } for ( ; seqlistidx; seqlistidx--) gcry_free (seqlist[seqlistidx].buffer); diff --git a/agent/minip12.h b/agent/minip12.h index 6275f9ccb..7977fcb02 100644 --- a/agent/minip12.h +++ b/agent/minip12.h @@ -31,7 +31,8 @@ gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, unsigned char *p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen, - const char *pw, size_t *r_length); + const char *pw, const char *charset, + size_t *r_length); #endif /*MINIP12_H*/ diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 8f974e2da..937b0ef0c 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -65,6 +65,7 @@ enum cmd_and_opt_values oP12Import, oP12Export, + oP12Charset, oStore, oForce, oHaveCert, @@ -96,6 +97,7 @@ static int opt_have_cert; static const char *opt_passphrase; static char *opt_prompt; static int opt_status_msg; +static const char *opt_p12_charset; static char *get_passphrase (int promptno, int opt_check); static char *get_new_passphrase (int promptno); @@ -118,8 +120,10 @@ static ARGPARSE_OPTS opts[] = { { oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"}, { oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""}, - { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"}, - { oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"}, + { oP12Import, "p12-import", 256, "import a pkcs#12 encoded private key"}, + { oP12Export, "p12-export", 256, "export a private key pkcs#12 encoded"}, + { oP12Charset,"p12-charset", 2, + "|NAME|set charset for a new PKCS#12 passphrase to NAME" }, { oHaveCert, "have-cert", 0, "certificate to export provided on STDIN"}, { oStore, "store", 0, "store the created key in the appropriate place"}, { oForce, "force", 0, "force overwriting"}, @@ -127,6 +131,7 @@ static ARGPARSE_OPTS opts[] = { { oHomedir, "homedir", 2, "@" }, { oPrompt, "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"}, { oStatusMsg, "enable-status-msg", 0, "@"}, + {0} }; @@ -987,7 +992,7 @@ export_p12_file (const char *fname) kparms[8] = NULL; key = p12_build (kparms, cert, certlen, - (pw=get_new_passphrase (3)), &keylen); + (pw=get_new_passphrase (3)), opt_p12_charset, &keylen); release_passphrase (pw); xfree (cert); for (i=0; i < 8; i++) @@ -1101,6 +1106,7 @@ main (int argc, char **argv ) case oShowKeygrip: cmd = oShowKeygrip; break; case oP12Import: cmd = oP12Import; break; case oP12Export: cmd = oP12Export; break; + case oP12Charset: opt_p12_charset = pargs.r.ret_str; break; case oPassphrase: opt_passphrase = pargs.r.ret_str; break; case oStore: opt_store = 1; break; diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 9df760c69..451f09a9c 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -233,11 +233,11 @@ a few informational lines are prepended before each block. @item --export-secret-key-p12 @var{key-id} @opindex export -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 informational lines are prepended to the output. Note, that the -PKCS#12 format is higly insecure and this command is only provided if -there is no other way to exchange the private key. +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 +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}) @item --import [@var{files}] @opindex import @@ -437,6 +437,19 @@ Assume the input data is plain base-64 encoded. @opindex assume-binary Assume the input data is binary encoded. +@anchor{option --p12-charset} +@item --p12-charset @var{name} +@opindex p12-charset +@command{gpgsm} uses the UTF-8 encoding when encoding passphrases for +PKCS#12 files. This option may be used to force the passphrase to be +encoded in the specified encoding @var{name}. This is useful if the +application used to import the key uses a different encoding and thus +won't be able to import a file generated by @command{gpgsm}. Commonly +used values for @var{name} are @code{Latin1} and @code{CP850}. Note +that @command{gpgsm} itself automagically imports any file with a +passphrase encoded to the most commonly used encodings. + + @item --local-user @var{user_id} @item -u @var{user_id} @opindex local-user diff --git a/sm/ChangeLog b/sm/ChangeLog index f666af5c3..32663041a 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,9 @@ +2007-03-20 Werner Koch + + * gpgsm.c: Add option --p12-charset. + * gpgsm.h (struct opt): Add p12_charset. + * export.c (popen_protect_tool): Use new option. + 2007-03-19 Werner Koch Changes to let export and key listing use estream to help systems diff --git a/sm/export.c b/sm/export.c index a87499f94..ec8a764c3 100644 --- a/sm/export.c +++ b/sm/export.c @@ -416,6 +416,12 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp) putc ('\n', fp); } + if (opt.p12_charset) + { + fprintf (fp, "The passphrase is %s encoded.\n\n", + opt.p12_charset); + } + ctrl->pem_name = "PKCS12"; rc = gpgsm_create_writer (&b64writer, ctrl, fp, NULL, &writer); if (rc) @@ -567,6 +573,11 @@ popen_protect_tool (const char *pgmname, argv[i++] = "--prompt"; argv[i++] = prompt?prompt:""; argv[i++] = "--enable-status-msg"; + if (opt.p12_charset) + { + argv[i++] = "--p12-charset"; + argv[i++] = opt.p12_charset; + } argv[i++] = "--", argv[i++] = keygrip, argv[i] = NULL; diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 415a7cafa..9abd0636e 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -131,6 +131,7 @@ enum cmd_and_opt_values { oBase64, oNoArmor, + oP12Charset, oDisableCRLChecks, oEnableCRLChecks, @@ -280,6 +281,8 @@ static ARGPARSE_OPTS opts[] = { { oArmor, "armor", 0, N_("create ascii armored output")}, { oArmor, "armour", 0, "@" }, { oBase64, "base64", 0, N_("create base-64 encoded output")}, + + { oP12Charset, "p12-charset", 2, "@" }, { oAssumeArmor, "assume-armor", 0, N_("assume input is in PEM format")}, { oAssumeBase64, "assume-base64", 0, @@ -955,7 +958,7 @@ main ( int argc, char **argv) set_cmd (&cmd, pargs.r_opt); break; - /* output encoding selection */ + /* Output encoding selection. */ case oArmor: ctrl.create_pem = 1; break; @@ -968,7 +971,11 @@ main ( int argc, char **argv) ctrl.create_base64 = 0; break; - /* Input encoding selection */ + case oP12Charset: + opt.p12_charset = pargs.r.ret_str; + break; + + /* Input encoding selection. */ case oAssumeArmor: ctrl.autodetect_encoding = 0; ctrl.is_pem = 1; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index a52e9e653..727b1cf98 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -74,6 +74,10 @@ struct int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ + const char *p12_charset; /* Use this charset for encoding the + pkcs#12 passphrase. */ + + const char *def_cipher_algoid; /* cipher algorithm to use if nothing else is specified */