Attached is a patch which hopefully implements better certificate chain handling for add_cert. Although I've tried to test it, I am not a S/MIME user and don't have access to a large number of certificates to play with. I would greatly appreciate if smime_keys users would help me test this out.
Note that this patch should handle the case of multiple leafs in the certificate, but again I could use some help testing this. -Kevin
# HG changeset patch # User Kevin McCarthy <ke...@8t8.us> # Date 1432328933 25200 # Fri May 22 14:08:53 2015 -0700 # Node ID 0355d3924bd7b6608651cb3ec970d90848c438fc # Parent 577987ca2d02d898880a19f7fd0ee3e7baa41798 smime_keys: Handle certificate chains in add_cert. (closes #3339) (closes #3559) Find all chains in the certificate provided. For each chain, prompt for a label and separately add the intermediate and leaf certificates. diff --git a/smime_keys.pl b/smime_keys.pl --- a/smime_keys.pl +++ b/smime_keys.pl @@ -31,16 +31,17 @@ sub usage (); sub mutt_Q ($); sub mycopy ($$); sub query_label (); sub mkdir_recursive ($); sub verify_files_exist (@); sub create_tempfile (;$); sub new_cert_structure (); +sub create_cert_chains (@); # openssl helpers sub openssl_exec (@); sub openssl_format ($); sub openssl_x509_query ($@); sub openssl_hash ($); sub openssl_fingerprint ($); sub openssl_emails ($); @@ -289,16 +290,56 @@ $cert_data->{type} = ""; $cert_data->{localKeyID} = ""; $cert_data->{subject} = ""; $cert_data->{issuer} = ""; return $cert_data; } +sub create_cert_chains (@) { + my (@certs) = @_; + + my (%subject_hash, @leaves, @chains); + + foreach my $cert (@certs) { + $cert->{children} = 0; + if ($cert->{subject}) { + $subject_hash{$cert->{subject}} = $cert; + } + } + + foreach my $cert (@certs) { + my $parent = $subject_hash{$cert->{issuer}}; + if (defined($parent)) { + $parent->{children} += 1; + } + } + + @leaves = grep { $_->{children} == 0 } @certs; + foreach my $leaf (@leaves) { + my $chain = []; + my $cert = $leaf; + + while (defined($cert)) { + push @$chain, $cert; + + $cert = $subject_hash{$cert->{issuer}}; + if (defined($cert) && + (scalar(grep {$_ == $cert} @$chain) != 0)) { + $cert = undef; + } + } + + push @chains, $chain; + } + + return @chains; +} + ################## # openssl helpers ################## sub openssl_exec (@) { my (@args) = @_; @@ -827,34 +868,51 @@ 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_contents = openssl_parse_pem($filename, 0); + @cert_contents = grep { $_->{type} eq "C" } @cert_contents; - my $cert_hash = openssl_hash($filename); - cm_add_certificate($filename, \$cert_hash, 1, $label, '?'); + my @cert_chains = create_cert_chains(@cert_contents); + print "Found " . scalar(@cert_chains) . " certificate chains\n"; - # 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. + foreach my $chain (@cert_chains) { + my $leaf = shift(@$chain); + my $issuer_chain_hash = "?"; - # 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"; - # } - # } + print "Processing chain:\n"; + if ($leaf->{subject}) { + print "subject=" . $leaf->{subject} . "\n"; + } + my $label = query_label(); + + if (scalar(@$chain) > 0) { + my ($issuer_chain_fh, $issuer_chain_file) = create_tempfile(); + + foreach my $issuer (@$chain) { + my $issuer_datafile = $issuer->{datafile}; + open(my $issuer_fh, "< $issuer_datafile") or + die "can't open $issuer_datafile: $?"; + print $issuer_chain_fh $_ while (<$issuer_fh>); + close($issuer_fh); + } + + close($issuer_chain_fh); + $issuer_chain_hash = openssl_hash($issuer_chain_file); + cm_add_certificate($issuer_chain_file, \$issuer_chain_hash, 0, $label); + } + + my $leaf_hash = openssl_hash($leaf->{datafile}); + cm_add_certificate($leaf->{datafile}, \$leaf_hash, 1, $label, $issuer_chain_hash); + } } sub handle_add_pem ($) { my ($filename) = @_; my @pem_contents; my $iter; my $key;
signature.asc
Description: PGP signature