| 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__ |