| 1 |
#!/usr/bin/perl -w |
| 2 |
# |
| 3 |
# ***** BEGIN LICENSE BLOCK ***** |
| 4 |
# Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| 5 |
# |
| 6 |
# The contents of this file are subject to the Mozilla Public License Version |
| 7 |
# 1.1 (the "License"); you may not use this file except in compliance with |
| 8 |
# the License. You may obtain a copy of the License at |
| 9 |
# http://www.mozilla.org/MPL/ |
| 10 |
# |
| 11 |
# Software distributed under the License is distributed on an "AS IS" basis, |
| 12 |
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| 13 |
# for the specific language governing rights and limitations under the |
| 14 |
# License. |
| 15 |
# |
| 16 |
# The Original Code is the Netscape security libraries. |
| 17 |
# |
| 18 |
# The Initial Developer of the Original Code is |
| 19 |
# Netscape Communications Corporation. |
| 20 |
# Portions created by the Initial Developer are Copyright (C) 1994-2000 |
| 21 |
# the Initial Developer. All Rights Reserved. |
| 22 |
# |
| 23 |
# Contributor(s): |
| 24 |
# |
| 25 |
# Alternatively, the contents of this file may be used under the terms of |
| 26 |
# either the GNU General Public License Version 2 or later (the "GPL"), or |
| 27 |
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| 28 |
# in which case the provisions of the GPL or the LGPL are applicable instead |
| 29 |
# of those above. If you wish to allow use of your version of this file only |
| 30 |
# under the terms of either the GPL or the LGPL, and not to allow others to |
| 31 |
# use your version of this file under the terms of the MPL, indicate your |
| 32 |
# decision by deleting the provisions above and replace them with the notice |
| 33 |
# and other provisions required by the GPL or the LGPL. If you do not delete |
| 34 |
# the provisions above, a recipient may use your version of this file under |
| 35 |
# the terms of any one of the MPL, the GPL or the LGPL. |
| 36 |
# |
| 37 |
# ***** END LICENSE BLOCK ***** |
| 38 |
use strict; |
| 39 |
use Encode; |
| 40 |
|
| 41 |
my $count = 0; |
| 42 |
my @certificates = (); |
| 43 |
my %trusts = (); |
| 44 |
my $object = undef; |
| 45 |
my $output_trustbits; |
| 46 |
|
| 47 |
my %trust_types = ( |
| 48 |
"CKA_TRUST_DIGITAL_SIGNATURE" => "digital-signature", |
| 49 |
"CKA_TRUST_NON_REPUDIATION" => "non-repudiation", |
| 50 |
"CKA_TRUST_KEY_ENCIPHERMENT" => "key-encipherment", |
| 51 |
"CKA_TRUST_DATA_ENCIPHERMENT" => "data-encipherment", |
| 52 |
"CKA_TRUST_KEY_AGREEMENT" => "key-agreement", |
| 53 |
"CKA_TRUST_KEY_CERT_SIGN" => "cert-sign", |
| 54 |
"CKA_TRUST_CRL_SIGN" => "crl-sign", |
| 55 |
"CKA_TRUST_SERVER_AUTH" => "server-auth", |
| 56 |
"CKA_TRUST_CLIENT_AUTH" => "client-auth", |
| 57 |
"CKA_TRUST_CODE_SIGNING" => "code-signing", |
| 58 |
"CKA_TRUST_EMAIL_PROTECTION" => "email-protection", |
| 59 |
"CKA_TRUST_IPSEC_END_SYSTEM" => "ipsec-end-system", |
| 60 |
"CKA_TRUST_IPSEC_TUNNEL" => "ipsec-tunnel", |
| 61 |
"CKA_TRUST_IPSEC_USER" => "ipsec-user", |
| 62 |
"CKA_TRUST_TIME_STAMPING" => "time-stamping", |
| 63 |
"CKA_TRUST_STEP_UP_APPROVED" => "step-up-approved", |
| 64 |
); |
| 65 |
|
| 66 |
my %openssl_trust = ( |
| 67 |
CKA_TRUST_SERVER_AUTH => 'serverAuth', |
| 68 |
CKA_TRUST_CLIENT_AUTH => 'clientAuth', |
| 69 |
CKA_TRUST_EMAIL_PROTECTION => 'emailProtection', |
| 70 |
CKA_TRUST_CODE_SIGNING => 'codeSigning', |
| 71 |
); |
| 72 |
|
| 73 |
if (@ARGV && $ARGV[0] eq '--trustbits') { |
| 74 |
shift @ARGV; |
| 75 |
$output_trustbits = 1; |
| 76 |
} |
| 77 |
|
| 78 |
sub colonhex |
| 79 |
{ |
| 80 |
return join(':', unpack("(H2)*", $_[0])); |
| 81 |
} |
| 82 |
|
| 83 |
sub handle_object($) |
| 84 |
{ |
| 85 |
my $object = shift; |
| 86 |
return unless $object; |
| 87 |
if($object->{'CKA_CLASS'} eq 'CKO_CERTIFICATE' && $object->{'CKA_CERTIFICATE_TYPE'} eq 'CKC_X_509') { |
| 88 |
push @certificates, $object; |
| 89 |
} elsif ($object->{'CKA_CLASS'} eq 'CKO_NETSCAPE_TRUST') { |
| 90 |
my $label = $object->{'CKA_LABEL'}; |
| 91 |
my $serial = colonhex($object->{'CKA_SERIAL_NUMBER'}); |
| 92 |
die "$label exists ($serial)" if exists($trusts{$label.$serial}); |
| 93 |
$trusts{$label.$serial} = $object; |
| 94 |
} elsif ($object->{'CKA_CLASS'} eq 'CKO_NETSCAPE_BUILTIN_ROOT_LIST') { |
| 95 |
# ignore |
| 96 |
} else { |
| 97 |
print STDERR "class ", $object->{'CKA_CLASS'} ," not handled\n"; |
| 98 |
} |
| 99 |
} |
| 100 |
|
| 101 |
while(<>) { |
| 102 |
my @fields = (); |
| 103 |
|
| 104 |
s/^((?:[^"#]+|"[^"]*")*)(\s*#.*$)/$1/; |
| 105 |
next if (/^\s*$/); |
| 106 |
|
| 107 |
if( /(^CVS_ID\s+)(.*)/ ) { |
| 108 |
next; |
| 109 |
} |
| 110 |
|
| 111 |
# This was taken from the perl faq #4. |
| 112 |
my $text = $_; |
| 113 |
push(@fields, $+) while $text =~ m{ |
| 114 |
"([^\"\\]*(?:\\.[^\"\\]*)*)"\s? # groups the phrase inside the quotes |
| 115 |
| ([^\s]+)\s? |
| 116 |
| \s |
| 117 |
}gx; |
| 118 |
push(@fields, undef) if substr($text,-1,1) eq '\s'; |
| 119 |
|
| 120 |
if( $fields[0] =~ /BEGINDATA/ ) { |
| 121 |
next; |
| 122 |
} |
| 123 |
|
| 124 |
if( $fields[1] =~ /MULTILINE/ ) { |
| 125 |
die "expected MULTILINE_OCTAL" unless $fields[1] eq 'MULTILINE_OCTAL'; |
| 126 |
$fields[2] = ""; |
| 127 |
while(<>) { |
| 128 |
last if /END/; |
| 129 |
chomp; |
| 130 |
$fields[2] .= pack("C", oct($+)) while $_ =~ /\G\\([0-3][0-7][0-7])/g; |
| 131 |
} |
| 132 |
} |
| 133 |
|
| 134 |
if( $fields[0] =~ /CKA_CLASS/ ) { |
| 135 |
$count++; |
| 136 |
handle_object($object); |
| 137 |
$object = {}; |
| 138 |
} |
| 139 |
|
| 140 |
$object->{$fields[0]} = $fields[2]; |
| 141 |
} |
| 142 |
handle_object($object); |
| 143 |
undef $object; |
| 144 |
|
| 145 |
use MIME::Base64; |
| 146 |
for my $cert (@certificates) { |
| 147 |
my $alias = $cert->{'CKA_LABEL'}; |
| 148 |
my $serial = colonhex($cert->{'CKA_SERIAL_NUMBER'}); |
| 149 |
if(!exists($trusts{$alias.$serial})) { |
| 150 |
print STDERR "NO TRUST: $alias\n"; |
| 151 |
next; |
| 152 |
} |
| 153 |
# check trust. We only include certificates that are trusted for identifying |
| 154 |
# web sites |
| 155 |
my $trust = $trusts{$alias.$serial}; |
| 156 |
my @addtrust; |
| 157 |
my @addtrust_openssl; |
| 158 |
my $trusted; |
| 159 |
if ($output_trustbits) { |
| 160 |
for my $type (keys %trust_types) { |
| 161 |
if (exists $trust->{$type} |
| 162 |
&& $trust->{$type} eq 'CKT_NETSCAPE_TRUSTED_DELEGATOR') { |
| 163 |
push @addtrust, $trust_types{$type}; |
| 164 |
if (exists $openssl_trust{$type}) { |
| 165 |
push @addtrust_openssl, $openssl_trust{$type}; |
| 166 |
} |
| 167 |
$trusted = 1; |
| 168 |
} |
| 169 |
} |
| 170 |
} else { |
| 171 |
if($trust->{'CKA_TRUST_SERVER_AUTH'} eq 'CKT_NETSCAPE_TRUSTED_DELEGATOR') { |
| 172 |
$trusted = 1; |
| 173 |
} |
| 174 |
} |
| 175 |
|
| 176 |
if (!$trusted) { |
| 177 |
my $t = $trust->{'CKA_TRUST_SERVER_AUTH'}; |
| 178 |
$t =~ s/CKT_NETSCAPE_//; |
| 179 |
print STDERR "$t: $alias\n"; |
| 180 |
next; |
| 181 |
} |
| 182 |
|
| 183 |
if ($alias =~ /\\x[0-9a-fA-F]{2}/) { |
| 184 |
$alias =~ s/\\x([0-9a-fA-F]{2})/chr(hex($1))/ge; # thanks mls! |
| 185 |
$alias = Encode::decode("UTF-8", $alias); |
| 186 |
} |
| 187 |
my $file = $alias; |
| 188 |
$alias =~ s/'/-/g; |
| 189 |
$file =~ s/[^[:alnum:]\\]+/_/g; |
| 190 |
$file = Encode::encode("UTF-8", $file); |
| 191 |
if (-e $file.'.pem') { |
| 192 |
my $i = 1; |
| 193 |
while (-e $file.".$i.pem") { |
| 194 |
++$i; |
| 195 |
} |
| 196 |
$file .= ".$i.pem"; |
| 197 |
} else { |
| 198 |
$file .= '.pem'; |
| 199 |
} |
| 200 |
if (!open(O, '>', $file)) { |
| 201 |
print STDERR "$file: $!\n"; |
| 202 |
next; |
| 203 |
} |
| 204 |
print "$file\n" if $ENV{'VERBOSE'}; |
| 205 |
my $value = $cert->{'CKA_VALUE'}; |
| 206 |
if ($output_trustbits) { |
| 207 |
print O "# alias=",Encode::encode("UTF-8", $alias),"\n"; |
| 208 |
print O "# trust=",join(" ", @addtrust),"\n"; |
| 209 |
if (@addtrust_openssl) { |
| 210 |
print O "# openssl-trust=",join(" ", @addtrust_openssl),"\n"; |
| 211 |
} |
| 212 |
} |
| 213 |
print O "-----BEGIN CERTIFICATE-----\n"; |
| 214 |
print O encode_base64($value); |
| 215 |
print O "-----END CERTIFICATE-----\n"; |
| 216 |
close O; |
| 217 |
} |