Smime users, I could definitely use testing and feedback for this patch. This patch came from a bug report on IRC from "ep". He noticed Mutt was hardcoding the micalg parameter to sha1, but his actual signature digest was using sha256. Because of this, Thunderbird was rejecting the signature.
There are two ways to deal with this. One is try to querying the "Signature Algorithm" field in the certificate and use that value in the micalg parameter instead. Another possibility is to let the Mutt user instead specify the signature digest algorithm they want to use. This patch takes that route. The downside to this is that the revised $smime_sign_command, with a '-md %d' needs to be used or else the generated signature (again) won't match the value stored in micalg. Smime users, and ep if you're out there, please help me test this and provide feedback. Thanks. -- Kevin J. McCarthy GPG Fingerprint: 8975 A9B3 3AA3 7910 385C 5308 ADEF 7684 8031 6BDA http://www.8t8.us/configs/gpg-key-transition-statement.txt
# HG changeset patch # User Kevin McCarthy <[email protected]> # Date 1436742302 25200 # Sun Jul 12 16:05:02 2015 -0700 # Node ID 5e4f6e5164fbe2fa99b544fd5494f28e035b0dfd # Parent 7e91a8855dc3a918b1d9e1b4fa32254c2c6b99a3 smime: allow signing message digest algorithm to be specified. Currently, Mutt hardcodes micalg=sha1 for signed messages. Unfortunately, the actual message digest algorithm used defaults to the value in the "Signature Algorithm" field in the signing key's certificate. Add a new configuration option $smime_sign_digest_alg, defaulting to sha256. Add a new printf format string, %d, to be used in the signing command to specify the digest algorithm. Modify the sample $smime_sign_command to include "-md %d". Note: This solution requires using the modified $smime_sign_command, or else the micalg parameter again may not match the algorithm used. An alternative solution would be to query the certificate "Signature Algorithm" field and try to change the micalg to match it. diff --git a/contrib/smime.rc b/contrib/smime.rc --- a/contrib/smime.rc +++ b/contrib/smime.rc @@ -60,18 +60,22 @@ # Algorithm to use for encryption. # valid choices are aes128, aes192, aes256, rc2-40, rc2-64, rc2-128, des, des3 set smime_encrypt_with="aes256" # Encrypt a message. Input file is a MIME entity. set smime_encrypt_command="openssl smime -encrypt -%a -outform DER -in %f %c" +# Algorithm for the signature message digest. +# Valid choices are md5, sha1, sha224, sha256, sha384, sha512. +set smime_sign_digest_alg="sha256" + # Sign. -set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER" +set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -certfile %i -outform DER" #Section C: Incoming messages # Decrypt a message. Output is a MIME entity. set smime_decrypt_command="openssl smime -decrypt -passin stdin -inform DER -in %f -inkey %k -recip %c" @@ -84,15 +88,15 @@ openssl smime -verify -inform DER -in %s -noverify 2>/dev/null" # Section D: Alternatives # Sign. If you wish to NOT include the certificate your CA used in signing # your public key, use this command instead. -# set smime_sign_command="openssl smime -sign -signer %c -inkey %k -passin stdin -in %f -outform DER" +# set smime_sign_command="openssl smime -sign -md %d -signer %c -inkey %k -passin stdin -in %f -outform DER" # # In order to verify the signature only and skip checking the certificate chain: # # set smime_verify_command="openssl smime -verify -inform DER -in %s -content %f -noverify" # set smime_verify_opaque_command="openssl smime -verify -inform DER -in %s -noverify" # diff --git a/globals.h b/globals.h --- a/globals.h +++ b/globals.h @@ -253,16 +253,17 @@ WHERE char *SmimeCertificates; WHERE char *SmimeKeys; WHERE char *SmimeCryptAlg; WHERE char *SmimeCALocation; WHERE char *SmimeVerifyCommand; WHERE char *SmimeVerifyOpaqueCommand; WHERE char *SmimeDecryptCommand; WHERE char *SmimeSignCommand; +WHERE char *SmimeDigestAlg; WHERE char *SmimeSignOpaqueCommand; WHERE char *SmimeEncryptCommand; WHERE char *SmimeGetSignerCertCommand; WHERE char *SmimePk7outCommand; WHERE char *SmimeGetCertCommand; WHERE char *SmimeImportCertCommand; WHERE char *SmimeGetCertEmailCommand; diff --git a/init.h b/init.h --- a/init.h +++ b/init.h @@ -2679,16 +2679,17 @@ ** similar to PGP's: ** .dl ** .dt %f .dd Expands to the name of a file containing a message. ** .dt %s .dd Expands to the name of a file containing the signature part ** . of a \fCmultipart/signed\fP attachment when verifying it. ** .dt %k .dd The key-pair specified with $$smime_default_key ** .dt %c .dd One or more certificate IDs. ** .dt %a .dd The algorithm used for encryption. + ** .dt %d .dd The message digest algorithm specified with $$smime_sign_digest_alg. ** .dt %C .dd CA location: Depending on whether $$smime_ca_location ** . points to a directory or file, this expands to ** . ``-CApath $$smime_ca_location'' or ``-CAfile $$smime_ca_location''. ** .de ** .pp ** For examples on how to configure these formats, see the \fCsmime.rc\fP in ** the \fCsamples/\fP subdirectory which has been installed on your system ** alongside the documentation. @@ -2802,16 +2803,23 @@ ** .pp ** This command is used to created S/MIME signatures of type ** \fCmultipart/signed\fP, which can be read by all mail clients. ** .pp ** This is a format string, see the $$smime_decrypt_command command for ** possible \fCprintf(3)\fP-like sequences. ** (S/MIME only) */ + { "smime_sign_digest_alg", DT_STR, R_NONE, UL &SmimeDigestAlg, UL "sha256" }, + /* + ** .pp + ** This sets the algorithm that should be used for the signature message digest. + ** Valid choices are ``md5'', ``sha1'', ``sha224'', ``sha256'', ``sha384'', ``sha512''. + ** (S/MIME only) + */ { "smime_sign_opaque_command", DT_STR, R_NONE, UL &SmimeSignOpaqueCommand, 0}, /* ** .pp ** This command is used to created S/MIME signatures of type ** \fCapplication/x-pkcs7-signature\fP, which can only be handled by mail ** clients supporting the S/MIME extension. ** .pp ** This is a format string, see the $$smime_decrypt_command command for diff --git a/smime.c b/smime.c --- a/smime.c +++ b/smime.c @@ -51,16 +51,17 @@ #ifdef CRYPT_BACKEND_CLASSIC_SMIME #include "mutt_crypt.h" struct smime_command_context { const char *key; /* %k */ const char *cryptalg; /* %a */ + const char *digestalg; /* %d */ const char *fname; /* %f */ const char *sig_fname; /* %s */ const char *certificates; /* %c */ const char *intermediates; /* %i */ }; char SmimePass[STRING]; @@ -261,16 +262,27 @@ snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (cctx->fname)); } else if (!cctx->fname) optional = 0; break; } + case 'd': + { /* algorithm for the signature message digest */ + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%ss", prefix); + snprintf (dest, destlen, fmt, NONULL (cctx->digestalg)); + } + else if (!cctx->key) + optional = 0; + break; + } + default: *dest = '\0'; break; } if (optional) mutt_FormatString (dest, destlen, col, ifstring, _mutt_fmt_smime_command, data, 0); @@ -294,16 +306,17 @@ static pid_t smime_invoke (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *sig_fname, const char *cryptalg, + const char *digestalg, const char *key, const char *certificates, const char *intermediates, const char *format) { struct smime_command_context cctx; char cmd[HUGE_STRING]; @@ -311,16 +324,17 @@ if (!format || !*format) return (pid_t) -1; cctx.fname = fname; cctx.sig_fname = sig_fname; cctx.key = key; cctx.cryptalg = cryptalg; + cctx.digestalg = digestalg; cctx.certificates = certificates; cctx.intermediates = intermediates; mutt_smime_command (cmd, sizeof (cmd), &cctx, format); return mutt_create_filter_fd (cmd, smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd); } @@ -930,17 +944,17 @@ safe_fclose (&fperr); mutt_perror (tmpfname); return 1; } mutt_unlink (tmpfname); if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fpout), fileno (fperr), - certificate, NULL, NULL, NULL, NULL, NULL, + certificate, NULL, NULL, NULL, NULL, NULL, NULL, SmimeGetCertEmailCommand))== -1) { mutt_message (_("Error: unable to create OpenSSL subprocess!")); safe_fclose (&fperr); safe_fclose (&fpout); return 1; } @@ -1023,17 +1037,17 @@ return NULL; } /* Step 1: Convert the signature to a PKCS#7 structure, as we can't extract the full set of certificates directly. */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fpout), fileno (fperr), - infile, NULL, NULL, NULL, NULL, NULL, + infile, NULL, NULL, NULL, NULL, NULL, NULL, SmimePk7outCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); safe_fclose (&fperr); safe_fclose (&fpout); mutt_unlink (pk7out); return NULL; } @@ -1067,17 +1081,17 @@ mutt_perror (certfile); return NULL; } /* Step 2: Extract the certificates from a PKCS#7 structure. */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, fileno (fpout), fileno (fperr), - pk7out, NULL, NULL, NULL, NULL, NULL, + pk7out, NULL, NULL, NULL, NULL, NULL, NULL, SmimeGetCertCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); safe_fclose (&fperr); safe_fclose (&fpout); mutt_unlink (pk7out); mutt_unlink (certfile); return NULL; @@ -1132,17 +1146,17 @@ mutt_perror (certfile); return NULL; } /* Extract signer's certificate */ if ((thepid = smime_invoke (NULL, NULL, NULL, -1, -1, fileno (fperr), - infile, NULL, NULL, NULL, certfile, NULL, + infile, NULL, NULL, NULL, NULL, certfile, NULL, SmimeGetSignerCertCommand))== -1) { mutt_any_key_to_continue (_("Error: unable to create OpenSSL subprocess!")); safe_fclose (&fperr); safe_fclose (&fpout); mutt_unlink (pk7out); mutt_unlink (certfile); return NULL; @@ -1207,17 +1221,17 @@ mutt_endwin (NULL); if ((certfile = smime_extract_certificate(infile))) { mutt_endwin (NULL); if ((thepid = smime_invoke (&smimein, NULL, NULL, -1, fileno(fpout), fileno(fperr), - certfile, NULL, NULL, NULL, NULL, NULL, + certfile, NULL, NULL, NULL, NULL, NULL, NULL, SmimeImportCertCommand))== -1) { mutt_message (_("Error: unable to create OpenSSL subprocess!")); return; } fputs (buf, smimein); fputc ('\n', smimein); safe_fclose (&smimein); @@ -1319,28 +1333,28 @@ static pid_t smime_invoke_encrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *uids) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, smimeerrfd, - fname, NULL, SmimeCryptAlg, NULL, uids, NULL, + fname, NULL, SmimeCryptAlg, NULL, NULL, uids, NULL, SmimeEncryptCommand); } static pid_t smime_invoke_sign (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, - smimeerrfd, fname, NULL, NULL, SmimeKeyToUse, + smimeerrfd, fname, NULL, NULL, SmimeDigestAlg, SmimeKeyToUse, SmimeCertToUse, SmimeIntermediateToUse, SmimeSignCommand); } BODY *smime_build_smime_entity (BODY *a, char *certlist) @@ -1457,29 +1471,57 @@ t->unlink = 1; /*delete after sending the message */ t->parts=0; t->next=0; return (t); } +/* The openssl -md doesn't want hyphens: + * md5, sha1, sha224, sha256, sha384, sha512 + * However, the micalg does: + * md5, sha-1, sha-224, sha-256, sha-384, sha-512 + */ +static char *openssl_md_to_smime_micalg(char *md) +{ + char *micalg; + size_t l; + + if (!md) + return 0; + + if (mutt_strncasecmp("sha", md, 3) == 0) + { + l = strlen (md) + 2; + micalg = (char *)safe_malloc (l); + snprintf (micalg, l, "sha-%s", md +3); + } + else + { + micalg = safe_strdup (md); + } + + return micalg; +} + BODY *smime_sign_message (BODY *a ) { BODY *t; char buffer[LONG_STRING]; char signedfile[_POSIX_PATH_MAX], filetosign[_POSIX_PATH_MAX]; FILE *smimein = NULL, *smimeout = NULL, *smimeerr = NULL, *sfp = NULL; int err = 0; int empty = 0; pid_t thepid; smime_key_t *default_key; char *intermediates; + char *micalg; if (!SmimeDefaultKey) { mutt_error _("Can't sign: No key specified. Use Sign As."); return NULL; } convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */ @@ -1576,18 +1618,21 @@ t = mutt_new_body (); t->type = TYPEMULTIPART; t->subtype = safe_strdup ("signed"); t->encoding = ENC7BIT; t->use_disp = 0; t->disposition = DISPINLINE; mutt_generate_boundary (&t->parameter); - /* check if this can be extracted from private key somehow.... */ - mutt_set_parameter ("micalg", "sha1", &t->parameter); + + micalg = openssl_md_to_smime_micalg (SmimeDigestAlg); + mutt_set_parameter ("micalg", micalg, &t->parameter); + FREE (&micalg); + mutt_set_parameter ("protocol", "application/x-pkcs7-signature", &t->parameter); t->parts = a; a = t; t->parts->next = mutt_new_body (); t = t->parts->next; @@ -1619,28 +1664,28 @@ static pid_t smime_invoke_verify (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname, const char *sig_fname, int opaque) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, - smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL, + smimeerrfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL, (opaque ? SmimeVerifyOpaqueCommand : SmimeVerifyCommand)); } static pid_t smime_invoke_decrypt (FILE **smimein, FILE **smimeout, FILE **smimeerr, int smimeinfd, int smimeoutfd, int smimeerrfd, const char *fname) { return smime_invoke (smimein, smimeout, smimeerr, smimeinfd, smimeoutfd, - smimeerrfd, fname, NULL, NULL, SmimeKeyToUse, + smimeerrfd, fname, NULL, NULL, NULL, SmimeKeyToUse, SmimeCertToUse, NULL, SmimeDecryptCommand); } int smime_verify_one (BODY *sigbdy, STATE *s, const char *tempfile) { char signedfile[_POSIX_PATH_MAX], smimeerrfile[_POSIX_PATH_MAX];
signature.asc
Description: PGP signature
