last week i put together a few emacs functions that were
of use in composing S/MIME messages with emacs's mail programs.
i just thought it may be of interest to someone.
ps. - Note the shortcomings mentioned below.
- You need a relatively recent version of emacs since
this emacs library uses defcustom and defgroup.
- testing
- These functions have been tested mainly on NTEmacs. I
have also tried them out on a linux version of emacs and
good success. hope you have the same.
-george
---------------------- cut here -------------------------
;; tinysmime.el version 0.01
;; 4/3/00
;; Here are a few user functions for easing the generation
;; of S/MIME messages using openssl's smime program from emacs
;;
;; The user functions provided are:
;; tinysmime-encrypt
;; tinysmime-sign
;; tinysmime-decrypt
;; tinysmime-verify
;; tinysmime-encrypt-then-sign
;; tinysmime-sign-then-encrypt
;; tinysmime-decrypt-then-verify
;; tinysmime-verify-then-decrypt
;; The last two on the list are convenience functions: they envoke
;; tinysmime-verify and tinysmime-decrypt appropriately in turn.
;; Given a buffer in the ubiquitous email format -- message headers plus
;; blank line (or the "--text follows this line--" line) plus message
;; body -- these functions will work their and openssl's magic on the buffer.
;; If an arbitrary prefix argument is given to tinysmime-sign,
;; tinysmime-sign-then-encrypt, or tinysmime-encrypt-then-sign, the
;; signature generated will be opaque (ie. not detached).
;; Note: Both tinysmime-encrypt and tinysmime-sign assume the body of the
;; message to be processed is of type text/plain -- the existing
;; Content-Type of the message is ignored. This is one reason
;; tinysmime-encrypt-then-sign and tinysmime-sign-then-encrypt do not
;; simply envoke tinysmime-sign and tinysmime-encrypt in turn, that is
;; they are not convenience functions
;; The user functions here use the elisp function call-process-region
;; to pass (and read) the plaintext or ciphertext input to (and output
;; from) openssl. This has the advantage that plaintext is not
;; written to disk, but it also has the disadvantage that openssl is
;; prevented from prompting for the private key's passphrase.
;; The best workaround (of wrapping the emacs executable with a script that
;; decrypts the private key, then envokes emacs, and when emacs finishes
;; deletes the plaintext private key) still has serious shortcomings.
;; The library defines a customization group called tinysmime as follows:
;; (At a minimum the variables in the general subgroup below should be set.)
;; 1) general:
;; tinysmime-openssl-name, tinysmime-my-dir, tinysmime-my-cert-name,
;; tinysmime-private-key-name
;; 2) passed to openssl whenever it is invoked (unless nil):
;; tinysmime-rand-name
;; 3) passed to openssl only when signing (unless nil):
;; tinysmime-sign-option-nocerts, tinysmime-sign-option-noattr,
;; tinysmime-sign-certs-to-include-name
;; 4) passed to openssl only when verifying (unless nil):
;; tinysmime-verify-external-certs-name, tinysmime-verify-capath-dir
;; tinysmime-verify-cafile-name, tinysmime-verify-option-nosig
;; tinysmime-verify-option-nochain, tinysmime-verify-option-noverify
;; tinysmime-verify-option-nointern
;; 5) passed to openssl only when encrypting (unless nil):
;; tinysmime-encrypt-alg
;; 6) default list of certificates to choose from when encrypting (optional)
;; tinysmime-encrypt-cert-names
;; Installation:
;; 1) include "(load "tinysmime")" in your .emacs file
;; 2) set the custom variables you wish preset using setq in .emacs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defgroup tinysmime nil
"Functions for manipulating smime messages"
:prefix "tinysmime-"
:group 'mail)
;; primary custom options
(defcustom tinysmime-openssl-name "openssl"
"*Name of openssl executable (version 0.9.5 or greater).
Provide a fully qualified filename if executable is not in PATH
since filename will not be expanded."
:type 'string
:group 'tinysmime)
(defcustom tinysmime-my-dir ""
"*Directory of your certificates and private keys"
:type 'string
:group 'tinysmime)
(defcustom tinysmime-my-cert-name ""
"*Name of your x509 certificate"
:type 'string
:group 'tinysmime)
(defcustom tinysmime-my-private-key-name ""
"*Name of your RSA private key"
:type 'string
:group 'tinysmime)
;; extended custom options (general)
(defcustom tinysmime-rand-name nil
"*Name of file(s) to help seed the random number generator.
Use the appropriate delimiter mentioned by the openssl smime
documentation if supplying more than one file."
:type '(choice string (const nil))
:group 'tinysmime)
;; extended custom options (for signing)
(defcustom tinysmime-sign-option-nocerts nil
"*Enable the -nocerts option while signing -- causing
the signer's certificate to not be included."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-sign-option-noattr nil
"*Enable the -noattr option while signing -- causing
signing attributes to not be set."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-sign-certs-to-include-name nil
"*Name of file that has the certificate(s), beyond
the signer's certificate, which are to be included with
a message while signing"
:type '(choice string (const nil))
:group 'tinysmime)
;; extended custom options (for verifying)
(defcustom tinysmime-verify-option-nointern nil
"*Enable the -nointern option while verifying -- causing the
certificates included in the message not to be used, but
rather certificates named by tinysmime-verify-external-certs-name."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-verify-option-noverify t
"*Enable the -noverify option while verifying -- causing
the certificates themselves not to be verified."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-verify-option-nochain nil
"*Enable the -nochain option while verifying -- causes
the certificate verification not to be chained."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-verify-option-nosig nil
"*Enable the -nosig option while verifying -- causing
the message signature not to be verified."
:type 'boolean
:group 'tinysmime)
(defcustom tinysmime-verify-cafile-name nil
"*Name of file with trusted CA certificates"
:type '(choice string (const nil))
:group 'tinysmime)
(defcustom tinysmime-verify-capath-dir nil
"*Name of directory with hash of trusted CA certificates"
:type '(choice string (const nil))
:group 'tinysmime)
(defcustom tinysmime-verify-external-certs-name nil
"*Name of file that has the certificate(s) for
verifying the signature(s) on the message. See the
tinysmime-verify-option-nointern variable."
:type '(choice string (const nil))
:group 'tinysmime)
;; extended custom options (for encrypting)
(defcustom tinysmime-encrypt-alg nil
"*Select desired encryption algorithm"
:type '(choice (const nil)
(const "-des")
(const "-des3")
(const "-rc2-40")
(const "-rc2-64")
(const "-rc2-128"))
:group 'tinysmime)
(defcustom tinysmime-encrypt-cert-names nil
"*List of certificates, which will be provided as possible
completions when queried for the certificate to use to
encrypt your message."
:type '(repeat string)
:group 'tinysmime)
;; low-level non-interactive routines
(defvar tinysmime-history tinysmime-encrypt-cert-names)
(defun tinysmime-cert-prompt ()
(read-from-minibuffer "Certificate: " nil nil nil
'tinysmime-history))
(defun tinysmime-message (a)
(and a
(princ (concat (car a) " ") t)
(tinysmime-message (cdr a))))
(defun tinysmime-expand (userfile)
(expand-file-name
(substitute-in-file-name userfile)
(substitute-in-file-name tinysmime-my-dir)))
(defun tinysmime-wash-headers (min)
(goto-char (point-min))
(while (re-search-forward "^Content-" min t)
(setq min (+ min 2))
(replace-match "X-Content-"))
(goto-char (point-min))
(while (re-search-forward "^MIME-" min t)
(setq min (+ min 2))
(replace-match "X-MIME-"))
min)
(defun tinysmime-encode (arg-list &optional keep)
"setup buffer for encoding then call openssl"
(goto-char (point-min))
(let ((delim (re-search-forward "^--text follows this line--" nil t)))
(cond (delim
(beginning-of-line)
(kill-line)))
(goto-char (point-min))
(re-search-forward "^\r?$" nil t)
(beginning-of-line)
(if keep (next-line 1) (kill-line 1))
(let ((min (tinysmime-wash-headers (point))))
(goto-char min)
(tinysmime-message arg-list)
(apply 'call-process-region
min (point-max)
(substitute-in-file-name tinysmime-openssl-name)
t t nil arg-list)
(goto-char min)
(if (re-search-forward
"Loading 'screen' into random state -" (+ min 40) t)
(replace-match ""))
(goto-char (point-max))
(if (re-search-backward "done" (- (point-max) 10) t)
(replace-match ""))
(cond (delim
(goto-char (point-min))
(re-search-forward "^\r?$" nil t)
(beginning-of-line)
(kill-line)
(insert "--text follows this line--\n")))
(goto-char min))))
(defun tinysmime-decode (arg-list)
"setup buffer for decoding then call openssl"
(goto-char (point-min))
(let ((delim (re-search-forward "^--text follows this line--" nil t)))
(cond (delim
(beginning-of-line)
(kill-line)))
(goto-char (point-min))
(re-search-forward "^\r?$" nil t)
(let ((min (point)))
(copy-region-as-kill (point-min) min)
(yank)
(setq min (tinysmime-wash-headers min))
(goto-char min)
(tinysmime-message arg-list)
(apply 'call-process-region
min (point-max)
(substitute-in-file-name tinysmime-openssl-name)
t t nil arg-list)
(goto-char (point-max))
(if (re-search-backward "Verification Successful" nil t)
(replace-match ""))
(cond (delim
(goto-char (point-min))
(re-search-forward "^\r?$" nil t)
(beginning-of-line)
(kill-line)
(insert "--text follows this line--\n")))
(goto-char min))))
(defun tinysmime-params-verify ()
(append (list "smime" "-verify")
(if tinysmime-rand-name
(list "-rand" (tinysmime-expand tinysmime-rand-name)))
(if tinysmime-verify-option-nosig '("-nosig"))
(if tinysmime-verify-option-noverify '("-noverify"))
(if tinysmime-verify-option-nochain '("-nochain"))
(if tinysmime-verify-option-nointern '("-nointern"))
(if tinysmime-verify-external-certs-name
(list "-certfile"
(tinysmime-expand tinysmime-verify-external-certs-name)))
(if tinysmime-verify-capath-dir
(list "-CApath" (expand-file-name (substitute-in-file-name
tinysmime-verify-capath-dir))))
(if tinysmime-verify-cafile-name
(list "-CAfile"
(tinysmime-expand tinysmime-verify-cafile-name)))))
(defun tinysmime-params-decrypt ()
(append (list "smime" "-decrypt"
"-inkey" (tinysmime-expand tinysmime-my-private-key-name)
"-recip" (tinysmime-expand tinysmime-my-cert-name))
(if tinysmime-rand-name
(list "-rand" (tinysmime-expand tinysmime-rand-name)))))
(defun tinysmime-params-encrypt (&optional notext)
(append (list "smime" "-encrypt")
(if tinysmime-rand-name
(list "-rand" (tinysmime-expand tinysmime-rand-name)))
(if tinysmime-encrypt-alg (list tinysmime-encrypt-alg))
(if (not notext) '("-text"))
(list (tinysmime-expand (tinysmime-cert-prompt)))))
(defun tinysmime-params-sign (nodetach &optional notext)
(append (list "smime" "-sign"
"-signer" (tinysmime-expand tinysmime-my-cert-name)
"-inkey" (tinysmime-expand tinysmime-my-private-key-name))
(if tinysmime-rand-name (list "-rand" tinysmime-rand-name))
(if tinysmime-sign-option-nocerts '("-nocerts"))
(if tinysmime-sign-option-noattr '("-noattr"))
(if tinysmime-sign-certs-to-include-name
(list "-certfile"
(tinysmime-expand tinysmime-sign-certs-to-include-name)))
(if (not notext) '("-text"))
(if nodetach '("-nodetach"))))
;; interactive commands (encrypting/signing)
(defun tinysmime-encrypt ()
"s/mime encrypt the current buffer"
(interactive)
(tinysmime-encode (tinysmime-params-encrypt)))
(defun tinysmime-sign (&optional nodetach)
"s/mime sign the current buffer"
(interactive "P")
(tinysmime-encode (tinysmime-params-sign nodetach)))
(defun tinysmime-sign-then-encrypt (&optional nodetach)
"s/mime sign and then encrypt the current buffer"
(interactive "P")
(tinysmime-encode (tinysmime-params-sign nodetach) t)
(tinysmime-encode (tinysmime-params-encrypt t)))
(defun tinysmime-encrypt-then-sign (&optional nodetach)
"s/mime encrypt and then sign the current buffer"
(interactive "P")
(tinysmime-encode (tinysmime-params-encrypt) t)
(tinysmime-encode (tinysmime-params-sign nodetach t)))
;; interactive commands (decrypting/verifying)
(defun tinysmime-decrypt ()
"s/mime decrypt the current buffer"
(interactive)
(tinysmime-decode (tinysmime-params-decrypt)))
(defun tinysmime-verify ()
"s/mime verify the current buffer"
(interactive)
(tinysmime-decode (tinysmime-params-verify)))
;; interactive commands (convenience routines)
(defun tinysmime-verify-then-decrypt ()
"s/mime verify and then decrypt the current buffer"
(interactive)
(tinysmime-decode (tinysmime-params-verify))
(tinysmime-decode (tinysmime-params-decrypt)))
(defun tinysmime-decrypt-then-verify ()
"s/mime decrypt and then verify the current buffer"
(interactive)
(tinysmime-decode (tinysmime-params-decrypt))
(tinysmime-decode (tinysmime-params-verify)))
smime.p7s