]> granicus.if.org Git - neomutt/commitdiff
Start cleaning up and fixing smime_keys.pl (closes #3324) (see #2456)
authorKevin McCarthy <kevin@8t8.us>
Fri, 15 May 2015 17:47:38 +0000 (10:47 -0700)
committerKevin McCarthy <kevin@8t8.us>
Fri, 15 May 2015 17:47:38 +0000 (10:47 -0700)
* Convert to using File::Temp (#3324).  This was also suggested at
  https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=775199

* Use File::Temp for add_p12 temp file. (#2456)

* Make the query_label() method a bit more robust with empty strings,
  ctrl-d, and leading spaces.

* Clean up openssl_do_verify() logic.  Mark cert as invalid
  rather that die'ing if an openssl verify command fails.

* General cleanup:
  - Clearly separate op handler, certificate management, and helper
    functions by section and using prefixes.
  - Create openssl helper functions to reduce copy/paste invocations
    and make the code clearer.
  - Make indentation consistent at 2 spaces.
  - Change handle_add_pem() to re-use handle_add_chain() once the
    correct files are identified.
  - Change openssl_parse_pem() to return a single array of data
    structures representing the parsed certs/keys.

smime_keys.pl

index a82535883aa820c8e9f17b607512ad56a40c1dc4..7001bdddfb38e785af5d602b12d5945a51312a83 100755 (executable)
@@ -8,12 +8,12 @@
 #     it under the terms of the GNU General Public License as published by
 #     the Free Software Foundation; either version 2 of the License, or
 #     (at your option) any later version.
-# 
+#
 #     This program is distributed in the hope that it will be useful,
 #     but WITHOUT ANY WARRANTY; without even the implied warranty of
 #     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #     GNU General Public License for more details.
-# 
+#
 #     You should have received a copy of the GNU General Public License
 #     along with this program; if not, write to the Free Software
 #     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 use strict;
 use File::Copy;
 use File::Glob ':glob';
+use File::Temp qw(tempfile tempdir);
 
 umask 077;
 
 use Time::Local;
 
+# helper routines
 sub usage ();
-sub newfile ($;$$);
-sub mutt_Q ($ );
+sub mutt_Q ($);
 sub mycopy ($$);
-
-#  directory setup routines
-sub mkdir_recursive ($ );
-sub init_paths ();
+sub query_label ();
+sub mkdir_recursive ($);
+sub verify_files_exist (@);
+sub create_tempfile (;$);
+sub new_cert_structure ();
+
+# openssl helpers
+sub openssl_format ($);
+sub openssl_hash ($);
+sub openssl_fingerprint ($);
+sub openssl_emails ($);
+sub openssl_do_verify($$$);
+sub openssl_parse_pem ($$);
 
 # key/certificate management methods
-sub list_certs ();
-sub query_label ();
-sub add_entry ($$$$$ );
-sub add_certificate ($$$$;$ );
-sub add_key ($$$$);
-sub add_root_cert ($ );
-sub parse_pem (@ );
-sub handle_pem (@ );
-sub modify_entry ($$$;$ );
-sub remove_pair ($ );
-sub change_label ($ );
-sub verify_cert($$);
-sub do_verify($$$ );
-              
-# Get the directories mutt uses for certificate/key storage.
+sub cm_list_certs ();
+sub cm_add_entry ($$$$$);
+sub cm_add_certificate ($$$$;$);
+sub cm_add_key ($$$$);
+sub cm_modify_entry ($$$;$);
+
+# op handlers
+sub handle_init_paths ();
+sub handle_change_label ($);
+sub handle_add_cert ($);
+sub handle_add_pem ($);
+sub handle_add_p12 ($);
+sub handle_add_chain ($$$);
+sub handle_verify_cert($$);
+sub handle_remove_pair ($);
+sub handle_add_root_cert ($);
+
 
 my $mutt = $ENV{MUTT_CMDLINE} || 'mutt';
 my $opensslbin = "/usr/bin/openssl";
-my @tempfiles = ();
-my @cert_tmp_file = ();
-
 my $tmpdir;
+
+# Get the directories mutt uses for certificate/key storage.
+
 my $private_keys_path = mutt_Q 'smime_keys';
 die "smime_keys is not set in mutt's configuration file"
-       if length $private_keys_path == 0;
+   if length $private_keys_path == 0;
 
 my $certificates_path = mutt_Q 'smime_certificates';
 die "smime_certificates is not set in mutt's configuration file"
-       if length $certificates_path == 0;
+   if length $certificates_path == 0;
+
 my $root_certs_path   = mutt_Q 'smime_ca_location';
 die "smime_ca_location is not set in mutt's configuration file"
-       if length $root_certs_path == 0;
+   if length $root_certs_path == 0;
 
 my $root_certs_switch;
 if ( -d $root_certs_path) {
-       $root_certs_switch = -CApath;
+  $root_certs_switch = -CApath;
 } else {
-       $root_certs_switch = -CAfile;
+  $root_certs_switch = -CAfile;
 }
 
 
-#
+######
 # OPS
-#
+######
 
 if(@ARGV == 1 and $ARGV[0] eq "init") {
-    init_paths;
+  handle_init_paths();
 }
 elsif(@ARGV == 1 and $ARGV[0] eq "list") {
-    list_certs;
+  cm_list_certs();
 }
 elsif(@ARGV == 2 and $ARGV[0] eq "label") {
-    change_label($ARGV[1]);
+  handle_change_label($ARGV[1]);
 }
 elsif(@ARGV == 2 and $ARGV[0] eq "add_cert") {
-    my $format = -B $ARGV[1] ? 'DER' : 'PEM'; 
-    my $cmd = "$opensslbin x509 -noout -hash -in $ARGV[1] -inform $format";
-    my $cert_hash = `$cmd`;
-    $? and die "'$cmd' returned $?";
-    chomp($cert_hash); 
-    my $label = query_label;
-    &add_certificate($ARGV[1], \$cert_hash, 1, $label, '?');
+  verify_files_exist($ARGV[1]);
+  handle_add_cert($ARGV[1]);
 }
 elsif(@ARGV == 2 and $ARGV[0] eq "add_pem") {
-    -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
-    open(PEM_FILE, "<$ARGV[1]") or die("Can't open $ARGV[1]: $!");
-    my @pem = <PEM_FILE>;
-    close(PEM_FILE);
-    handle_pem(@pem);
+  verify_files_exist($ARGV[1]);
+  handle_add_pem($ARGV[1]);
 }
 elsif( @ARGV == 2 and $ARGV[0] eq "add_p12") {
-    -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
-
-    print "\nNOTE: This will ask you for two passphrases:\n";
-    print "       1. The passphrase you used for exporting\n";
-    print "       2. The passphrase you wish to secure your private key with.\n\n";
-
-    my $pem_file = "$ARGV[1].pem";
-    
-    my $cmd = "$opensslbin pkcs12 -in $ARGV[1] -out $pem_file";
-    system $cmd and die "'$cmd' returned $?";
-    
-    -e $pem_file and -s $pem_file or die("Conversion of $ARGV[1] failed.");
-    open(PEM_FILE, $pem_file) or die("Can't open $pem_file: $!");
-    my @pem = <PEM_FILE>;
-    close(PEM_FILE);
-    unlink $pem_file;
-    handle_pem(@pem);
+  verify_files_exist($ARGV[1]);
+  handle_add_p12($ARGV[1]);
 }
 elsif(@ARGV == 4 and $ARGV[0] eq "add_chain") {
-    my $mailbox;
-    my $format = -B $ARGV[2] ? 'DER' : 'PEM'; 
-    my $cmd = "$opensslbin x509 -noout -hash -in $ARGV[2] -inform $format";
-    my $cert_hash = `$cmd`;
-
-    $? and die "'$cmd' returned $?";
-
-    $format = -B $ARGV[3] ? 'DER' : 'PEM'; 
-
-    $cmd = "$opensslbin x509 -noout -hash -in $ARGV[3] -inform $format";
-    my $issuer_hash = `$cmd`;
-    $? and die "'$cmd' returned $?";
-    
-    chomp($cert_hash); 
-    chomp($issuer_hash);
-
-    my $label = query_label;
-    
-    add_certificate($ARGV[3], \$issuer_hash, 0, $label); 
-    my @mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash);
-    
-    foreach $mailbox (@mailbox) {
-      chomp($mailbox);
-      add_key($ARGV[1], $cert_hash, $mailbox, $label);
-    }
+  verify_files_exist($ARGV[1], $ARGV[2], $ARGV[3]);
+  handle_add_chain($ARGV[1], $ARGV[2], $ARGV[3]);
 }
 elsif((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") {
-    verify_cert($ARGV[1], $ARGV[2]);
+  verify_files_exist($ARGV[2]) if (@ARGV == 3);
+  handle_verify_cert($ARGV[1], $ARGV[2]);
 }
 elsif(@ARGV == 2 and $ARGV[0] eq "remove") {
-    remove_pair($ARGV[1]);
+  handle_remove_pair($ARGV[1]);
 }
 elsif(@ARGV == 2 and $ARGV[0] eq "add_root") {
-    add_root_cert($ARGV[1]);
+  verify_files_exist($ARGV[1]);
+  handle_add_root_cert($ARGV[1]);
 }
-else {    
-    usage;
-    exit(1);
+else {
+  usage();
+  exit(1);
 }
 
 exit(0);
 
 
+##############  sub-routines  ########################
 
 
-
-##############  sub-routines  ########################
+###################
+#  helper routines
+###################
 
 sub usage () {
     print <<EOF;
@@ -207,87 +178,336 @@ EOF
 }
 
 sub mutt_Q ($) {
-    my $var = shift or die;
+  my ($var) = @_;
 
-    my $cmd = "$mutt -v >/dev/null 2>/dev/null";
-    system ($cmd) == 0 
-        or die<<EOF;
+  my $cmd = "$mutt -v >/dev/null 2>/dev/null";
+  system ($cmd) == 0 or die<<EOF;
 Couldn't launch mutt. I attempted to do so by running the command "$mutt".
-If that's not the right command, you can override it by setting the 
+If that's not the right command, you can override it by setting the
 environment variable \$MUTT_CMDLINE
 EOF
 
-    $cmd = "$mutt -Q $var 2>/dev/null";
-    my $answer = `$cmd`;
+  $cmd = "$mutt -Q $var 2>/dev/null";
+  my $answer = `$cmd`;
 
-    $? and die<<EOF;
-Couldn't look up the value of the mutt variable "$var". 
+  $? and die<<EOF;
+Couldn't look up the value of the mutt variable "$var".
 You must set this in your mutt config file. See contrib/smime.rc for an example.
 EOF
-#'
 
-    $answer =~ /\"(.*?)\"/ and return bsd_glob($1, GLOB_TILDE | GLOB_NOCHECK);
-    
-    $answer =~ /^Mutt (.*?) / and die<<EOF;
+  $answer =~ /\"(.*?)\"/ and return bsd_glob($1, GLOB_TILDE | GLOB_NOCHECK);
+
+  $answer =~ /^Mutt (.*?) / and die<<EOF;
 This script requires mutt 1.5.0 or later. You are using mutt $1.
 EOF
-    
-    die "Value of $var is weird\n";
+
+  die "Value of $var is weird\n";
 }
 
 sub mycopy ($$) {
-    my $source = shift or die;
-    my $dest = shift or die;
+  my ($source, $dest) = @_;
 
-    copy $source, $dest or die "Problem copying $source to $dest: $!\n";
+  copy $source, $dest or die "Problem copying $source to $dest: $!\n";
 }
 
-#
-#  directory setup routines
-#
+sub query_label () {
+  my $input;
+  my $label;
+  my $junk;
+
+  print "\nYou may assign a label to this key, so you don't have to remember\n";
+  print "the key ID. This has to be _one_ word (no whitespaces).\n\n";
+
+  print "Enter label: ";
+  $input = <STDIN>;
 
+  if (defined($input) && ($input !~ /^\s*$/)) {
+    chomp($input);
+    $input =~ s/^\s+//;
+    ($label, $junk) = split(/\s/, $input, 2);
+
+    if (defined($junk)) {
+      print "\nUsing '$label' as label; ignoring '$junk'\n";
+    }
+  }
+
+  if ((! defined($label)) || ($label =~ /^\s*$/)) {
+    $label =  "-";
+  }
+
+  return $label;
+}
 
 sub mkdir_recursive ($) {
-    my $path = shift or die;
-    my $tmp_path;
-    
-    for my $dir (split /\//, $path) {
-        $tmp_path .= "$dir/";
-
-        -d $tmp_path 
-            or mkdir $tmp_path, 0700
-                or die "Can't mkdir $tmp_path: $!";
+  my ($path) = @_;
+  my $tmp_path;
+
+  for my $dir (split /\//, $path) {
+    $tmp_path .= "$dir/";
+
+    -d $tmp_path
+      or mkdir $tmp_path, 0700
+        or die "Can't mkdir $tmp_path: $!";
+  }
+}
+
+sub verify_files_exist (@) {
+  my (@files) = @_;
+
+  foreach my $file (@files) {
+    if ((! -e $file) || (! -s $file)) {
+      die("$file is nonexistent or empty.");
     }
+  }
 }
 
-sub init_paths () {
-    mkdir_recursive($certificates_path);
-    mkdir_recursive($private_keys_path);
+# Returns a list ($fh, $filename)
+sub create_tempfile (;$) {
+  my ($directory) = @_;
+
+  if (! defined($directory)) {
+    if (! defined($tmpdir)) {
+      $tmpdir = tempdir(CLEANUP => 1);
+    }
+    $directory = $tmpdir;
+  }
 
-    my $file;
+  return tempfile(DIR => $directory);
+}
 
-    $file = $certificates_path . "/.index";
-    -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
-        or die "Can't touch $file: $!";
+# Creates a cert data structure used by openssl_parse_pem
+sub new_cert_structure () {
+  my $cert_data = {};
 
-    $file = $private_keys_path . "/.index";
-    -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
-        or die "Can't touch $file: $!";
+  $cert_data->{datafile} = "";
+  $cert_data->{type} = "";
+  $cert_data->{localKeyID} = "";
+  $cert_data->{subject} = "";
+  $cert_data->{issuer} = "";
+
+  return $cert_data;
 }
 
 
+##################
+# openssl helpers
+##################
 
-#
+sub openssl_format ($) {
+  my ($filename) = @_;
+
+  return -B $filename ? 'DER' : 'PEM';
+}
+
+sub openssl_hash ($) {
+  my ($filename) = @_;
+
+  my $format = openssl_format($filename);
+  my $cmd = "$opensslbin x509 -noout -hash -in $filename -inform $format";
+  my $cert_hash = `$cmd`;
+  $? and die "'$cmd' returned $?";
+
+  chomp($cert_hash);
+  return $cert_hash;
+}
+
+sub openssl_fingerprint ($) {
+  my ($filename) = @_;
+
+  my $format = openssl_format($filename);
+  my $cmd = "$opensslbin x509 -in $filename -inform $format -fingerprint -noout";
+  my $fingerprint = `$cmd`;
+  $? and die "'$cmd' returned $?";
+
+  chomp($fingerprint);
+  return $fingerprint;
+}
+
+sub openssl_emails ($) {
+  my ($filename) = @_;
+
+  my $format = openssl_format($filename);
+  my $cmd = "$opensslbin x509 -in $filename -inform $format -email -noout";
+  my @mailboxes = `$cmd`;
+  $? and die "'$cmd' returned $?";
+
+  chomp(@mailboxes);
+  return @mailboxes;
+}
+
+sub openssl_do_verify ($$$) {
+  my ($cert, $issuerid, $crl) = @_;
+
+  my $result = 't';
+  my $issuer_path;
+  my $cert_path = "$certificates_path/$cert";
+
+  if($issuerid eq '?') {
+    $issuer_path = "$certificates_path/$cert";
+  } else {
+    $issuer_path = "$certificates_path/$issuerid";
+  }
+
+  my $cmd = "$opensslbin verify $root_certs_switch $root_certs_path -purpose smimesign -purpose smimeencrypt -untrusted $issuer_path $cert_path";
+  my $output = `$cmd`;
+  chomp $output;
+  if ($?) {
+    print "'$cmd' returned exit code " . ($? >> 8) . " with output:\n";
+    print "$output\n\n";
+    print "Marking as invalid\n";
+    return 'i';
+  }
+  print "\n$output\n";
+
+  if ($output !~ /OK/) {
+    return 'i';
+  }
+
+  my $format = openssl_format($cert_path);
+  $cmd = "$opensslbin x509 -dates -serial -noout -in $cert_path -inform $format";
+  (my $not_before, my $not_after, my $serial_in) = `$cmd`;
+  $? and die "'$cmd' returned $?";
+
+  if ( defined $not_before and defined $not_after ) {
+    my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03',
+                  'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07',
+                  'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11');
+
+    my @tmp = split (/\=/, $not_before);
+    my $not_before_date = $tmp[1];
+    my @fields =
+      $not_before_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
+    if ($#fields == 5) {
+      if (timegm($fields[4], $fields[3], $fields[2], $fields[1],
+                 $months{$fields[0]}, $fields[5]) > time) {
+        print "Certificate is not yet valid.\n";
+        return 'e';
+      }
+    } else {
+      print "Expiration Date: Parse Error :  $not_before_date\n\n";
+    }
+
+    @tmp = split (/\=/, $not_after);
+    my $not_after_date = $tmp[1];
+    @fields =
+      $not_after_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
+    if ($#fields == 5) {
+      if (timegm($fields[4], $fields[3], $fields[2], $fields[1],
+                 $months{$fields[0]}, $fields[5]) < time) {
+        print "Certificate has expired.\n";
+        return 'e';
+      }
+    } else {
+      print "Expiration Date: Parse Error :  $not_after_date\n\n";
+    }
+  }
+
+  if ( defined $crl ) {
+    my @serial = split (/\=/, $serial_in);
+    my $cmd = "$opensslbin crl -text -noout -in $crl | grep -A1 $serial[1]";
+    (my $l1, my $l2) = `$cmd`;
+    $? and die "'$cmd' returned $?";
+
+    if ( defined $l2 ) {
+      my @revoke_date = split (/:\s/, $l2);
+      print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n";
+      $result = 'r';
+    }
+  }
+  print "\n";
+
+  return $result;
+}
+
+sub openssl_parse_pem ($$) {
+  my ($filename, $attrs_required) = @_;
+
+  my $state = 0;
+  my $cert_data;
+  my @certs;
+  my $cert_count = 0;
+  my $bag_count = 0;
+  my $cert_tmp_fh;
+  my $cert_tmp_filename;
+
+  $cert_data = new_cert_structure();
+  ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile();
+
+  open(PEM_FILE, "<$filename") or die("Can't open $filename: $!");
+  while(<PEM_FILE>) {
+    if(/^Bag Attributes/) {
+      $bag_count++;
+      $state == 0 or  die("PEM-parse error at: $.");
+      $state = 1;
+    }
+
+    if ($state == 1) {
+      if (/localKeyID:\s*(.*)/) {
+        $cert_data->{localKeyID} = $1;
+      }
+
+      if (/subject=\s*(.*)/) {
+        $cert_data->{subject} = $1;
+      }
+
+      if (/issuer=\s*(.*)/) {
+        $cert_data->{issuer} = $1;
+      }
+    }
+
+
+    if(/^-----/) {
+      if(/BEGIN/) {
+        print $cert_tmp_fh $_;
+        $state = 2;
+
+        if(/PRIVATE/) {
+            $cert_data->{type} = "K";
+            next;
+        }
+        if(/CERTIFICATE/) {
+            $cert_data->{type} = "C";
+            next;
+        }
+        die("What's this: $_");
+      }
+      if(/END/) {
+        $state = 0;
+        print $cert_tmp_fh $_;
+        close($cert_tmp_fh);
+
+        $cert_count++;
+        push (@certs, $cert_data);
+
+        $cert_data = new_cert_structure();
+        ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile();
+        next;
+      }
+    }
+    print $cert_tmp_fh $_;
+  }
+  close($cert_tmp_fh);
+  close(PEM_FILE);
+
+  if ($attrs_required && ($bag_count != $cert_count)) {
+    die("Not all contents were bagged. can't continue.");
+  }
+
+  return @certs;
+}
+
+
+#################################
 # certificate management methods
-#
+#################################
 
-sub list_certs () {
+sub cm_list_certs () {
   my %keyflags = ( 'i', '(Invalid)',  'r', '(Revoked)', 'e', '(Expired)',
-                  'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)');
+                   'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)');
 
-  open(INDEX, "<$certificates_path/.index") or 
+  open(INDEX, "<$certificates_path/.index") or
     die "Couldn't open $certificates_path/.index: $!";
-  
+
   print "\n";
   while(<INDEX>) {
     my $tmp;
@@ -308,7 +528,7 @@ sub list_certs () {
             die "Couldn't open $certfile: $!";
         local $/;
         $cert = <F>;
-       close F;
+        close F;
     }
 
     my $subject_in;
@@ -316,7 +536,7 @@ sub list_certs () {
     my $date1_in;
     my $date2_in;
 
-    my $format = -B $certfile ? 'DER' : 'PEM'; 
+    my $format = openssl_format($certfile);
     my $cmd = "$opensslbin x509 -subject -issuer -dates -noout -in $certfile -inform $format";
     ($subject_in, $issuer_in, $date1_in, $date2_in) = `$cmd`;
     $? and print "ERROR: '$cmd' returned $?\n\n" and next;
@@ -345,13 +565,13 @@ sub list_certs () {
       $tmp = $tmp[1];
       @tmp = split (/\=/, $date2_in);
       print $tab."Certificate is not valid before $tmp".
-       $tab."                      or after  ".$tmp[1];
+        $tab."                      or after  ".$tmp[1];
     }
 
     -e "$private_keys_path/$fields[1]" and
       print "$tab - Matching private key installed -\n";
 
-    $format = -B "$certificates_path/$fields[1]" ? 'DER' : 'PEM'; 
+    $format = openssl_format("$certificates_path/$fields[1]");
     $cmd = "$opensslbin x509 -purpose -noout -in $certfile -inform $format";
     my $purpose_in = `$cmd`;
     $? and die "'$cmd' returned $?";
@@ -367,428 +587,175 @@ sub list_certs () {
 
     print "\n";
   }
-  
-  close(INDEX);
-}
 
-
-
-sub query_label () {
-    my @words;
-    my $input;
-
-    print "\nYou may assign a label to this key, so you don't have to remember\n";
-    print "the key ID. This has to be _one_ word (no whitespaces).\n\n";
-
-    print "Enter label: ";
-    chomp($input = <STDIN>);
-
-    my ($label, $junk) = split(/\s/, $input, 2);     
-    
-    defined $junk 
-        and print "\nUsing '$label' as label; ignoring '$junk'\n";
-
-    defined $label || ($label =  "-");
-
-    return $label;
+  close(INDEX);
 }
 
+sub cm_add_entry ($$$$$) {
+  my ($mailbox, $hashvalue, $use_cert, $label, $issuer_hash) = @_;
 
+  my @fields;
 
-sub add_entry ($$$$$) {
-    my $mailbox = shift or die;
-    my $hashvalue = shift or die;
-    my $use_cert = shift;
-    my $label = shift or die;
-    my $issuer_hash = shift;
-
-    my @fields;
-
-    if ($use_cert) {
-        open(INDEX, "+<$certificates_path/.index") or 
-            die "Couldn't open $certificates_path/.index: $!";
-    }
-    else {
-        open(INDEX, "+<$private_keys_path/.index") or 
-            die "Couldn't open $private_keys_path/.index: $!";
-    }
+  if ($use_cert) {
+    open(INDEX, "+<$certificates_path/.index") or
+        die "Couldn't open $certificates_path/.index: $!";
+  }
+  else {
+    open(INDEX, "+<$private_keys_path/.index") or
+        die "Couldn't open $private_keys_path/.index: $!";
+  }
 
-    while(<INDEX>) {
-        @fields = split;
-        return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue);
-    }
+  while(<INDEX>) {
+    @fields = split;
+    return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue);
+  }
 
-    if ($use_cert) {
-        print INDEX "$mailbox $hashvalue $label $issuer_hash u\n";
-    }
-    else {
-        print INDEX "$mailbox $hashvalue $label \n";
-    }
+  if ($use_cert) {
+    print INDEX "$mailbox $hashvalue $label $issuer_hash u\n";
+  }
+  else {
+    print INDEX "$mailbox $hashvalue $label \n";
+  }
 
-    close(INDEX);
+  close(INDEX);
 }
 
+sub cm_add_certificate ($$$$;$) {
+  my ($filename, $hashvalue, $add_to_index, $label, $issuer_hash) = @_;
 
-sub add_certificate ($$$$;$) {
-    my $filename = shift or die;
-    my $hashvalue = shift or die;
-    my $add_to_index = shift;
-    my $label = shift or die;
-    my $issuer_hash = shift;
+  my $iter = 0;
+  my @mailboxes;
 
-    my $iter = 0;
-    my @mailbox;
-    my $mailbox;
+  my $fp1 = openssl_fingerprint($filename);
 
-    while(-e "$certificates_path/$$hashvalue.$iter") {
-        my ($t1, $t2);
-        my $format = -B $filename ? 'DER' : 'PEM'; 
-        my $cmd = "$opensslbin x509 -in $filename -inform $format -fingerprint -noout";
-        $t1 = `$cmd`;
-        $? and die "'$cmd' returned $?";
+  while (-e "$certificates_path/$$hashvalue.$iter") {
+    my $fp2 = openssl_fingerprint("$certificates_path/$$hashvalue.$iter");
 
-        $format = -B "$certificates_path/$$hashvalue.$iter" ? 'DER' : 'PEM'; 
-        $cmd = "$opensslbin x509 -in $certificates_path/$$hashvalue.$iter -inform $format -fingerprint -noout";
-        $t2 = `$cmd`;
-        $? and die "'$cmd' returned $?";
-        
-        $t1 eq $t2 and last;
+    last if $fp1 eq $fp2;
+    $iter++;
+  }
+  $$hashvalue .= ".$iter";
 
-        $iter++;
-    }
-    $$hashvalue .= ".$iter";
-    
-    if (-e "$certificates_path/$$hashvalue") {
-            print "\nCertificate: $certificates_path/$$hashvalue already installed.\n";
+  if (-e "$certificates_path/$$hashvalue") {
+    print "\nCertificate: $certificates_path/$$hashvalue already installed.\n";
+  }
+  else {
+    mycopy $filename, "$certificates_path/$$hashvalue";
+
+    if ($add_to_index) {
+      @mailboxes = openssl_emails($filename);
+      foreach my $mailbox (@mailboxes) {
+        cm_add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
+        print "\ncertificate $$hashvalue ($label) for $mailbox added.\n";
+      }
+      cm_modify_entry('V', $$hashvalue, 1);
     }
     else {
-        mycopy $filename, "$certificates_path/$$hashvalue";
-
-        if ($add_to_index) {
-            my $format = -B $filename ? 'DER' : 'PEM'; 
-           my $cmd = "$opensslbin x509 -in $filename -inform $format -email -noout";
-           @mailbox = `$cmd`;
-           $? and die "'$cmd' returned $?";
-
-           foreach $mailbox (@mailbox) {
-             chomp($mailbox);
-             add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
-
-             print "\ncertificate $$hashvalue ($label) for $mailbox added.\n";
-           }
-           verify_cert($$hashvalue, undef);
-        }
-        else {
-            print "added certificate: $certificates_path/$$hashvalue.\n";
-        }
+      print "added certificate: $certificates_path/$$hashvalue.\n";
     }
+  }
 
-    return @mailbox;
+  return @mailboxes;
 }
 
-
-sub add_key ($$$$) {
-    my $file = shift or die;
-    my $hashvalue = shift or die;
-    my $mailbox = shift or die;
-    my $label = shift or die;
+sub cm_add_key ($$$$) {
+    my ($file, $hashvalue, $mailbox, $label) = @_;
 
     unless (-e "$private_keys_path/$hashvalue") {
         mycopy $file, "$private_keys_path/$hashvalue";
-    }    
+    }
 
-    add_entry($mailbox, $hashvalue, 0, $label, "");
+    cm_add_entry($mailbox, $hashvalue, 0, $label, "");
     print "added private key: " .
       "$private_keys_path/$hashvalue for $mailbox\n";
-} 
-
-
-
-
-
-
-sub parse_pem (@) {
-    my $state = 0;
-    my $cert_iter = 0;
-    my @bag_attribs;
-    my $numBags = 0;
-
-    $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp");
-    my $cert_tmp_iter = $cert_tmp_file[$cert_iter];
-    open(CERT_FILE, ">$cert_tmp_iter") 
-        or die "Couldn't open $cert_tmp_iter: $!";
-
-    while($_ = shift(@_)) {
-        if(/^Bag Attributes/) {
-            $numBags++;
-            $state == 0 or  die("PEM-parse error at: $.");
-           $state = 1;
-            $bag_attribs[$cert_iter*4+1] = "";
-            $bag_attribs[$cert_iter*4+2] = "";
-            $bag_attribs[$cert_iter*4+3] = "";
-        }
-
-        ($state == 1) and /localKeyID:\s*(.*)/ 
-            and ($bag_attribs[$cert_iter*4+1] = $1);
-
-        ($state == 1) and /subject=\s*(.*)/    
-            and ($bag_attribs[$cert_iter*4+2] = $1);
-
-        ($state == 1) and /issuer=\s*(.*)/     
-            and ($bag_attribs[$cert_iter*4+3] = $1);
-        
-        if(/^-----/) {
-            if(/BEGIN/) {
-                print CERT_FILE;
-                $state = 2;
-
-                if(/PRIVATE/) {
-                    $bag_attribs[$cert_iter*4] = "K";
-                    next;
-                }
-                if(/CERTIFICATE/) {
-                    $bag_attribs[$cert_iter*4] = "C";
-                    next;
-                }
-                die("What's this: $_");
-            }
-            if(/END/) {
-                $state = 0;
-                print CERT_FILE;
-                close(CERT_FILE);
-                $cert_iter++;
-               $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp");
-               $cert_tmp_iter = $cert_tmp_file[$cert_iter];
-                open(CERT_FILE, ">$cert_tmp_iter")
-                    or die "Couldn't open $cert_tmp_iter: $!";
-                next;
-            }
-        }
-        print CERT_FILE;
-    }
-    close(CERT_FILE);
-
-    # I'll add support for unbagged cetificates, in case this is needed.
-    $numBags == $cert_iter or 
-        die("Not all contents were bagged. can't continue.");
-
-    return @bag_attribs;
 }
 
+sub cm_modify_entry ($$$;$) {
+  my ($op, $hashvalue, $use_cert, $opt_param) = @_;
 
-# This requires the Bag Attributes to be set
-sub handle_pem (@) {
-
-    my @pem_contents;
-    my $iter=0;
-    my $root_cert;
-    my $key;
-    my $certificate;
-    my $intermediate;
-    my @mailbox;
-    my $mailbox;
-
-    @pem_contents = &parse_pem(@_);
+  my $crl;
+  my $label;
+  my $path;
+  my @fields;
 
-    # private key and certificate use the same 'localKeyID'
-    while($iter <= $#pem_contents / 4) {
-        if($pem_contents[$iter * 4] eq "K") {
-            $key = $iter;
-            last;
-        }
-        $iter++;
-    }
-    ($iter > $#pem_contents / 2) and die("Couldn't find private key!");
+  $op eq 'L' and ($label = $opt_param);
+  $op eq 'V' and ($crl = $opt_param);
 
-    $pem_contents[($key * 4)+1] or die("Attribute 'localKeyID' wasn't set.");
 
-    $iter = 0;
-    while($iter <= $#pem_contents / 4) {
-        $iter == $key and ($iter++) and next;
-        if($pem_contents[($iter * 4)+1] eq $pem_contents[($key * 4)+1]) {
-            $certificate = $iter;
-            last;
-        }
-        $iter++;
-    }
-    ($iter > $#pem_contents / 4) and die("Couldn't find matching certificate!");
+  if ($use_cert) {
+      $path = $certificates_path;
+  }
+  else {
+      $path = $private_keys_path;
+  }
 
-    my $tmp_key = newfile("tmp_key","temp");
-    mycopy $cert_tmp_file[$key], $tmp_key;
-    my $tmp_certificate = newfile("tmp_certificate","temp");
-    mycopy $cert_tmp_file[$certificate], $tmp_certificate;
+  open(INDEX, "<$path/.index") or
+    die "Couldn't open $path/.index: $!";
+  my ($newindex_fh, $newindex) = create_tempfile();
 
-    # root certificate is self signed
-    $iter = 0;
+  while(<INDEX>) {
+    @fields = split;
+    if($fields[1] eq $hashvalue or $hashvalue eq 'all') {
+      $op eq 'R' and next;
 
-    while($iter <= $#pem_contents / 4) {
-        if ($iter == $key or $iter == $certificate) {
-            $iter++; 
-            next;
-        }
+      print $newindex_fh "$fields[0] $fields[1]";
 
-        if($pem_contents[($iter * 4)+2] eq $pem_contents[($iter * 4)+3]) {
-            $root_cert = $iter;
-            last;
+      if($op eq 'L') {
+        if($use_cert) {
+          print $newindex_fh " $label $fields[3] $fields[4]";
         }
-        $iter++;
-    }
-    if ($iter > $#pem_contents / 4) {
-      print "Couldn't identify root certificate!\n";
-      $root_cert = -1;      
-    }
-
-    # what's left are intermediate certificates.
-    $iter = 0;
-
-    # needs to be set, so we can check it later
-    $intermediate = $root_cert;
-    my $tmp_issuer_cert = newfile("tmp_issuer_cert","temp");
-    while($iter <= $#pem_contents / 4) {
-        if ($iter == $key or $iter == $certificate or $iter == $root_cert) {
-            $iter++; 
-            next;
+        else {
+          print $newindex_fh " $label";
         }
+      }
 
-       open (IC, ">> $tmp_issuer_cert") or die "can't open $tmp_issuer_cert: $?";
-       my $cert_tmp_iter = $cert_tmp_file[$iter];
-       open (CERT, "< $cert_tmp_iter") or die "can't open $cert_tmp_iter: $?";
-       print IC while (<CERT>);
-       close IC;
-       close CERT;
-
-       # although there may be many, just need to know if there was any
-       $intermediate = $iter;
-
-        $iter++;
-    }
-
-    # no intermediate certificates ? use root-cert instead (if that was found...)
-    if($intermediate == $root_cert) {
-        if ($root_cert == -1) {
-         die("No root and no intermediate certificates. Can't continue.");
-       }
-        mycopy $cert_tmp_file[$root_cert], $tmp_issuer_cert;
-    }
-
-    my $label = query_label;
-
-    my $format = -B $tmp_certificate ? 'DER' : 'PEM'; 
-    my $cmd = "$opensslbin x509 -noout -hash -in $tmp_certificate -inform $format";
-    my $cert_hash = `$cmd`;
-    $? and die "'$cmd' returned $?";
-
-    $format = -B $tmp_issuer_cert ? 'DER' : 'PEM'; 
-    $cmd = "$opensslbin x509 -noout -hash -in $tmp_issuer_cert -inform $format";
-    my $issuer_hash = `$cmd`;
-    $? and die "'$cmd' returned $?";
-
-    chomp($cert_hash); chomp($issuer_hash);
-
-    # Note: $cert_hash will be changed to reflect the correct filename
-    #       within add_cert() ONLY, so these _have_ to get called first..
-    add_certificate($tmp_issuer_cert, \$issuer_hash, 0, $label);
-    @mailbox = &add_certificate("$tmp_certificate", \$cert_hash, 1, $label, $issuer_hash); 
-    foreach $mailbox (@mailbox) {
-      chomp($mailbox);
-      add_key($tmp_key, $cert_hash, $mailbox, $label);
-    }
-}
-
-
-
-
-
-
-sub modify_entry ($$$;$ ) {
-    my $op = shift or die;
-    my $hashvalue = shift or die;
-    my $use_cert = shift;
-    my $crl;
-    my $label;
-    my $path;
-    my @fields;
-
-    $op eq 'L' and ($label = shift or die);
-    $op eq 'V' and ($crl = shift);
-
-
-    if ($use_cert) {
-        $path = $certificates_path;
-    }
-    else {
-        $path = $private_keys_path;
-    }
+      if ($op eq 'V') {
+        print "\n==> about to verify certificate of $fields[0]\n";
+        my $flag = openssl_do_verify($fields[1], $fields[3], $crl);
+        print $newindex_fh " $fields[2] $fields[3] $flag";
+      }
 
-    open(INDEX, "<$path/.index") or  
-      die "Couldn't open $path/.index: $!";
-    my $newindex = newfile("$path/.index.tmp");
-    open(NEW_INDEX, ">$newindex") or 
-      die "Couldn't create $newindex: $!";
-
-    while(<INDEX>) {
-        @fields = split;
-        if($fields[1] eq $hashvalue or $hashvalue eq 'all') {
-         $op eq 'R' and next;
-         print NEW_INDEX "$fields[0] $fields[1]";
-         if($op eq 'L') {
-           if($use_cert) {
-             print NEW_INDEX " $label $fields[3] $fields[4]";
-           }
-           else {
-             print NEW_INDEX " $label";
-           }
-         }
-         if ($op eq 'V') {
-           print "\n==> about to verify certificate of $fields[0]\n";
-           my $flag = &do_verify($fields[1], $fields[3], $crl);
-           print NEW_INDEX " $fields[2] $fields[3] $flag";
-         }
-         print NEW_INDEX "\n";
-         next;
-       }
-       print NEW_INDEX;
+      print $newindex_fh "\n";
+      next;
     }
-    close(INDEX);
-    close(NEW_INDEX);
+    print $newindex_fh $_;
+  }
+  close(INDEX);
+  close($newindex_fh);
 
-    rename $newindex, "$path/.index" 
-        or die "Couldn't rename $newindex to $path/.index: $!\n";
+  move $newindex, "$path/.index"
+      or die "Couldn't move $newindex to $path/.index: $!\n";
 
-    print "\n";
+  print "\n";
 }
 
 
+##############
+# Op handlers
+##############
 
+sub handle_init_paths () {
+  mkdir_recursive($certificates_path);
+  mkdir_recursive($private_keys_path);
 
-sub remove_pair ($ ) {
-  my $keyid = shift or die;
+  my $file;
 
-  if (-e "$certificates_path/$keyid") {
-    unlink "$certificates_path/$keyid";
-    modify_entry('R', $keyid, 1);
-    print "Removed certificate $keyid.\n";
-  }
-  else {
-    die "No such certificate: $keyid";
-  }
+  $file = $certificates_path . "/.index";
+  -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
+      or die "Can't touch $file: $!";
 
-  if (-e "$private_keys_path/$keyid") {
-    unlink "$private_keys_path/$keyid";
-    modify_entry('R', $keyid, 0);
-    print "Removed private key $keyid.\n";
-  }
+  $file = $private_keys_path . "/.index";
+  -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
+      or die "Can't touch $file: $!";
 }
 
+sub handle_change_label ($) {
+  my ($keyid) = @_;
 
-
-sub change_label ($ ) {
-  my $keyid = shift or die;
-  
-  my $label = query_label;
+  my $label = query_label();
 
   if (-e "$certificates_path/$keyid") {
-    modify_entry('L', $keyid, 1, $label);
+    cm_modify_entry('L', $keyid, 1, $label);
     print "Changed label for certificate $keyid.\n";
   }
   else {
@@ -796,130 +763,189 @@ sub change_label ($ ) {
   }
 
   if (-e "$private_keys_path/$keyid") {
-    modify_entry('L', $keyid, 0, $label);
+    cm_modify_entry('L', $keyid, 0, $label);
     print "Changed label for private key $keyid.\n";
   }
-
 }
 
+sub handle_add_cert($) {
+  my ($filename) = @_;
 
+  my $label = query_label();
 
+  my $cert_hash = openssl_hash($filename);
+  cm_add_certificate($filename, \$cert_hash, 1, $label, '?');
 
-sub verify_cert ($$) {
-  my $keyid = shift or die;
-  my $crl = shift;
+  # TODO:
+  # Below is the method from http://kb.wisc.edu/middleware/page.php?id=4091
+  # Investigate threading the chain and separating out issuer as an alternative.
 
-  -e "$certificates_path/$keyid" or $keyid eq 'all'
-    or die "No such certificate: $keyid";
-  modify_entry('V', $keyid, 1, $crl);
+  # my @cert_contents = openssl_parse_pem($filename, 0);
+  # foreach my $cert (@cert_contents) {
+  #   if ($cert->{type} eq "C") {
+  #     my $cert_hash = openssl_hash($cert->{datafile});
+  #     cm_add_certificate($cert->{datafile}, \$cert_hash, 1, $label, '?');
+  #   } else {
+  #     print "Ignoring private key\n";
+  #   }
+  # }
 }
 
+sub handle_add_pem ($) {
+  my ($filename) = @_;
+
+  my @pem_contents;
+  my $iter;
+  my $key;
+  my $certificate;
+  my $root_cert;
+  my $issuer_cert_file;
+
+  @pem_contents = openssl_parse_pem($filename, 1);
+
+  # look for key
+  $iter = 0;
+  while($iter <= $#pem_contents) {
+    if ($pem_contents[$iter]->{type} eq "K") {
+      $key = $pem_contents[$iter];
+      splice(@pem_contents, $iter, 1);
+      last;
+    }
+    $iter++;
+  }
+  defined($key) or die("Couldn't find private key!");
+  $key->{localKeyID} or die("Attribute 'localKeyID' wasn't set.");
+
+  # private key and certificate use the same 'localKeyID'
+  $iter = 0;
+  while($iter <= $#pem_contents) {
+    if (($pem_contents[$iter]->{type} eq "C") &&
+        ($pem_contents[$iter]->{localKeyID} eq $key->{localKeyID})) {
+      $certificate = $pem_contents[$iter];
+      splice(@pem_contents, $iter, 1);
+      last;
+    }
+    $iter++;
+  }
+  defined($certificate) or die("Couldn't find matching certificate!");
 
+  if ($#pem_contents < 0) {
+    die("No root and no intermediate certificates. Can't continue.");
+  }
 
+  # Look for a self signed root certificate
+  $iter = 0;
+  while($iter <= $#pem_contents) {
+    if ($pem_contents[$iter]->{subject} eq $pem_contents[$iter]->{issuer}) {
+      $root_cert = $pem_contents[$iter];
+      splice(@pem_contents, $iter, 1);
+      last;
+    }
+    $iter++;
+  }
+  if (defined($root_cert)) {
+    $issuer_cert_file = $root_cert->{datafile};
+  } else {
+    print "Couldn't identify root certificate!\n";
+  }
 
-sub do_verify($$$) {
-
-  my $cert = shift or die;
-  my $issuerid = shift or die;
-  my $crl = shift;
+  # what's left are intermediate certificates.
+  if ($#pem_contents >= 0) {
+    my ($tmp_issuer_cert_fh, $tmp_issuer_cert) = create_tempfile();
+    $issuer_cert_file = $tmp_issuer_cert;
 
-  my $result = 'i';
-  my $trust_q;
-  my $issuer_path;
-  my $cert_path = "$certificates_path/$cert";
+    $iter = 0;
+    while($iter <= $#pem_contents) {
+      my $cert_datafile = $pem_contents[$iter]->{datafile};
+      open (CERT, "< $cert_datafile") or die "can't open $cert_datafile: $?";
+      print $tmp_issuer_cert_fh $_ while (<CERT>);
+      close CERT;
 
-  if($issuerid eq '?') {
-    $issuer_path = "$certificates_path/$cert";
-  } else {
-    $issuer_path = "$certificates_path/$issuerid";
+      $iter++;
+    }
+    close $tmp_issuer_cert_fh;
   }
 
-  my $cmd = "$opensslbin verify $root_certs_switch $root_certs_path -purpose smimesign -purpose smimeencrypt -untrusted $issuer_path $cert_path";
-  my $output = `$cmd`;
-  $? and die "'$cmd' returned $?";
-  chop $output;
-  print "\n$output\n";
+  handle_add_chain($key->{datafile}, $certificate->{datafile}, $issuer_cert_file);
+}
 
-  ($output =~ /OK/) and ($result = 'v');
+sub handle_add_p12 ($) {
+  my ($filename) = @_;
 
-  $result eq 'i' and return $result;
+  print "\nNOTE: This will ask you for two passphrases:\n";
+  print "       1. The passphrase you used for exporting\n";
+  print "       2. The passphrase you wish to secure your private key with.\n\n";
 
-  my $format = -B $cert_path ? 'DER' : 'PEM'; 
-  $cmd = "$opensslbin x509 -dates -serial -noout -in $cert_path -inform $format";
-  (my $date1_in, my $date2_in, my $serial_in) = `$cmd`;
-  $? and die "'$cmd' returned $?";
+  my ($pem_fh, $pem_file) = create_tempfile();
+  close($pem_fh);
 
-  if ( defined $date1_in and defined $date2_in ) {
-    my @tmp = split (/\=/, $date1_in);
-    my $tmp = $tmp[1];
-    @tmp = split (/\=/, $date2_in);
-    my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03',
-                 'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07',
-                 'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11');
+  my $cmd = "$opensslbin pkcs12 -in $filename -out $pem_file";
+  system $cmd and die "'$cmd' returned $?";
 
-    my @fields =
-      $tmp =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
+  -e $pem_file and -s $pem_file or die("Conversion of $filename failed.");
+  handle_add_pem($pem_file);
+}
 
-    $#fields != 5 and print "Expiration Date: Parse Error :  $tmp\n\n" or
-      timegm($fields[4], $fields[3], $fields[2], $fields[1],
-            $months{$fields[0]}, $fields[5]) > time and $result = 'e';
-    $result eq 'e' and print "Certificate is not yet valid.\n" and return $result;
+sub handle_add_chain ($$$) {
+  my ($key_file, $cert_file, $issuer_file) = @_;
 
-    @fields =
-      $tmp[1] =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
+  my $cert_hash = openssl_hash($cert_file);
+  my $issuer_hash = openssl_hash($issuer_file);
 
-    $#fields != 5 and print "Expiration Date: Parse Error :  $tmp[1]\n\n" or
-      timegm($fields[4], $fields[3], $fields[2], $fields[1],
-            $months{$fields[0]}, $fields[5]) < time and $result = 'e';
-    $result eq 'e' and print "Certificate has expired.\n" and return $result;
+  my $label = query_label();
 
-  }
-    
-  if ( defined $crl ) {
-    my @serial = split (/\=/, $serial_in);
-    my $cmd = "$opensslbin crl -text -noout -in $crl | grep -A1 $serial[1]";
-    (my $l1, my $l2) = `$cmd`;
-    $? and die "'$cmd' returned $?";
-    
-    if ( defined $l2 ) {
-      my @revoke_date = split (/:\s/, $l2);
-      print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n";
-      $result = 'r';
-    }
-  }    
-  print "\n";
+  cm_add_certificate($issuer_file, \$issuer_hash, 0, $label);
+  my @mailbox = cm_add_certificate($cert_file, \$cert_hash, 1, $label, $issuer_hash);
 
-  if ($result eq 'v') {
-    return 't';
+  foreach my $mailbox (@mailbox) {
+    cm_add_key($key_file, $cert_hash, $mailbox, $label);
   }
+}
 
-  return $result;
+sub handle_verify_cert ($$) {
+  my ($keyid, $crl) = @_;
+
+  -e "$certificates_path/$keyid" or $keyid eq 'all'
+    or die "No such certificate: $keyid";
+  cm_modify_entry('V', $keyid, 1, $crl);
 }
 
+sub handle_remove_pair ($) {
+  my ($keyid) = @_;
 
+  if (-e "$certificates_path/$keyid") {
+    unlink "$certificates_path/$keyid";
+    cm_modify_entry('R', $keyid, 1);
+    print "Removed certificate $keyid.\n";
+  }
+  else {
+    die "No such certificate: $keyid";
+  }
 
-sub add_root_cert ($) {
-  my $root_cert = shift or die;
+  if (-e "$private_keys_path/$keyid") {
+    unlink "$private_keys_path/$keyid";
+    cm_modify_entry('R', $keyid, 0);
+    print "Removed private key $keyid.\n";
+  }
+}
 
-  my $format = -B $root_cert ? 'DER' : 'PEM'; 
+sub handle_add_root_cert ($) {
+  my ($root_cert) = @_;
 
-  my $cmd = "$opensslbin x509 -noout -hash -in $root_cert -inform $format";
-  my $root_hash = `$cmd`;
-  $? and die "'$cmd' returned $?";
+  my $root_hash = openssl_hash($root_cert);
 
   if (-d $root_certs_path) {
     -e "$root_certs_path/$root_hash" or
         mycopy $root_cert, "$root_certs_path/$root_hash";
   }
   else {
-    open(ROOT_CERTS, ">>$root_certs_path") or 
+    open(ROOT_CERTS, ">>$root_certs_path") or
       die ("Couldn't open $root_certs_path for writing");
 
-    $cmd = "$opensslbin x509 -in $root_cert -inform $format -fingerprint -noout";
-    $? and die "'$cmd' returned $?";
-    chomp(my $md5fp = `$cmd`);
+    my $md5fp = openssl_fingerprint($root_cert);
+    my $format = openssl_format($root_cert);
 
-    $cmd = "$opensslbin x509 -in $root_cert -inform $format -text -noout";
+    my $cmd = "$opensslbin x509 -in $root_cert -inform $format -text -noout";
     $? and die "'$cmd' returned $?";
     my @cert_text = `$cmd`;
 
@@ -936,38 +962,5 @@ sub add_root_cert ($) {
     print ROOT_CERTS @cert_text;
     close (ROOT_CERTS);
   }
-  
-}
-
-sub newfile ($;$$) {
-       # returns a file name which does not exist for tmp file creation
-       my $filename = shift;
-       my $option = shift;
-       $option = "notemp" if (not defined($option));
-       if (! $tmpdir and $option eq "temp") {
-               $tmpdir = mutt_Q 'tmpdir';
-               $tmpdir = newfile("$tmpdir/smime");
-               mkdir $tmpdir, 0700 || die "Can't create $tmpdir: $!\n";
-       }
-       $filename = "$tmpdir/$filename" if ($option eq "temp");
-       my $newfilename = $filename;
-       my $count = 0;
-       while (-e $newfilename) {
-               $newfilename = "$filename.$count";
-               $count++;
-       }
-       unshift(@tempfiles,$newfilename);
-       return $newfilename;
-}
-
-
-END {
-       # remove all our temporary files in the end:
-       for (@tempfiles){
-               if (-f) {
-                       unlink;
-               } elsif (-d) { 
-                       rmdir;
-               }
-       }
+
 }