Allow setting of the passphrase encoding of pkcs#12 files.
authorWerner Koch <wk@gnupg.org>
Tue, 20 Mar 2007 10:00:55 +0000 (10:00 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 20 Mar 2007 10:00:55 +0000 (10:00 +0000)
New option --p12-charset.

NEWS
agent/ChangeLog
agent/minip12.c
agent/minip12.h
agent/protect-tool.c
doc/gpgsm.texi
sm/ChangeLog
sm/export.c
sm/gpgsm.c
sm/gpgsm.h

diff --git a/NEWS b/NEWS
index 42facb3..59afa35 100644 (file)
--- 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)
index 3afb61d..ea0fbe6 100644 (file)
@@ -1,3 +1,8 @@
+2007-03-20  Werner Koch  <wk@g10code.com>
+
+       * protect-tool.c: New option --p12-charset. 
+       * minip12.c (p12_build): Implement it.
+
 2007-03-19  Werner Koch  <wk@g10code.com>
 
        * minip12.c: Include iconv.h.
index d6029f7..6958e5e 100644 (file)
 #include <assert.h>
 #include <gcrypt.h>
 #include <iconv.h>
+#include <errno.h>
 
 #ifdef TEST
 #include <sys/stat.h>
 #include <unistd.h>
-#include <errno.h>
 #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);
 
index 6275f9c..7977fcb 100644 (file)
@@ -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*/
index 8f974e2..937b0ef 100644 (file)
@@ -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;
index 9df760c..451f09a 100644 (file)
@@ -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
index f666af5..3266304 100644 (file)
@@ -1,3 +1,9 @@
+2007-03-20  Werner Koch  <wk@g10code.com>
+
+       * 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  <wk@g10code.com>
 
         Changes to let export and key listing use estream to help systems
index a87499f..ec8a764 100644 (file)
@@ -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;
index 415a7ca..9abd063 100644 (file)
@@ -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;
index a52e9e6..727b1cf 100644 (file)
@@ -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 */