line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package VOMS::Lite::CertKeyHelper; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
26
|
use 5.004; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
45
|
|
4
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
43
|
|
5
|
1
|
|
|
1
|
|
7
|
use VOMS::Lite::ASN1Helper qw(ASN1Index ASN1Unwrap ASN1Wrap Hex ASN1OIDtoOID); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
91
|
|
6
|
1
|
|
|
1
|
|
795
|
use VOMS::Lite::RSAHelper qw(rsasign rsaverify); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
84
|
|
7
|
1
|
|
|
1
|
|
925
|
use VOMS::Lite::PEMHelper qw(readCert); |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
88
|
|
8
|
1
|
|
|
1
|
|
8
|
use VOMS::Lite::X509; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
32
|
|
9
|
1
|
|
|
1
|
|
6
|
use Digest::MD5 qw(md5); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
59
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
require Exporter; |
12
|
1
|
|
|
1
|
|
6
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3359
|
|
13
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
14
|
|
|
|
|
|
|
%EXPORT_TAGS = ( ); |
15
|
|
|
|
|
|
|
@EXPORT_OK = qw(buildchain digestSign OIDtoDNattrib DNattribToOID); |
16
|
|
|
|
|
|
|
@EXPORT = ( ); |
17
|
|
|
|
|
|
|
$VERSION = '0.20'; |
18
|
|
|
|
|
|
|
################################################## |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Define some common OIDs used in Distunguished names NB we're using UID and Email not UserID and emailAddress |
21
|
|
|
|
|
|
|
my %DNOIDs=( '2.5.4.49' => 'DN', |
22
|
|
|
|
|
|
|
'0.9.2342.19200300.100.1.1' => 'UID', |
23
|
|
|
|
|
|
|
'0.9.2342.19200300.100.1.25' => 'DC', |
24
|
|
|
|
|
|
|
'1.2.840.113549.1.9.1' => 'Email', |
25
|
|
|
|
|
|
|
'2.5.4.3' => 'CN', |
26
|
|
|
|
|
|
|
'2.5.4.4' => 'SN', |
27
|
|
|
|
|
|
|
'2.5.4.5' => 'serialNumber', |
28
|
|
|
|
|
|
|
'2.5.4.6' => 'C', |
29
|
|
|
|
|
|
|
'2.5.4.7' => 'L', |
30
|
|
|
|
|
|
|
'2.5.4.8' => 'ST', |
31
|
|
|
|
|
|
|
'2.5.4.9' => 'street', |
32
|
|
|
|
|
|
|
'2.5.4.12' => 'title', |
33
|
|
|
|
|
|
|
'2.5.4.16' => 'postalAddress', |
34
|
|
|
|
|
|
|
'2.5.4.17' => 'postalCode', |
35
|
|
|
|
|
|
|
'2.5.4.18' => 'postOfficeBox', |
36
|
|
|
|
|
|
|
'2.5.4.26' => 'registeredAddress', |
37
|
|
|
|
|
|
|
'2.5.4.11' => 'OU', |
38
|
|
|
|
|
|
|
'2.5.4.41' => 'name', |
39
|
|
|
|
|
|
|
'2.5.4.10' => 'O', |
40
|
|
|
|
|
|
|
'2.5.4.42' => 'givenName', |
41
|
|
|
|
|
|
|
'2.5.4.43' => 'initials', |
42
|
|
|
|
|
|
|
'2.5.6.3' => 'locality', |
43
|
|
|
|
|
|
|
'2.5.6.4' => 'organization'); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my %DNAttribs=( reverse %DNOIDs, |
46
|
|
|
|
|
|
|
commonName => '2.5.4.3', |
47
|
|
|
|
|
|
|
serialNumber => '2.5.4.4', |
48
|
|
|
|
|
|
|
countryName => '2.5.4.6', |
49
|
|
|
|
|
|
|
localityName => '2.5.4.7', |
50
|
|
|
|
|
|
|
stateOrProvinceName => '2.5.4.8', |
51
|
|
|
|
|
|
|
organizationName => '2.5.4.10', |
52
|
|
|
|
|
|
|
organizationalUnitName => '2.5.4.11', |
53
|
|
|
|
|
|
|
emailAddress => '1.2.840.113549.1.9.1', |
54
|
|
|
|
|
|
|
UserID => '0.9.2342.19200300.100.1.1', |
55
|
|
|
|
|
|
|
domainComponent => '0.9.2342.19200300.100.1.25' |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub OIDtoDNattrib { |
59
|
30
|
50
|
|
30
|
0
|
222
|
return (defined $DNOIDs{$_[0]})?$DNOIDs{$_[0]}:$_[0]; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub DNattribToOID { |
63
|
9
|
50
|
|
9
|
0
|
40
|
return (defined $DNAttribs{$_[0]})?$DNAttribs{$_[0]}:undef; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
################################################## |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub verifychain { |
69
|
1
|
|
|
1
|
0
|
2
|
my ($lastExp,$lastMod,$lastCAPurpose,$lastKeyCertSign,$lastkeyUsageDigitalSignature,$EECDN,$EECIDN,$EEC); |
70
|
0
|
|
|
|
|
0
|
my @Self; |
71
|
0
|
|
|
|
|
0
|
my @Time; |
72
|
0
|
|
|
|
|
0
|
my @LifeTime; |
73
|
0
|
|
|
|
|
0
|
my @Signed; |
74
|
0
|
|
|
|
|
0
|
my @SignerPurposeCA; |
75
|
0
|
|
|
|
|
0
|
my @SignerCertSignPurpose; |
76
|
0
|
|
|
|
|
0
|
my @PathLen; |
77
|
0
|
|
|
|
|
0
|
my @GSIType; |
78
|
0
|
|
|
|
|
0
|
my @ProxyPathLen; |
79
|
1
|
|
|
|
|
2
|
my $now=time(); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# Loop over certificate starting at 'root-most' |
82
|
1
|
|
|
|
|
3
|
foreach ( reverse @_ ) { |
83
|
2
|
|
|
|
|
5
|
my %CI = %{ VOMS::Lite::X509::Examine( $_ , { SubjectDN=>"", |
|
2
|
|
|
|
|
57
|
|
84
|
|
|
|
|
|
|
IssuerDN=>"", |
85
|
|
|
|
|
|
|
Start=>"", |
86
|
|
|
|
|
|
|
End=>"", |
87
|
|
|
|
|
|
|
SignatureType=>"", |
88
|
|
|
|
|
|
|
SignatureValue=>"", |
89
|
|
|
|
|
|
|
X509TBSCert=>"", |
90
|
|
|
|
|
|
|
keyUsage=>"", |
91
|
|
|
|
|
|
|
basicConstraints=>"", |
92
|
|
|
|
|
|
|
KeypublicExponent=>"", |
93
|
|
|
|
|
|
|
Keymodulus=>"", |
94
|
|
|
|
|
|
|
authorityKeyIdentifier=>"", |
95
|
|
|
|
|
|
|
subjectKeyIdentifier=>"", |
96
|
|
|
|
|
|
|
ProxyInfo=>"" |
97
|
|
|
|
|
|
|
}) }; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Check if selfsigned, if first in chain set 'last' values to these values |
100
|
2
|
|
|
|
|
29
|
push @Self,0; |
101
|
2
|
100
|
|
|
|
12
|
if ( $CI{IssuerDN} eq $CI{SubjectDN} ) { |
102
|
1
|
50
|
33
|
|
|
12
|
if ( ! defined $CI{authorityKeyIdentifierSkid} || $CI{subjectKeyIdentifier} eq $CI{authorityKeyIdentifierSkid} ) { |
103
|
1
|
|
|
|
|
3
|
$Self[-1]=1; |
104
|
1
|
50
|
|
|
|
6
|
if ( ! defined $lastMod ) { |
105
|
1
|
|
|
|
|
4
|
$lastExp = Hex($CI{KeypublicExponent}); |
106
|
1
|
|
|
|
|
6
|
$lastMod = Hex($CI{Keymodulus}); |
107
|
1
|
|
|
|
|
4
|
$lastCAPurpose = $CI{basicConstraintsCA}; |
108
|
1
|
|
|
|
|
2
|
$lastKeyCertSign = $CI{keyUsageKeyCertSign}; |
109
|
1
|
|
|
|
|
3
|
$lastkeyUsageDigitalSignature = $CI{keyUsageDigitalSignature}; |
110
|
|
|
|
|
|
|
} } } |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# Check Validity of Time, signature, Issuer's purpose and key usage, and path length |
113
|
2
|
50
|
33
|
|
|
16
|
push @Time, ( $CI{Start} < $now && $CI{End} > $now ) ? 1:0; |
114
|
2
|
50
|
33
|
|
|
17
|
push @PathLen, ( defined $CI{basicConstraintsPathLen} && $CI{basicConstraintsPathLen} < ($#_-$#PathLen) ) ? 0:1; |
115
|
2
|
|
|
|
|
10
|
push @Signed, verifySignature($CI{SignatureType},Hex($CI{SignatureValue}),$CI{X509TBSCert},$lastExp,$lastMod); |
116
|
2
|
|
|
|
|
15
|
push @SignerPurposeCA, $lastCAPurpose; |
117
|
2
|
|
|
|
|
7
|
push @SignerCertSignPurpose, $lastKeyCertSign; |
118
|
|
|
|
|
|
|
# TODO Check CRL: 1 find CRL 2 In Date 3 CRL Signed 4 CA has keyUsageCRLSign 5 Cert not in List |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Check GSIness |
121
|
2
|
50
|
33
|
|
|
20
|
if ( ! $SignerPurposeCA[-1] && ! $SignerCertSignPurpose[-1] && $lastkeyUsageDigitalSignature) { |
|
|
|
33
|
|
|
|
|
122
|
0
|
0
|
0
|
|
|
0
|
if ( $CI{SubjectDN} eq "$CI{IssuerDN}/CN=proxy" ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
123
|
0
|
0
|
|
|
|
0
|
if ( $CI{IssuerDN} =~ /\/CN=limited proxy$/ ) { push @GSIType,"Bad Legacy proxy: issuer was limited" } |
|
0
|
|
|
|
|
0
|
|
124
|
0
|
|
|
|
|
0
|
else { push @GSIType,"Legacy Proxy" } |
125
|
|
|
|
|
|
|
} |
126
|
0
|
|
|
|
|
0
|
elsif ( $CI{SubjectDN} eq "$CI{IssuerDN}/CN=limited proxy" ) { push @GSIType,"Limited Proxy"; } |
127
|
|
|
|
|
|
|
elsif ( $CI{ProxyInfo} eq 'RFC' || $CI{ProxyInfo} eq 'Pre-RFC' ) { |
128
|
0
|
0
|
|
|
|
0
|
if ( $CI{SubjectDN} !~ /^$CI{IssuerDN}\/CN=[0-9]+$/ ) { push @GSIType,"Bad RFC proxy: issuer name mismatch" } |
|
0
|
0
|
|
|
|
0
|
|
129
|
0
|
|
|
|
|
0
|
elsif ( $CI{IssuerDN} =~ /\/CN=(?:limited )?proxy$/ ) { push @GSIType,"Bad legacy proxy: issuer was legacy" } |
130
|
0
|
|
|
|
|
0
|
else { push @GSIType,"$CI{ProxyInfo} Proxy"; } |
131
|
|
|
|
|
|
|
} |
132
|
0
|
|
|
|
|
0
|
else { push @GSIType,"Bad proxy"; } |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
else { |
135
|
2
|
100
|
66
|
|
|
19
|
if ( $CI{basicConstraintsCA} || $CI{keyUsageKeyCertSign} ) { push @GSIType,"CA" } |
|
1
|
|
|
|
|
3
|
|
136
|
1
|
|
|
|
|
3
|
else { push @GSIType, "EEC"; |
137
|
1
|
|
|
|
|
6
|
($EECDN,$EECIDN,$EEC) = ($CI{SubjectDN},$CI{IssuerDN},$_); |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# GSI PathLen |
142
|
2
|
50
|
33
|
|
|
18
|
push @ProxyPathLen,(defined $CI{"ProxyPathlen$CI{ProxyInfo}"} && $CI{"ProxyPathlen$CI{ProxyInfo}"}<($#_-$#ProxyPathLen))?0:1; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Populate remaining times array |
145
|
2
|
|
|
|
|
8
|
push @LifeTime, ( $CI{End} - $now ); |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Remember relevant parts of this certificate as issuer for next |
148
|
2
|
|
|
|
|
15
|
$lastExp = Hex($CI{KeypublicExponent}); |
149
|
2
|
|
|
|
|
11
|
$lastMod = Hex($CI{Keymodulus}); |
150
|
2
|
|
|
|
|
6
|
$lastCAPurpose = $CI{basicConstraintsCA}; |
151
|
2
|
|
|
|
|
5
|
$lastKeyCertSign = $CI{keyUsageKeyCertSign}; |
152
|
2
|
|
|
|
|
25
|
$lastkeyUsageDigitalSignature = $CI{keyUsageDigitalSignature}; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
1
|
|
|
|
|
3
|
my $i=0; |
156
|
1
|
|
|
|
|
2
|
my @returnErrors; |
157
|
1
|
|
|
|
|
5
|
while ( defined $Time[$i] ) { |
158
|
2
|
|
|
|
|
4
|
my @errors=(); |
159
|
2
|
50
|
66
|
|
|
12
|
if ( $i > 0 && $GSIType[$i] =~ /^Bad/ ) { push @errors, $GSIType[$i]; } |
|
0
|
|
|
|
|
0
|
|
160
|
2
|
50
|
|
|
|
8
|
if ( ! $Time[$i] ) { push @errors, "Time error in certificate $i"; } |
|
0
|
|
|
|
|
0
|
|
161
|
2
|
0
|
0
|
|
|
7
|
if ( ! $Signed[$i] && ( $i!=0 || $Self[$i] )) { push @errors, "Bad signature on certificate $i"; } |
|
0
|
|
33
|
|
|
0
|
|
162
|
2
|
50
|
66
|
|
|
10
|
if ( $i!=0 && $Self[$i] ) { push @errors, "Self signed certificate in at $i the chain"; } |
|
0
|
|
|
|
|
0
|
|
163
|
2
|
50
|
66
|
|
|
16
|
if ( $GSIType[$i] !~ /^(?:Lega[sc]y|Limited|RFC|Pre-RFC) Proxy$/ && ( $i!=0 || $Self[$i] ) ) { |
|
|
|
33
|
|
|
|
|
164
|
2
|
50
|
|
|
|
6
|
if ( ! $SignerPurposeCA[$i] ) { push @errors, "Signer of certificate $i is not a CA"; } |
|
0
|
|
|
|
|
0
|
|
165
|
2
|
50
|
|
|
|
9
|
if ( ! $SignerCertSignPurpose[$i] ) { push @errors, "Signer of certificate $i may not sign certificates"; } |
|
0
|
|
|
|
|
0
|
|
166
|
|
|
|
|
|
|
} |
167
|
2
|
50
|
|
|
|
5
|
if ( ! $PathLen[$i] ) { push @errors, "Path Length exceeded at certificate $i"; } |
|
0
|
|
|
|
|
0
|
|
168
|
2
|
50
|
|
|
|
5
|
if ( ! $ProxyPathLen[$i] ) { push @errors, "Proxy Path Length exceeded at certificate $i"; } |
|
0
|
|
|
|
|
0
|
|
169
|
2
|
|
|
|
|
3
|
push @returnErrors,\@errors; |
170
|
2
|
|
|
|
|
7
|
$i++; |
171
|
|
|
|
|
|
|
} |
172
|
1
|
|
|
|
|
8
|
return ( \@returnErrors, \@GSIType, \@LifeTime, $EECDN, $EECIDN, $EEC ); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
################################################## |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub buildchain { |
178
|
1
|
|
|
1
|
0
|
2
|
my $inref = shift; |
179
|
1
|
|
|
|
|
2
|
my %in = %{$inref}; |
|
1
|
|
|
|
|
4
|
|
180
|
|
|
|
|
|
|
|
181
|
1
|
50
|
|
|
|
4
|
my @CAdirs = (defined $in{'trustedCAdirs'})? @{$in{'trustedCAdirs'}}:(); |
|
1
|
|
|
|
|
3
|
|
182
|
1
|
50
|
|
|
|
5
|
my @certs = (defined $in{'suppliedcerts'})? @{$in{'suppliedcerts'}}:(); |
|
1
|
|
|
|
|
2
|
|
183
|
1
|
50
|
|
|
|
4
|
my @CAcerts = (defined $in{'trustedCAs'})? @{$in{'trustedCAs'}}:(); |
|
0
|
|
|
|
|
0
|
|
184
|
|
|
|
|
|
|
|
185
|
1
|
|
|
|
|
2
|
my %cert; my %hash; my %ihash; my %skid; my %akid; my %dn; my %idn; my %dir; my %trust; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
186
|
1
|
|
|
|
|
12
|
my $CertHashTemplate={authorityKeyIdentifier=>"", subjectKeyIdentifier=>"", keyUsage=>"", basicConstraints=>"",IHash=>"",Hash=>"",SubjectDN=>"",IssuerDN=>""}; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Load Leaf certificate information |
189
|
1
|
|
|
|
|
2
|
my @returnCerts = (shift @certs); # First one is treated as the leaf certificate/gsi-certificate |
190
|
1
|
|
|
|
|
8
|
my $CertInfoRef = VOMS::Lite::X509::Examine( $returnCerts[0] ,$CertHashTemplate); |
191
|
1
|
|
|
|
|
26
|
my %CERTINFO = %$CertInfoRef; |
192
|
1
|
|
|
|
|
5
|
my @IHash = ($CERTINFO{IHash}); |
193
|
1
|
|
|
|
|
3
|
my @Hash = ($CERTINFO{Hash}); |
194
|
1
|
|
|
|
|
4
|
my @SKID = ($CERTINFO{subjectKeyIdentifier}); |
195
|
1
|
|
|
|
|
2
|
my @AKID = ($CERTINFO{authorityKeyIdentifierSkid}); |
196
|
1
|
|
|
|
|
3
|
my @DNs = ($CERTINFO{SubjectDN}); |
197
|
1
|
|
|
|
|
3
|
my @IDNs = ($CERTINFO{IssuerDN}); |
198
|
1
|
|
|
|
|
1
|
my @Trust = (0); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
#Make place holder for CA certs which reside in supplied directories (only load them if needed) |
201
|
1
|
|
|
|
|
2
|
my @cas; |
202
|
1
|
|
|
|
|
3
|
foreach my $dir (reverse @CAdirs) { # First directory takes presidence |
203
|
1
|
|
|
|
|
63
|
opendir(GRIDSECURITYDIR,$dir); |
204
|
1
|
|
|
|
|
824
|
push @cas, grep(/\.[0-9]+$/, readdir(GRIDSECURITYDIR)); |
205
|
1
|
|
|
|
|
16
|
closedir(GRIDSECURITYDIR); |
206
|
1
|
|
|
|
|
4
|
foreach (@cas) { |
207
|
1
|
|
|
|
|
4
|
$hash{$_}="$_"; |
208
|
1
|
|
|
|
|
2
|
$cert{$_}=""; |
209
|
1
|
|
|
|
|
6
|
$dir{$_}=$dir; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
#Load locally supplied CAcert info and peer supplied CAcert info |
214
|
1
|
|
|
|
|
3
|
my $remainingtrusted=$#CAcerts; |
215
|
1
|
|
|
|
|
3
|
foreach (@CAcerts,@certs) { |
216
|
1
|
|
|
|
|
4
|
my $CertInfoRef=VOMS::Lite::X509::Examine($_,$CertHashTemplate); |
217
|
1
|
|
|
|
|
22
|
my %CERTINFO=%$CertInfoRef; |
218
|
1
|
|
|
|
|
5
|
my $index=0; |
219
|
1
|
|
|
|
|
10
|
until ( ! defined $hash{"$CERTINFO{Hash}.$index"} ) { $index++; } |
|
1
|
|
|
|
|
5
|
|
220
|
1
|
|
|
|
|
5
|
$cert{"$CERTINFO{Hash}.$index"} = $_; |
221
|
1
|
|
|
|
|
5
|
$hash{"$CERTINFO{Hash}.$index"} = $CERTINFO{Hash}; |
222
|
1
|
|
|
|
|
3
|
$ihash{"$CERTINFO{Hash}.$index"} = $CERTINFO{IHash}; |
223
|
1
|
|
|
|
|
5
|
$skid{"$CERTINFO{Hash}.$index"} = $CERTINFO{subjectKeyIdentifier}; |
224
|
1
|
|
|
|
|
4
|
$akid{"$CERTINFO{Hash}.$index"} = $CERTINFO{authorityKeyIdentifierSkid}; |
225
|
1
|
|
|
|
|
5
|
$dn{"$CERTINFO{Hash}.$index"} = $CERTINFO{SubjectDN}; |
226
|
1
|
|
|
|
|
4
|
$idn{"$CERTINFO{Hash}.$index"} = $CERTINFO{IssuerDN}; |
227
|
1
|
50
|
|
|
|
6
|
$trust{"$CERTINFO{Hash}.$index"} = ( $remainingtrusted > -1 ) ? 1:0; #trust only locally supplied certs |
228
|
1
|
|
|
|
|
12
|
$remainingtrusted--; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
#Loop round all certificate authorities until the chain is constructed or there are no found issuers. |
232
|
1
|
|
|
|
|
3
|
my $found; |
233
|
1
|
|
|
|
|
2
|
my $self=0; |
234
|
1
|
|
|
|
|
3
|
for(;;) { |
235
|
1
|
|
|
|
|
2
|
$found="no"; |
236
|
1
|
|
|
|
|
3
|
my @a; |
237
|
1
|
50
|
|
|
|
5
|
foreach (keys(%cert)) { if ($_ =~ /^$IHash[-1].[0-9]+$/ ) { push @a, $_;} } |
|
2
|
|
|
|
|
51
|
|
|
2
|
|
|
|
|
7
|
|
238
|
1
|
|
|
|
|
11
|
my @b=sort(@a); |
239
|
1
|
|
|
|
|
5
|
foreach my $file ( @b ) { |
240
|
1
|
50
|
33
|
|
|
19
|
if ( $cert{$file} eq "" && defined $dir{$file} ) { # Load in CA certificate as required |
241
|
1
|
|
|
|
|
11
|
my $certder = readCert("$dir{$file}/$file"); |
242
|
1
|
|
|
|
|
7
|
my $CertInfoRef = VOMS::Lite::X509::Examine($certder,$CertHashTemplate); |
243
|
1
|
|
|
|
|
34
|
my %CERTINFO = %$CertInfoRef; |
244
|
1
|
|
|
|
|
5
|
$cert{$file} = $certder; |
245
|
1
|
|
|
|
|
3
|
$hash{$file} = $CERTINFO{Hash}; |
246
|
1
|
|
|
|
|
4
|
$ihash{$file} = $CERTINFO{IHash}; |
247
|
1
|
|
|
|
|
3
|
$skid{$file} = $CERTINFO{subjectKeyIdentifier}; |
248
|
1
|
|
|
|
|
2
|
$akid{$file} = $CERTINFO{authorityKeyIdentifierSkid}; |
249
|
1
|
|
|
|
|
3
|
$dn{$file} = $CERTINFO{SubjectDN}; |
250
|
1
|
|
|
|
|
2
|
$idn{$file} = $CERTINFO{IssuerDN}; |
251
|
1
|
|
|
|
|
10
|
$trust{$file} = 1; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
1
|
50
|
33
|
|
|
17
|
if($IDNs[-1] eq $dn{$file} && ( ! defined $AKID[-1] || $AKID[-1] eq $skid{$file} ) ) { #Issuer names match and Key ID's match |
|
|
|
33
|
|
|
|
|
255
|
1
|
|
|
|
|
3
|
$found="yes"; |
256
|
1
|
50
|
0
|
|
|
8
|
if ( $DNs[-1] eq $IDNs[-1] && ( ! defined $AKID[-1] || $SKID[-1] eq $AKID[-1] )) { #Check last cert not self-signed |
|
|
|
33
|
|
|
|
|
257
|
0
|
|
|
|
|
0
|
$self=1; |
258
|
0
|
|
|
|
|
0
|
$Trust[-1]=1; |
259
|
0
|
|
|
|
|
0
|
last; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
else { |
262
|
1
|
|
|
|
|
3
|
push @returnCerts, $cert{$file}; |
263
|
1
|
|
|
|
|
3
|
push @IHash, $ihash{$file}; |
264
|
1
|
|
|
|
|
3
|
push @Hash, $hash{$file}; |
265
|
1
|
|
|
|
|
4
|
push @SKID, $skid{$file}; |
266
|
1
|
|
|
|
|
2
|
push @AKID, $akid{$file}; |
267
|
1
|
|
|
|
|
2
|
push @DNs, $dn{$file}; |
268
|
1
|
|
|
|
|
1
|
push @IDNs, $idn{$file}; |
269
|
1
|
|
|
|
|
3
|
push @Trust, $trust{$file}; |
270
|
1
|
|
|
|
|
3
|
last; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
# Stop looping if no signer found or certificate is self signed |
275
|
1
|
50
|
|
|
|
5
|
last if ( $found eq "no" ); |
276
|
1
|
50
|
33
|
|
|
12
|
if ( $DNs[-1] eq $IDNs[-1] && ( !defined $AKID[-1] || $SKID[-1] eq $AKID[-1] )) { $self=1; last; } |
|
1
|
|
33
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
277
|
|
|
|
|
|
|
} |
278
|
1
|
|
|
|
|
6
|
my @verify = verifychain(@returnCerts); |
279
|
1
|
|
|
|
|
5
|
my @Errors = reverse @{ $verify[0] }; |
|
1
|
|
|
|
|
4
|
|
280
|
1
|
|
|
|
|
2
|
my @GSI = reverse @{ $verify[1] }; |
|
1
|
|
|
|
|
4
|
|
281
|
1
|
|
|
|
|
2
|
my @LifeTime = reverse @{ $verify[2] }; |
|
1
|
|
|
|
|
3
|
|
282
|
1
|
|
|
|
|
3
|
my $EECDN = $verify[3]; |
283
|
1
|
|
|
|
|
2
|
my $EECIDN = $verify[4]; |
284
|
1
|
|
|
|
|
15
|
my $EEC = $verify[5]; |
285
|
|
|
|
|
|
|
|
286
|
1
|
|
|
|
|
65
|
return { Certs => \@returnCerts, |
287
|
|
|
|
|
|
|
IssuerHashes => \@IHash, |
288
|
|
|
|
|
|
|
SubjectHashes => \@Hash, |
289
|
|
|
|
|
|
|
SubjectKeyIdentifiers => \@SKID, |
290
|
|
|
|
|
|
|
AuthorityKeyIdentifiersSKIDs => \@AKID, |
291
|
|
|
|
|
|
|
DistinguishedNames => \@DNs, |
292
|
|
|
|
|
|
|
IssuerDistinguishedNames => \@IDNs, |
293
|
|
|
|
|
|
|
TrustedCA => \@Trust, |
294
|
|
|
|
|
|
|
SelfSignedInChain => $self, |
295
|
|
|
|
|
|
|
GSIType => \@GSI, |
296
|
|
|
|
|
|
|
EndEntityDN => $EECDN, |
297
|
|
|
|
|
|
|
EndEntityIssuerDN => $EECIDN, |
298
|
|
|
|
|
|
|
EndEntityCert => $EEC, |
299
|
|
|
|
|
|
|
Lifetimes => \@LifeTime, |
300
|
|
|
|
|
|
|
Errors => \@Errors |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
################################################################ |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# Need type of digestType, BinaryData, HexKey, HexModulus, |
307
|
|
|
|
|
|
|
sub digestTBS { |
308
|
7
|
|
|
7
|
0
|
14
|
my ($type,$Data) = @_; |
309
|
|
|
|
|
|
|
|
310
|
7
|
100
|
|
|
|
26
|
if ( $type eq "md5WithRSA" ) { # 128 bit digest |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
311
|
2
|
50
|
|
|
|
252
|
if ( eval "require Digest::MD5" ) { |
312
|
|
|
|
|
|
|
# 1.2.840.113549.2.5 for use with 1.840.113549.1.1.4 for RSA signatures |
313
|
2
|
|
|
|
|
28
|
return ASN1Wrap("30","300c06082a864886f70d02050500".ASN1Wrap("04",Digest::MD5::md5_hex($Data))); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
elsif ( $type eq "sha1WithRSA" ) { # 160-bit |
317
|
5
|
50
|
|
|
|
598
|
if ( eval "require Digest::SHA1" ) { |
318
|
|
|
|
|
|
|
# 1.3.14.3.2.26 for use with 1.840.113549.1.1.5 for RSA signatures |
319
|
5
|
|
|
|
|
76
|
return ASN1Wrap("30","300906052b0e03021a0500".ASN1Wrap("04",Digest::SHA1::sha1_hex($Data))); |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
elsif ( $type eq "md2WithRSA" ) { # 128 bit |
323
|
0
|
0
|
|
|
|
0
|
if ( eval "require Digest::MD2" ) { |
324
|
|
|
|
|
|
|
# 1.840.113549.2.2 for use with 1.840.113549.1.1.2 for RSA signatures |
325
|
0
|
|
|
|
|
0
|
return ASN1Wrap("30","300c06082a864886f70d02020500".ASN1Wrap("04",Digest::MD2::md2_hex($Data))); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
# elsif ( $type eq "md4WithRSA" ) { #128 bit |
329
|
|
|
|
|
|
|
# if ( eval "require Digest::MD4" ) { |
330
|
|
|
|
|
|
|
# # 1.840.113549.2.4 for use with 1.840.113549.1.1.3 for RSA signatures |
331
|
|
|
|
|
|
|
# return ASN1Wrap("30","300c06082a864886f70d02040500".ASN1Wrap("04",Digest::MD4::md4_hex($Data))); |
332
|
|
|
|
|
|
|
# } |
333
|
|
|
|
|
|
|
# } |
334
|
0
|
|
|
|
|
0
|
return undef; |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
################################################################ |
338
|
|
|
|
|
|
|
#digestSign("md5WithRSA",$Data,$chex,$nhex); |
339
|
|
|
|
|
|
|
sub digestSign { |
340
|
5
|
|
|
5
|
0
|
24
|
my ($type,$Data,$chex,$nhex) = @_; |
341
|
5
|
|
|
|
|
17
|
my $digestTBS=digestTBS($type,$Data); |
342
|
|
|
|
|
|
|
|
343
|
5
|
50
|
|
|
|
28
|
if ( defined $digestTBS ) { return rsasign($digestTBS,$chex,$nhex); } |
|
5
|
|
|
|
|
29
|
|
344
|
0
|
|
|
|
|
0
|
return ""; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
################################################################ |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub verifySignature { |
350
|
2
|
|
|
2
|
0
|
7
|
my ($digestType,$SignedInfo,$TBS,$chex,$nhex)=@_; |
351
|
2
|
50
|
|
|
|
23
|
return 1 if ( digestTBS($digestType,$TBS) eq rsaverify($SignedInfo,$chex,$nhex) ); |
352
|
0
|
|
|
|
|
|
return 0; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
################################################################ |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
1; |
358
|
|
|
|
|
|
|
__END__ |