1package Crypt::X509; 2use Carp; 3use strict; 4use warnings; 5use Convert::ASN1 qw(:io :debug); 6require Exporter; 7our @ISA = qw(Exporter); 8our %EXPORT_TAGS = ( 'all' => [qw()] ); 9our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); 10#our @EXPORT = qw(error new not_before not_after serial); 11our $VERSION = '0.54'; 12my $parser = undef; 13my $asn = undef; 14my $error = undef; 15our %oid2enchash = ( 16 '1.2.840.113549.1.1.1' => { 'enc' => 'RSA' }, 17 '1.2.840.113549.1.1.2' => { 'enc' => 'RSA', 'hash' => 'MD2' }, 18 '1.2.840.113549.1.1.3' => { 'enc' => 'RSA', 'hash' => 'MD4' }, 19 '1.2.840.113549.1.1.4' => { 'enc' => 'RSA', 'hash' => 'MD5' }, 20 '1.2.840.113549.1.1.5' => { 'enc' => 'RSA', 'hash' => 'SHA1' }, 21 '1.2.840.113549.1.1.6' => { 'enc' => 'OAEP' }, 22 '1.2.840.113549.1.1.11' => { 'enc' => 'RSA', 'hash' => 'SHA256' }, 23 '1.2.840.113549.1.1.12' => { 'enc' => 'RSA', 'hash' => 'SHA384' }, 24 '1.2.840.113549.1.1.13' => { 'enc' => 'RSA', 'hash' => 'SHA512' }, 25 '1.2.840.113549.1.1.14' => { 'enc' => 'RSA', 'hash' => 'SHA224' }, 26 '1.2.840.10045.2.1' => { 'enc' => 'EC' }, 27); 28 29our %oid2attr = ( 30 "2.5.4.3" => "CN", 31 "2.5.4.4" => "SN", 32 "2.5.4.42" => "GN", 33 "2.5.4.5" => "serialNumber", 34 "2.5.4.6" => "C", 35 "2.5.4.7" => "L", 36 "2.5.4.8" => "ST", 37 '2.5.4.9' => 'streetAddress', 38 "2.5.4.10" => "O", 39 "2.5.4.11" => "OU", 40 "1.2.840.113549.1.9.1" => "emailAddress", 41 '1.2.840.113549.1.9.2' => 'unstructuredName', 42 "0.9.2342.19200300.100.1.1" => "UID", 43 "0.9.2342.19200300.100.1.25" => "DC", 44 "0.2.262.1.10.7.20" => "nameDistinguisher", 45 '2.5.4.12' => 'title', 46 '2.5.4.13' => 'description', 47 '2.5.4.14' => 'searchGuide', 48 '2.5.4.15' => 'businessCategory', 49 '2.5.4.16' => 'postalAddress', 50 '2.5.4.17' => 'postalCode', 51 '2.5.4.18' => 'postOfficeBox', 52 '2.5.4.19' => 'physicalDeliveryOfficeName', 53 '2.5.4.20' => 'telephoneNumber', 54 '2.5.4.23' => 'facsimileTelephoneNumber', 55 '2.5.4.41' => 'name', 56 '2.5.4.43' => 'initials', 57 '2.5.4.44' => 'generationQualifier', 58 '2.5.4.45' => 'uniqueIdentifier', 59 '2.5.4.46' => 'dnQualifier', 60 '2.5.4.51' => 'houseIdentifier', 61 '2.5.4.65' => 'pseudonym', 62); 63 64=head1 NAME 65 66Crypt::X509 - Parse a X.509 certificate 67 68=head1 SYNOPSIS 69 70 use Crypt::X509; 71 72 $decoded = Crypt::X509->new( cert => $cert ); 73 74 $subject_email = $decoded->subject_email; 75 print "do not use after: ".gmtime($decoded->not_after)." GMT\n"; 76 77=head1 REQUIRES 78 79Convert::ASN1 80 81=head1 DESCRIPTION 82 83B<Crypt::X509> parses X.509 certificates. Methods are provided for accessing most 84certificate elements. 85 86It is based on the generic ASN.1 module by Graham Barr, on the F<x509decode> 87example by Norbert Klasen and contributions on the perl-ldap-dev-Mailinglist 88by Chriss Ridd. 89 90=head1 CONSTRUCTOR 91 92=head2 new ( OPTIONS ) 93 94Creates and returns a parsed X.509 certificate hash, containing the parsed 95contents. The data is organised as specified in RFC 2459. 96By default only the first ASN.1 Layer is decoded. Nested decoding 97is done automagically through the data access methods. 98 99=over 4 100 101=item cert =E<gt> $certificate 102 103A variable containing the DER formatted certificate to be parsed 104(eg. as stored in C<usercertificate;binary> attribute in an 105LDAP-directory). 106 107=back 108 109 use Crypt::X509; 110 use Data::Dumper; 111 112 $decoded= Crypt::X509->new(cert => $cert); 113 114 print Dumper($decoded); 115 116=cut back 117 118sub new { 119 my ( $class, %args ) = @_; 120 if ( !defined($parser) || $parser->error ) { 121 $parser = _init(); 122 } 123 my $self = $parser->decode( $args{'cert'} ); 124 $self->{"_error"} = $parser->error; 125 bless( $self, $class ); 126 return $self; 127} 128 129=head1 METHODS 130 131=head2 error 132 133Returns the last error from parsing, C<undef> when no error occured. 134This error is updated on deeper parsing with the data access methods. 135 136 137 $decoded= Crypt::X509->new(cert => $cert); 138 if ($decoded->error) { 139 warn "Error on parsing Certificate:".$decoded->error; 140 } 141 142=cut back 143 144sub error { 145 my $self = shift; 146 return $self->{"_error"}; 147} 148 149=head1 DATA ACCESS METHODS 150 151You can access all parsed data directly from the returned hash. For convenience 152the following methods have been implemented to give quick access to the most-used 153certificate attributes. 154 155=head2 version 156 157Returns the certificate's version as an integer. NOTE that version is defined as 158an Integer where 0 = v1, 1 = v2, and 2 = v3. 159 160=cut back 161 162sub version { 163 my $self = shift; 164 return $self->{tbsCertificate}{version}; 165} 166 167=head2 version_string 168 169Returns the certificate's version as a string value. 170 171=cut back 172 173sub version_string { 174 my $self = shift; 175 my $v = $self->{tbsCertificate}{version}; 176 return "v1" if $v == 0; 177 return "v2" if $v == 1; 178 return "v3" if $v == 2; 179} 180 181=head2 serial 182 183returns the serial number (integer or Math::BigInt Object, that gets automagic 184evaluated in scalar context) from the certificate 185 186 187 $decoded= Crypt::X509->new(cert => $cert); 188 print "Certificate has serial number:".$decoded->serial."\n"; 189 190=cut back 191 192sub serial { 193 my $self = shift; 194 return $self->{tbsCertificate}{serialNumber}; 195} 196 197=head2 not_before 198 199returns the GMT-timestamp of the certificate's beginning date of validity. 200If the Certificate holds this Entry in utcTime, it is guaranteed by the 201RFC to been correct. 202 203As utcTime is limited to 32-bit values (like unix-timestamps) newer certificates 204hold the timesamps as "generalTime"-entries. B<The contents of "generalTime"-entries 205are not well defined in the RFC and 206are returned by this module unmodified>, if no utcTime-entry is found. 207 208 209 $decoded= Crypt::X509->new(cert => $cert); 210 if ($decoded->notBefore < time()) { 211 warn "Certificate: not yet valid!"; 212 } 213 214=cut back 215 216sub not_before { 217 my $self = shift; 218 if ( $self->{tbsCertificate}{validity}{notBefore}{utcTime} ) { 219 return $self->{tbsCertificate}{validity}{notBefore}{utcTime}; 220 } elsif ( $self->{tbsCertificate}{validity}{notBefore}{generalTime} ) { 221 return $self->{tbsCertificate}{validity}{notBefore}{generalTime}; 222 } else { 223 return undef; 224 } 225} 226 227=head2 not_after 228 229returns the GMT-timestamp of the certificate's ending date of validity. 230If the Certificate holds this Entry in utcTime, it is guaranteed by the 231RFC to been correct. 232 233As utcTime is limited to 32-bit values (like unix-timestamps) newer certificates 234hold the timesamps as "generalTime"-entries. B<The contents of "generalTime"-entries 235are not well defined in the RFC and 236are returned by this module unmodified>, if no utcTime-entry is found. 237 238 239 $decoded= Crypt::X509->new(cert => $cert); 240 print "Certificate expires on ".gmtime($decoded->not_after)." GMT\n"; 241 242=cut back 243 244sub not_after { 245 my $self = shift; 246 if ( $self->{tbsCertificate}{validity}{notAfter}{utcTime} ) { 247 return $self->{tbsCertificate}{validity}{notAfter}{utcTime}; 248 } elsif ( $self->{tbsCertificate}{validity}{notAfter}{generalTime} ) { 249 return $self->{tbsCertificate}{validity}{notAfter}{generalTime}; 250 } else { 251 return undef; 252 } 253} 254 255=head2 signature 256 257Return's the certificate's signature in binary DER format. 258 259=cut back 260 261sub signature { 262 my $self = shift; 263 return $self->{signature}[0]; 264} 265 266=head2 pubkey 267 268Returns the certificate's public key in binary DER format. 269 270=cut back 271 272sub pubkey { 273 my $self = shift; 274 return $self->{tbsCertificate}{subjectPublicKeyInfo}{subjectPublicKey}[0]; 275} 276 277=head2 pubkey_size 278 279Returns the certificate's public key size. 280 281=cut back 282 283sub pubkey_size { 284 my $self = shift; 285 return $self->{tbsCertificate}{subjectPublicKeyInfo}{subjectPublicKey}[1]; 286} 287 288=head2 pubkey_algorithm 289 290Returns the algorithm as OID string which the public key was created with. 291 292=cut back 293 294sub pubkey_algorithm { 295 my $self = shift; 296 return $self->{tbsCertificate}{subjectPublicKeyInfo}{algorithm}{algorithm}; 297} 298 299=head2 PubKeyAlg 300 301returns the subject public key encryption algorithm (e.g. 'RSA') as string. 302 303 $decoded= Crypt::X509->new(cert => $cert); 304 print "Certificate public key is encrypted with:".$decoded->PubKeyAlg."\n"; 305 306 Example Output: Certificate public key is encrypted with: RSA 307 308=cut back 309 310sub PubKeyAlg { 311 my $self = shift; 312 return $oid2enchash{ $self->{tbsCertificate}{subjectPublicKeyInfo}{algorithm}{algorithm} }->{'enc'}; 313} 314 315=head2 pubkey_components 316 317If this certificate contains an RSA key, this function returns a 318hashref { modulus => $m, exponent => $e) from that key; each value in 319the hash will be an integer scalar or a Math::BigInt object. 320 321For other pubkey types, it returns undef (implementations welcome!). 322 323=cut back 324 325sub pubkey_components { 326 my $self = shift; 327 if ($self->PubKeyAlg() eq 'RSA') { 328 my $parser = _init('RSAPubKeyInfo'); 329 my $values = $parser->decode($self->{tbsCertificate}{subjectPublicKeyInfo}{subjectPublicKey}[0]); 330 return $values; 331 } else { 332 return undef; 333 } 334} 335 336=head2 sig_algorithm 337 338Returns the certificate's signature algorithm as OID string 339 340 $decoded= Crypt::X509->new(cert => $cert); 341 print "Certificate signature is encrypted with:".$decoded->sig_algorithm."\n";> 342 343 Example Output: Certificate signature is encrypted with: 1.2.840.113549.1.1.5 344 345=cut back 346 347sub sig_algorithm { 348 my $self = shift; 349 return $self->{tbsCertificate}{signature}{algorithm}; 350} 351 352=head2 SigEncAlg 353 354returns the signature encryption algorithm (e.g. 'RSA') as string. 355 356 $decoded= Crypt::X509->new(cert => $cert); 357 print "Certificate signature is encrypted with:".$decoded->SigEncAlg."\n"; 358 359 Example Output: Certificate signature is encrypted with: RSA 360 361=cut back 362 363sub SigEncAlg { 364 my $self = shift; 365 return $oid2enchash{ $self->{'signatureAlgorithm'}->{'algorithm'} }->{'enc'}; 366} 367 368=head2 SigHashAlg 369 370returns the signature hashing algorithm (e.g. 'SHA1') as string. 371 372 $decoded= Crypt::X509->new(cert => $cert); 373 print "Certificate signature is hashed with:".$decoded->SigHashAlg."\n"; 374 375 Example Output: Certificate signature is encrypted with: SHA1 376 377=cut back 378 379sub SigHashAlg { 380 my $self = shift; 381 return $oid2enchash{ $self->{'signatureAlgorithm'}->{'algorithm'} }->{'hash'}; 382} 383######################################################################### 384# accessors - subject 385######################################################################### 386 387=head2 Subject 388 389returns a pointer to an array of strings containing subject nameparts of the 390certificate. Attributenames for the most common Attributes are translated 391from the OID-Numbers, unknown numbers are output verbatim. 392 393 $decoded= Convert::ASN1::X509->new($cert); 394 print "DN for this Certificate is:".join(',',@{$decoded->Subject})."\n"; 395 396=cut back 397sub Subject { 398 my $self = shift; 399 my ( $i, $type ); 400 my $subjrdn = $self->{'tbsCertificate'}->{'subject'}->{'rdnSequence'}; 401 $self->{'tbsCertificate'}->{'subject'}->{'dn'} = []; 402 my $subjdn = $self->{'tbsCertificate'}->{'subject'}->{'dn'}; 403 foreach my $subj ( @{$subjrdn} ) { 404 foreach my $i ( @{$subj} ) { 405 if ( $oid2attr{ $i->{'type'} } ) { 406 $type = $oid2attr{ $i->{'type'} }; 407 } else { 408 $type = $i->{'type'}; 409 } 410 my @key = keys( %{ $i->{'value'} } ); 411 push @{$subjdn}, $type . "=" . $i->{'value'}->{ $key[0] }; 412 } 413 } 414 return $subjdn; 415} 416 417 418sub SubjectRaw { 419 420 my $self = shift; 421 my @subject; 422 foreach my $rdn (@{$self->{'tbsCertificate'}->{'subject'}->{'rdnSequence'}}) { 423 my @sequence = map { 424 $_->{format} = (keys %{$_->{value}})[0]; 425 $_->{value} = (values %{$_->{value}})[0]; 426 $_; 427 } @{$rdn}; 428 if (scalar @sequence > 1) { 429 push @subject, \@sequence; 430 } else { 431 push @subject, $sequence[0]; 432 } 433 } 434 return \@subject; 435} 436 437sub _subject_part { 438 my $self = shift; 439 my $oid = shift; 440 my $subjrdn = $self->{'tbsCertificate'}->{'subject'}->{'rdnSequence'}; 441 foreach my $subj ( @{$subjrdn} ) { 442 foreach my $i ( @{$subj} ) { 443 if ( $i->{'type'} eq $oid ) { 444 my @key = keys( %{ $i->{'value'} } ); 445 return $i->{'value'}->{ $key[0] }; 446 } 447 } 448 } 449 return undef; 450} 451 452=head2 subject_country 453 454Returns the string value for subject's country (= the value with the 455 OID 2.5.4.6 or in DN Syntax everything after C<C=>). 456Only the first entry is returned. C<undef> if subject contains no country attribute. 457 458=cut back 459 460sub subject_country { 461 my $self = shift; 462 return _subject_part( $self, '2.5.4.6' ); 463} 464 465=head2 subject_locality 466 467Returns the string value for subject's locality (= the value with the 468OID 2.5.4.7 or in DN Syntax everything after C<l=>). 469Only the first entry is returned. C<undef> if subject contains no locality attribute. 470 471=cut back 472 473sub subject_locality { 474 my $self = shift; 475 return _subject_part( $self, '2.5.4.7' ); 476} 477 478=head2 subject_state 479 480Returns the string value for subject's state or province (= the value with the 481OID 2.5.4.8 or in DN Syntax everything after C<S=>). 482Only the first entry is returned. C<undef> if subject contains no state attribute. 483 484=cut back 485 486sub subject_state { 487 my $self = shift; 488 return _subject_part( $self, '2.5.4.8' ); 489} 490 491=head2 subject_org 492 493Returns the string value for subject's organization (= the value with the 494OID 2.5.4.10 or in DN Syntax everything after C<O=>). 495Only the first entry is returned. C<undef> if subject contains no organization attribute. 496 497=cut back 498 499sub subject_org { 500 my $self = shift; 501 return _subject_part( $self, '2.5.4.10' ); 502} 503 504=head2 subject_ou 505 506Returns the string value for subject's organizational unit (= the value with the 507OID 2.5.4.11 or in DN Syntax everything after C<OU=>). 508Only the first entry is returned. C<undef> if subject contains no organization attribute. 509 510=cut back 511 512sub subject_ou { 513 my $self = shift; 514 return _subject_part( $self, '2.5.4.11' ); 515} 516 517=head2 subject_cn 518 519Returns the string value for subject's common name (= the value with the 520OID 2.5.4.3 or in DN Syntax everything after C<CN=>). 521Only the first entry is returned. C<undef> if subject contains no common name attribute. 522 523=cut back 524 525sub subject_cn { 526 my $self = shift; 527 return _subject_part( $self, '2.5.4.3' ); 528} 529 530=head2 subject_email 531 532Returns the string value for subject's email address (= the value with the 533OID 1.2.840.113549.1.9.1 or in DN Syntax everything after C<emailAddress=>). 534Only the first entry is returned. C<undef> if subject contains no email attribute. 535 536=cut back 537 538sub subject_email { 539 my $self = shift; 540 return _subject_part( $self, '1.2.840.113549.1.9.1' ); 541} 542######################################################################### 543# accessors - issuer 544######################################################################### 545 546=head2 Issuer 547 548returns a pointer to an array of strings building the DN of the certificate 549issuer (= the DN of the CA). Attributenames for the most common Attributes 550are translated from the OID-Numbers, unknown numbers are output verbatim. 551 552 $decoded= Crypt::X509->new($cert); 553 print "Certificate was issued by:".join(',',@{$decoded->Issuer})."\n"; 554 555=cut back 556sub Issuer { 557 my $self = shift; 558 my ( $i, $type ); 559 my $issuerdn = $self->{'tbsCertificate'}->{'issuer'}->{'rdnSequence'}; 560 $self->{'tbsCertificate'}->{'issuer'}->{'dn'} = []; 561 my $issuedn = $self->{'tbsCertificate'}->{'issuer'}->{'dn'}; 562 foreach my $issue ( @{$issuerdn} ) { 563 foreach my $i ( @{$issue} ) { 564 if ( $oid2attr{ $i->{'type'} } ) { 565 $type = $oid2attr{ $i->{'type'} }; 566 } else { 567 $type = $i->{'type'}; 568 } 569 my @key = keys( %{ $i->{'value'} } ); 570 push @{$issuedn}, $type . "=" . $i->{'value'}->{ $key[0] }; 571 } 572 } 573 return $issuedn; 574} 575 576sub _issuer_part { 577 my $self = shift; 578 my $oid = shift; 579 my $issuerrdn = $self->{'tbsCertificate'}->{'issuer'}->{'rdnSequence'}; 580 foreach my $issue ( @{$issuerrdn} ) { 581 foreach my $i ( @{$issue} ) { 582 if ( $i->{'type'} eq $oid ) { 583 my @key = keys( %{ $i->{'value'} } ); 584 return $i->{'value'}->{ $key[0] }; 585 } 586 } 587 } 588 return undef; 589} 590 591=head2 issuer_cn 592 593Returns the string value for issuer's common name (= the value with the 594OID 2.5.4.3 or in DN Syntax everything after C<CN=>). 595Only the first entry is returned. C<undef> if issuer contains no common name attribute. 596 597=cut back 598 599sub issuer_cn { 600 my $self = shift; 601 return _issuer_part( $self, '2.5.4.3' ); 602} 603 604=head2 issuer_country 605 606Returns the string value for issuer's country (= the value with the 607 OID 2.5.4.6 or in DN Syntax everything after C<C=>). 608Only the first entry is returned. C<undef> if issuer contains no country attribute. 609 610=cut back 611 612sub issuer_country { 613 my $self = shift; 614 return _issuer_part( $self, '2.5.4.6' ); 615} 616 617=head2 issuer_state 618 619Returns the string value for issuer's state or province (= the value with the 620OID 2.5.4.8 or in DN Syntax everything after C<S=>). 621Only the first entry is returned. C<undef> if issuer contains no state attribute. 622 623=cut back 624 625sub issuer_state { 626 my $self = shift; 627 return _issuer_part( $self, '2.5.4.8' ); 628} 629 630=head2 issuer_locality 631 632Returns the string value for issuer's locality (= the value with the 633OID 2.5.4.7 or in DN Syntax everything after C<L=>). 634Only the first entry is returned. C<undef> if issuer contains no locality attribute. 635 636=cut back 637 638sub issuer_locality { 639 my $self = shift; 640 return _issuer_part( $self, '2.5.4.7' ); 641} 642 643=head2 issuer_org 644 645Returns the string value for issuer's organization (= the value with the 646OID 2.5.4.10 or in DN Syntax everything after C<O=>). 647Only the first entry is returned. C<undef> if issuer contains no organization attribute. 648 649=cut back 650 651sub issuer_org { 652 my $self = shift; 653 return _issuer_part( $self, '2.5.4.10' ); 654} 655 656=head2 issuer_email 657 658Returns the string value for issuer's email address (= the value with the 659OID 1.2.840.113549.1.9.1 or in DN Syntax everything after C<E=>). 660Only the first entry is returned. C<undef> if issuer contains no email attribute. 661 662=cut back 663 664sub issuer_email { 665 my $self = shift; 666 return _issuer_part( $self, '1.2.840.113549.1.9.1' ); 667} 668######################################################################### 669# accessors - extensions (automate this) 670######################################################################### 671 672=head2 KeyUsage 673 674returns a pointer to an array of strings describing the valid Usages 675for this certificate. C<undef> is returned, when the extension is not set in the 676certificate. 677 678If the extension is marked critical, this is also reported. 679 680 $decoded= Crypt::X509->new(cert => $cert); 681 print "Allowed usages for this Certificate are:\n".join("\n",@{$decoded->KeyUsage})."\n"; 682 683 Example Output: 684 Allowed usages for this Certificate are: 685 critical 686 digitalSignature 687 keyEncipherment 688 dataEncipherment 689 690=cut back 691sub KeyUsage { 692 my $self = shift; 693 my $ext; 694 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 695 if ( !defined $exts ) { return undef; } 696 ; # no extensions in certificate 697 foreach $ext ( @{$exts} ) { 698 if ( $ext->{'extnID'} eq '2.5.29.15' ) { #OID for keyusage 699 my $parsKeyU = _init('KeyUsage'); # get a parser for this 700 my $keyusage = $parsKeyU->decode( $ext->{'extnValue'} ); # decode the value 701 if ( $parsKeyU->error ) { 702 $self->{"_error"} = $parsKeyU->error; 703 return undef; 704 } 705 my $keyu = unpack( "n", ${$keyusage}[0] . ${$keyusage}[1] ) & 0xff80; 706 $ext->{'usage'} = []; 707 if ( $ext->{'critical'} ) { push @{ $ext->{'usage'} }, "critical"; } # mark as critical, if appropriate 708 if ( $keyu & 0x8000 ) { push @{ $ext->{'usage'} }, "digitalSignature"; } 709 if ( $keyu & 0x4000 ) { push @{ $ext->{'usage'} }, "nonRepudiation"; } 710 if ( $keyu & 0x2000 ) { push @{ $ext->{'usage'} }, "keyEncipherment"; } 711 if ( $keyu & 0x1000 ) { push @{ $ext->{'usage'} }, "dataEncipherment"; } 712 if ( $keyu & 0x0800 ) { push @{ $ext->{'usage'} }, "keyAgreement"; } 713 if ( $keyu & 0x0400 ) { push @{ $ext->{'usage'} }, "keyCertSign"; } 714 if ( $keyu & 0x0200 ) { push @{ $ext->{'usage'} }, "cRLSign"; } 715 if ( $keyu & 0x0100 ) { push @{ $ext->{'usage'} }, "encipherOnly"; } 716 if ( $keyu & 0x0080 ) { push @{ $ext->{'usage'} }, "decipherOnly"; } 717 return $ext->{'usage'}; 718 } 719 } 720 return undef; # keyusage extension not found 721} 722 723=head2 ExtKeyUsage 724 725returns a pointer to an array of ExtKeyUsage strings (or OIDs for unknown OIDs) or 726C<undef> if the extension is not filled. OIDs of the following ExtKeyUsages are known: 727serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning 728 729If the extension is marked critical, this is also reported. 730 731 $decoded= Crypt::X509->new($cert); 732 print "ExtKeyUsage extension of this Certificates is: ", join(", ", @{$decoded->ExtKeyUsage}), "\n"; 733 734 Example Output: ExtKeyUsage extension of this Certificates is: critical, serverAuth 735 736=cut back 737our %oid2extkeyusage = ( 738 '1.3.6.1.5.5.7.3.1' => 'serverAuth', 739 '1.3.6.1.5.5.7.3.2' => 'clientAuth', 740 '1.3.6.1.5.5.7.3.3' => 'codeSigning', 741 '1.3.6.1.5.5.7.3.4' => 'emailProtection', 742 '1.3.6.1.5.5.7.3.8' => 'timeStamping', 743 '1.3.6.1.5.5.7.3.9' => 'OCSPSigning', 744); 745 746sub ExtKeyUsage { 747 my $self = shift; 748 my $ext; 749 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 750 if ( !defined $exts ) { return undef; } 751 ; # no extensions in certificate 752 foreach $ext ( @{$exts} ) { 753 if ( $ext->{'extnID'} eq '2.5.29.37' ) { #OID for ExtKeyUsage 754 return $ext->{'oids'} if defined $ext->{'oids'}; 755 my $parsExtKeyUsage = _init('ExtKeyUsageSyntax'); # get a parser for this 756 my $oids = $parsExtKeyUsage->decode( $ext->{'extnValue'} ); # decode the value 757 if ( $parsExtKeyUsage->error ) { 758 $self->{"_error"} = $parsExtKeyUsage->error; 759 return undef; 760 } 761 $ext->{'oids'} = [ map { $oid2extkeyusage{$_} || $_ } @$oids ]; 762 if ( $ext->{'critical'} ) { unshift @{ $ext->{'oids'} }, "critical"; } # mark as critical, if appropriate 763 return $ext->{'oids'}; 764 } 765 } 766 return undef; 767} 768 769=head2 SubjectAltName 770 771returns a pointer to an array of strings containing alternative Subjectnames or 772C<undef> if the extension is not filled. Usually this Extension holds the e-Mail 773address for person-certificates or DNS-Names for server certificates. 774 775It also pre-pends the field type (ie rfc822Name) to the returned value. 776 777 $decoded= Crypt::X509->new($cert); 778 print "E-Mail or Hostnames in this Certificates is/are:", join(", ", @{$decoded->SubjectAltName}), "\n"; 779 780 Example Output: E-Mail or Hostnames in this Certificates is/are: rfc822Name=user@server.com 781 782=cut back 783 784sub SubjectAltName { 785 my $self = shift; 786 my $ext; 787 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 788 if ( !defined $exts ) { return undef; } 789 ; # no extensions in certificate 790 foreach $ext ( @{$exts} ) { 791 if ( $ext->{'extnID'} eq '2.5.29.17' ) { #OID for SubjectAltName 792 my $parsSubjAlt = _init('SubjectAltName'); # get a parser for this 793 my $altnames = $parsSubjAlt->decode( $ext->{'extnValue'} ); # decode the value 794 if ( $parsSubjAlt->error ) { 795 $self->{"_error"} = $parsSubjAlt->error; 796 return undef; 797 } 798 $ext->{'names'} = []; 799 foreach my $name ( @{$altnames} ) { 800 foreach my $value ( keys %{$name} ) { 801 push @{ $ext->{'names'} }, "$value=" . $name->{$value}; 802 } 803 } 804 return $ext->{'names'}; 805 } 806 } 807 return undef; 808} 809 810=head2 DecodedSubjectAltNames 811 812Returns a pointer to an array of strings containing all the alternative subject name 813extensions. 814 815Each such extension is represented as a decoded ASN.1 value, i.e. a pointer to a list 816of pointers to objects, each object having a single key with the type of the alternative 817name and a value specific to that type. 818 819Example return value: 820 821 [ 822 [ 823 { 824 'directoryName' => { 825 'rdnSequence' => [ 826 [ 827 { 828 'value' => { 'utf8String' => 'example' }, 829 'type' => '2.5.4.3' 830 } 831 ] 832 ] 833 } 834 }, 835 { 836 'dNSName' => 'example.com' 837 } 838 ] 839 ] 840 841=cut back 842 843sub DecodedSubjectAltNames { 844 my $self = shift; 845 my @sans = (); 846 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 847 foreach my $ext ( @{$exts} ) { 848 if ( $ext->{'extnID'} eq '2.5.29.17' ) { #OID for subjectAltName 849 my $parsSubjAlt = _init('SubjectAltName'); 850 my $altnames = $parsSubjAlt->decode( $ext->{'extnValue'} ); 851 if ( $parsSubjAlt->error ) { 852 $self->{'_error'} = $parsSubjAlt->error; 853 return undef; 854 } 855 push @sans, $altnames; 856 } 857 } 858 return \@sans; 859} 860 861######################################################################### 862# accessors - authorityCertIssuer 863######################################################################### 864sub _AuthorityKeyIdentifier { 865 my $self = shift; 866 my $ext; 867 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 868 if ( !defined $exts ) { return undef; } 869 ; # no extensions in certificate 870 if ( defined $self->{'tbsCertificate'}{'AuthorityKeyIdentifier'} ) { 871 return ( $self->{'tbsCertificate'}{'AuthorityKeyIdentifier'} ); 872 } 873 foreach $ext ( @{$exts} ) { 874 if ( $ext->{'extnID'} eq '2.5.29.35' ) { #OID for AuthorityKeyIdentifier 875 my $pars = _init('AuthorityKeyIdentifier'); # get a parser for this 876 $self->{'tbsCertificate'}{'AuthorityKeyIdentifier'} = $pars->decode( $ext->{'extnValue'} ); # decode the value 877 if ( $pars->error ) { 878 $self->{"_error"} = $pars->error; 879 return undef; 880 } 881 return $self->{'tbsCertificate'}{'AuthorityKeyIdentifier'}; 882 } 883 } 884 return undef; 885} 886 887=head2 authorityCertIssuer 888 889returns a pointer to an array of strings building the DN of the Authority Cert 890Issuer. Attributenames for the most common Attributes 891are translated from the OID-Numbers, unknown numbers are output verbatim. 892undef if the extension is not set in the certificate. 893 894 $decoded= Crypt::X509->new($cert); 895 print "Certificate was authorised by:".join(',',@{$decoded->authorityCertIssuer})."\n"; 896 897=cut back 898 899sub authorityCertIssuer { 900 my $self = shift; 901 my ( $i, $type ); 902 my $rdn = _AuthorityKeyIdentifier($self); 903 if ( !defined($rdn) ) { 904 return (undef); # we do not have that extension 905 } else { 906 $rdn = $rdn->{'authorityCertIssuer'}[0]->{'directoryName'}; 907 } 908 $rdn->{'dn'} = []; 909 my $dn = $rdn->{'dn'}; 910 $rdn = $rdn->{'rdnSequence'}; 911 foreach my $r ( @{$rdn} ) { 912 $i = @{$r}[0]; 913 if ( $oid2attr{ $i->{'type'} } ) { 914 $type = $oid2attr{ $i->{'type'} }; 915 } else { 916 $type = $i->{'type'}; 917 } 918 my @key = keys( %{ $i->{'value'} } ); 919 push @{$dn}, $type . "=" . $i->{'value'}->{ $key[0] }; 920 } 921 return $dn; 922} 923 924sub _authcert_part { 925 my $self = shift; 926 my $oid = shift; 927 my $rdn = _AuthorityKeyIdentifier($self); 928 if ( !defined($rdn) ) { 929 return (undef); # we do not have that extension 930 } else { 931 $rdn = $rdn->{'authorityCertIssuer'}[0]->{'directoryName'}->{'rdnSequence'}; 932 } 933 foreach my $r ( @{$rdn} ) { 934 my $i = @{$r}[0]; 935 if ( $i->{'type'} eq $oid ) { 936 my @key = keys( %{ $i->{'value'} } ); 937 return $i->{'value'}->{ $key[0] }; 938 } 939 } 940 return undef; 941} 942 943=head2 authority_serial 944 945Returns the authority's certificate serial number. 946 947=cut back 948 949sub authority_serial { 950 my $self = shift; 951 return ( $self->_AuthorityKeyIdentifier )->{authorityCertSerialNumber}; 952} 953 954=head2 key_identifier 955 956Returns the authority key identifier or undef if it is a rooted cert 957 958=cut back 959 960sub key_identifier { 961 my $self = shift; 962 if ( defined $self->_AuthorityKeyIdentifier ) { return ( $self->_AuthorityKeyIdentifier )->{keyIdentifier}; } 963 return undef; 964} 965 966=head2 authority_cn 967 968Returns the authority's ca. 969 970=cut back 971 972sub authority_cn { 973 my $self = shift; 974 return _authcert_part( $self, '2.5.4.3' ); 975} 976 977=head2 authority_country 978 979Returns the authority's country. 980 981=cut back 982 983sub authority_country { 984 my $self = shift; 985 return _authcert_part( $self, '2.5.4.6' ); 986} 987 988=head2 authority_state 989 990Returns the authority's state. 991 992=cut back 993 994sub authority_state { 995 my $self = shift; 996 return _authcert_part( $self, '2.5.4.8' ); 997} 998 999=head2 authority_locality 1000 1001Returns the authority's locality. 1002 1003=cut back 1004 1005sub authority_locality { 1006 my $self = shift; 1007 return _authcert_part( $self, '2.5.4.7' ); 1008} 1009 1010=head2 authority_org 1011 1012Returns the authority's organization. 1013 1014=cut back 1015 1016sub authority_org { 1017 my $self = shift; 1018 return _authcert_part( $self, '2.5.4.10' ); 1019} 1020 1021=head2 authority_email 1022 1023Returns the authority's email. 1024 1025=cut back 1026 1027sub authority_email { 1028 my $self = shift; 1029 return _authcert_part( $self, '1.2.840.113549.1.9.1' ); 1030} 1031 1032=head2 CRLDistributionPoints 1033 1034Returns the CRL distribution points as an array of strings (with one value usually) 1035 1036=cut back 1037 1038sub CRLDistributionPoints { 1039 my $self = shift; 1040 my $ext; 1041 my $exts = $self->{'tbsCertificate'}->{'extensions'}; 1042 if ( !defined $exts ) { return undef; } 1043 ; # no extensions in certificate 1044 foreach $ext ( @{$exts} ) { 1045 if ( $ext->{'extnID'} eq '2.5.29.31' ) { #OID for cRLDistributionPoints 1046 my $crlp = _init('cRLDistributionPoints'); # get a parser for this 1047 my $points = $crlp->decode( $ext->{'extnValue'} ); # decode the value 1048 $points = $points->[0]->{'distributionPoint'}->{'fullName'}; 1049 if ( $crlp->error ) { 1050 $self->{"_error"} = $crlp->error; 1051 return undef; 1052 } 1053 foreach my $name ( @{$points} ) { 1054 push @{ $ext->{'crlpoints'} }, $name->{'uniformResourceIdentifier'}; 1055 } 1056 return $ext->{'crlpoints'}; 1057 } 1058 } 1059 return undef; 1060} 1061 1062=head2 CRLDistributionPoints2 1063 1064Returns the CRL distribution points as an array of hashes (allowing for some variations) 1065 1066=cut back 1067 1068# newer CRL 1069sub CRLDistributionPoints2 { 1070 my $self = shift; 1071 my %CDPs; 1072 my $dp_cnt = 0; # this is a counter used to show which CDP a particular value is listed in 1073 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1074 if ( !defined $extensions ) { return undef; } 1075 ; # no extensions in certificate 1076 for my $extension ( @{$extensions} ) { 1077 if ( $extension->{'extnID'} eq '2.5.29.31' ) { # OID for ARRAY of cRLDistributionPoints 1078 my $parser = _init('cRLDistributionPoints'); # get a parser for CDPs 1079 my $points = $parser->decode( $extension->{'extnValue'} ); # decode the values (returns an array) 1080 for my $each_dp ( @{$points} ) { # this loops through multiple "distributionPoint" values 1081 $dp_cnt++; 1082 for my $each_fullName ( @{ $each_dp->{'distributionPoint'}->{'fullName'} } ) 1083 { # this loops through multiple "fullName" values 1084 if ( exists $each_fullName->{directoryName} ) { 1085 1086 # found a rdnSequence 1087 my $rdn = join ',', reverse @{ my_CRL_rdn( $each_fullName->{directoryName}->{rdnSequence} ) }; 1088 push @{ $CDPs{$dp_cnt} }, "Directory Address: $rdn"; 1089 } elsif ( exists $each_fullName->{uniformResourceIdentifier} ) { 1090 1091 # found a URI 1092 push @{ $CDPs{$dp_cnt} }, "URL: " . $each_fullName->{uniformResourceIdentifier}; 1093 } else { 1094 1095 # found some other type of CDP value 1096 # return undef; 1097 } 1098 } 1099 } 1100 return %CDPs; 1101 } 1102 } 1103 return undef; 1104} 1105 1106sub my_CRL_rdn { 1107 my $crl_rdn = shift; # this should be the passed in 'rdnSequence' array 1108 my ( $i, $type ); 1109 my $crl_dn = []; 1110 for my $part ( @{$crl_rdn} ) { 1111 $i = @{$part}[0]; 1112 if ( $oid2attr{ $i->{'type'} } ) { 1113 $type = $oid2attr{ $i->{'type'} }; 1114 } else { 1115 $type = $i->{'type'}; 1116 } 1117 my @key = keys( %{ $i->{'value'} } ); 1118 push @{$crl_dn}, $type . "=" . $i->{'value'}->{ $key[0] }; 1119 } 1120 return $crl_dn; 1121} 1122 1123=head2 CertificatePolicies 1124 1125Returns the CertificatePolicies as an array of strings 1126 1127=cut back 1128 1129# CertificatePolicies (another extension) 1130sub CertificatePolicies { 1131 my $self = shift; 1132 my $extension; 1133 my $CertPolicies = []; 1134 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1135 if ( !defined $extensions ) { return undef; } 1136 ; # no extensions in certificate 1137 for $extension ( @{$extensions} ) { 1138 if ( $extension->{'extnID'} eq '2.5.29.32' ) { # OID for CertificatePolicies 1139 my $parser = _init('CertificatePolicies'); # get a parser for this 1140 my $policies = $parser->decode( $extension->{'extnValue'} ); # decode the value 1141 for my $policy ( @{$policies} ) { 1142 for my $key ( keys %{$policy} ) { 1143 push @{$CertPolicies}, "$key=" . $policy->{$key}; 1144 } 1145 } 1146 return $CertPolicies; 1147 } 1148 } 1149 return undef; 1150} 1151 1152=head2 EntrustVersionInfo 1153 1154Returns the EntrustVersion as a string 1155 1156 print "Entrust Version: ", $decoded->EntrustVersion, "\n"; 1157 1158 Example Output: Entrust Version: V7.0 1159 1160=cut back 1161 1162# EntrustVersion (another extension) 1163sub EntrustVersion { 1164 my $self = shift; 1165 my $extension; 1166 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1167 if ( !defined $extensions ) { return undef; } 1168 ; # no extensions in certificate 1169 for $extension ( @{$extensions} ) { 1170 if ( $extension->{'extnID'} eq '1.2.840.113533.7.65.0' ) { # OID for EntrustVersionInfo 1171 my $parser = _init('EntrustVersionInfo'); # get a parser for this 1172 my $entrust = $parser->decode( $extension->{'extnValue'} ); # decode the value 1173 return $entrust->{'entrustVers'}; 1174 1175 # not doing anything with the EntrustInfoFlags BIT STRING (yet) 1176 # $entrust->{'entrustInfoFlags'} 1177 } 1178 } 1179 return undef; 1180} 1181 1182=head2 SubjectDirectoryAttributes 1183 1184Returns the SubjectDirectoryAttributes as an array of key = value pairs, to include a data type 1185 1186 print "Subject Directory Attributes: ", join( ', ' , @{ $decoded->SubjectDirectoryAttributes } ), "\n"; 1187 1188 Example Output: Subject Directory Attributes: 1.2.840.113533.7.68.29 = 7 (integer) 1189 1190=cut back 1191 1192# SubjectDirectoryAttributes (another extension) 1193sub SubjectDirectoryAttributes { 1194 my $self = shift; 1195 my $extension; 1196 my $attributes = []; 1197 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1198 if ( !defined $extensions ) { return undef; } 1199 ; # no extensions in certificate 1200 for $extension ( @{$extensions} ) { 1201 if ( $extension->{'extnID'} eq '2.5.29.9' ) { # OID for SubjectDirectoryAttributes 1202 my $parser = _init('SubjectDirectoryAttributes'); # get a parser for this 1203 my $subject_dir_attrs = $parser->decode( $extension->{'extnValue'} ); # decode the value 1204 for my $type ( @{$subject_dir_attrs} ) { 1205 for my $value ( @{ $type->{'values'} } ) { 1206 for my $key ( keys %{$value} ) { 1207 push @{$attributes}, $type->{'type'} . " = " . $value->{$key} . " ($key)"; 1208 } 1209 } 1210 } 1211 return $attributes; 1212 } 1213 } 1214 return undef; 1215} 1216 1217=head2 BasicConstraints 1218 1219Returns the BasicConstraints as an array and the criticallity pre-pended. 1220 1221=cut back 1222 1223# BasicConstraints (another extension) 1224sub BasicConstraints { 1225 my $self = shift; 1226 my $extension; 1227 my $constraints = []; 1228 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1229 if ( !defined $extensions ) { return undef; } 1230 ; # no extensions in certificate 1231 for $extension ( @{$extensions} ) { 1232 if ( $extension->{'extnID'} eq '2.5.29.19' ) { # OID for BasicConstraints 1233 if ( $extension->{'critical'} ) { push @{$constraints}, "critical"; } # mark this as critical as appropriate 1234 my $parser = _init('BasicConstraints'); # get a parser for this 1235 my $basic_constraints = $parser->decode( $extension->{'extnValue'} ); # decode the value 1236 for my $key ( keys %{$basic_constraints} ) { 1237 push @{$constraints}, "$key = " . $basic_constraints->{$key}; 1238 } 1239 return $constraints; 1240 } 1241 } 1242 return undef; 1243} 1244 1245=head2 subject_keyidentifier 1246 1247Returns the subject key identifier from the extensions. 1248 1249=cut back 1250 1251# subject_keyidentifier (another extension) 1252sub subject_keyidentifier { 1253 my $self = shift; 1254 return $self->_SubjectKeyIdentifier; 1255} 1256 1257# _SubjectKeyIdentifier (another extension) 1258sub _SubjectKeyIdentifier { 1259 my $self = shift; 1260 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1261 if ( !defined $extensions ) { return undef; } 1262 ; # no extensions in certificate 1263 if ( defined $self->{'tbsCertificate'}{'SubjectKeyIdentifier'} ) { 1264 return ( $self->{'tbsCertificate'}{'SubjectKeyIdentifier'} ); 1265 } 1266 for my $extension ( @{$extensions} ) { 1267 if ( $extension->{'extnID'} eq '2.5.29.14' ) { # OID for SubjectKeyIdentifier 1268 my $parser = _init('SubjectKeyIdentifier'); # get a parser for this 1269 $self->{'tbsCertificate'}{'SubjectKeyIdentifier'} = $parser->decode( $extension->{'extnValue'} ); # decode the value 1270 if ( $parser->error ) { 1271 $self->{"_error"} = $parser->error; 1272 return undef; 1273 } 1274 return $self->{'tbsCertificate'}{'SubjectKeyIdentifier'}; 1275 } 1276 } 1277 return undef; 1278} 1279 1280=head2 SubjectInfoAccess 1281 1282Returns the SubjectInfoAccess as an array of hashes with key=value pairs. 1283 1284 print "Subject Info Access: "; 1285 if ( defined $decoded->SubjectInfoAccess ) { 1286 my %SIA = $decoded->SubjectInfoAccess; 1287 for my $key ( keys %SIA ) { 1288 print "\n\t$key: \n\t"; 1289 print join( "\n\t" , @{ $SIA{$key} } ), "\n"; 1290 } 1291 } else { print "\n" } 1292 1293 Example Output: 1294 Subject Info Access: 1295 1.3.6.1.5.5.7.48.5: 1296 uniformResourceIdentifier = http://pki.treas.gov/root_sia.p7c 1297 uniformResourceIdentifier = ldap://ldap.treas.gov/ou=US%20Treasury%20Root%20CA,ou=Certification%20Authorities,ou=Department%20of%20the%20Treasury,o=U.S.%20Government,c=US?cACertificate;binary,crossCertificatePair;binary 1298 1299=cut back 1300 1301# SubjectInfoAccess (another extension) 1302sub SubjectInfoAccess { 1303 my $self = shift; 1304 my $extension; 1305 my %SIA; 1306 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1307 if ( !defined $extensions ) { return undef; } 1308 ; # no extensions in certificate 1309 for $extension ( @{$extensions} ) { 1310 if ( $extension->{'extnID'} eq '1.3.6.1.5.5.7.1.11' ) { # OID for SubjectInfoAccess 1311 my $parser = _init('SubjectInfoAccessSyntax'); # get a parser for this 1312 my $subject_info_access = $parser->decode( $extension->{'extnValue'} ); # decode the value 1313 for my $sia ( @{$subject_info_access} ) { 1314 for my $key ( keys %{ $sia->{'accessLocation'} } ) { 1315 push @{ $SIA{ $sia->{'accessMethod'} } }, "$key = " . $sia->{'accessLocation'}{$key}; 1316 } 1317 } 1318 return %SIA; 1319 } 1320 } 1321 return undef; 1322} 1323 1324 1325=head2 PGPExtension 1326 1327Returns the creation timestamp of the corresponding OpenPGP key. 1328(see http://www.imc.org/ietf-openpgp/mail-archive/msg05320.html) 1329 1330 print "PGPExtension: "; 1331 if ( defined $decoded->PGPExtension ) { 1332 my $creationtime = $decoded->PGPExtension; 1333 printf "\n\tcorresponding OpenPGP Creation Time: ", $creationtime, "\n"; 1334 } 1335 1336 Example Output: 1337 PGPExtension: 1338 whatever 1339 1340=cut back 1341 1342# PGPExtension (another extension) 1343sub PGPExtension { 1344 my $self = shift; 1345 my $extension; 1346 my $extensions = $self->{'tbsCertificate'}->{'extensions'}; 1347 if ( !defined $extensions ) { return undef; } 1348 ; # no extensions in certificate 1349 for $extension ( @{$extensions} ) { 1350 if ( $extension->{'extnID'} eq '1.3.6.1.4.1.3401.8.1.1' ) { # OID for PGPExtension 1351 my $parser = _init('PGPExtension'); # get a parser for this 1352 my $pgpextension = $parser->decode( $extension->{'extnValue'} ); # decode the value 1353 if ($pgpextension->{version} != 0) { 1354 $self->{"_error"} = sprintf("got PGPExtension version %d. We only know how to deal with v1 (0)", $pgpextension->{version}); 1355 } else { 1356 foreach my $timetype ('generalTime', 'utcTime') { 1357 return $pgpextension->{keyCreation}->{$timetype} 1358 if exists $pgpextension->{keyCreation}->{$timetype}; 1359 } 1360 } 1361 } 1362 } 1363 return undef; 1364} 1365 1366####################################################################### 1367# internal functions 1368####################################################################### 1369sub _init { 1370 my $what = shift; 1371 if ( ( !defined $what ) || ( '' eq $what ) ) { $what = 'Certificate' } 1372 if ( !defined $asn ) { 1373 $asn = Convert::ASN1->new; 1374 $asn->prepare(<<ASN1); 1375-- ASN.1 from RFC2459 and X.509(2001) 1376-- Adapted for use with Convert::ASN1 1377-- Id: x509decode,v 1.1 2002/02/10 16:41:28 gbarr Exp 1378 1379-- attribute data types -- 1380 1381Attribute ::= SEQUENCE { 1382 type AttributeType, 1383 values SET OF AttributeValue 1384 -- at least one value is required -- 1385 } 1386 1387AttributeType ::= OBJECT IDENTIFIER 1388 1389AttributeValue ::= DirectoryString --ANY 1390 1391AttributeTypeAndValue ::= SEQUENCE { 1392 type AttributeType, 1393 value AttributeValue 1394 } 1395 1396 1397-- naming data types -- 1398 1399Name ::= CHOICE { -- only one possibility for now 1400 rdnSequence RDNSequence 1401 } 1402 1403RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 1404 1405DistinguishedName ::= RDNSequence 1406 1407RelativeDistinguishedName ::= 1408 SET OF AttributeTypeAndValue --SET SIZE (1 .. MAX) OF 1409 1410 1411-- Directory string type -- 1412 1413DirectoryString ::= CHOICE { 1414 teletexString TeletexString, --(SIZE (1..MAX)), 1415 printableString PrintableString, --(SIZE (1..MAX)), 1416 bmpString BMPString, --(SIZE (1..MAX)), 1417 universalString UniversalString, --(SIZE (1..MAX)), 1418 utf8String UTF8String, --(SIZE (1..MAX)), 1419 ia5String IA5String, --added for EmailAddress, 1420 integer INTEGER 1421 } 1422 1423 1424-- certificate and CRL specific structures begin here 1425 1426Certificate ::= SEQUENCE { 1427 tbsCertificate TBSCertificate, 1428 signatureAlgorithm AlgorithmIdentifier, 1429 signature BIT STRING 1430 } 1431 1432TBSCertificate ::= SEQUENCE { 1433 version [0] EXPLICIT Version OPTIONAL, --DEFAULT v1 1434 serialNumber CertificateSerialNumber, 1435 signature AlgorithmIdentifier, 1436 issuer Name, 1437 validity Validity, 1438 subject Name, 1439 subjectPublicKeyInfo SubjectPublicKeyInfo, 1440 issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 1441 -- If present, version shall be v2 or v3 1442 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 1443 -- If present, version shall be v2 or v3 1444 extensions [3] EXPLICIT Extensions OPTIONAL 1445 -- If present, version shall be v3 1446 } 1447 1448Version ::= INTEGER --{ v1(0), v2(1), v3(2) } 1449 1450CertificateSerialNumber ::= INTEGER 1451 1452Validity ::= SEQUENCE { 1453 notBefore Time, 1454 notAfter Time 1455 } 1456 1457Time ::= CHOICE { 1458 utcTime UTCTime, 1459 generalTime GeneralizedTime 1460 } 1461 1462UniqueIdentifier ::= BIT STRING 1463 1464SubjectPublicKeyInfo ::= SEQUENCE { 1465 algorithm AlgorithmIdentifier, 1466 subjectPublicKey BIT STRING 1467 } 1468 1469 1470RSAPubKeyInfo ::= SEQUENCE { 1471 modulus INTEGER, 1472 exponent INTEGER 1473 } 1474 1475Extensions ::= SEQUENCE OF Extension --SIZE (1..MAX) OF Extension 1476 1477Extension ::= SEQUENCE { 1478 extnID OBJECT IDENTIFIER, 1479 critical BOOLEAN OPTIONAL, --DEFAULT FALSE, 1480 extnValue OCTET STRING 1481 } 1482 1483AlgorithmIdentifier ::= SEQUENCE { 1484 algorithm OBJECT IDENTIFIER, 1485 parameters ANY OPTIONAL 1486 } 1487 1488 1489--extensions 1490 1491AuthorityKeyIdentifier ::= SEQUENCE { 1492 keyIdentifier [0] KeyIdentifier OPTIONAL, 1493 authorityCertIssuer [1] GeneralNames OPTIONAL, 1494 authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } 1495 -- authorityCertIssuer and authorityCertSerialNumber shall both 1496 -- be present or both be absent 1497 1498KeyIdentifier ::= OCTET STRING 1499 1500SubjectKeyIdentifier ::= KeyIdentifier 1501 1502-- key usage extension OID and syntax 1503 1504-- id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } 1505 1506KeyUsage ::= BIT STRING --{ 1507-- digitalSignature (0), 1508-- nonRepudiation (1), 1509-- keyEncipherment (2), 1510-- dataEncipherment (3), 1511-- keyAgreement (4), 1512-- keyCertSign (5), 1513-- cRLSign (6), 1514-- encipherOnly (7), 1515-- decipherOnly (8) } 1516 1517 1518-- private key usage period extension OID and syntax 1519 1520-- id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } 1521 1522PrivateKeyUsagePeriod ::= SEQUENCE { 1523 notBefore [0] GeneralizedTime OPTIONAL, 1524 notAfter [1] GeneralizedTime OPTIONAL } 1525 -- either notBefore or notAfter shall be present 1526 1527-- certificate policies extension OID and syntax 1528-- id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } 1529 1530CertificatePolicies ::= SEQUENCE OF PolicyInformation 1531 1532PolicyInformation ::= SEQUENCE { 1533 policyIdentifier CertPolicyId, 1534 policyQualifiers SEQUENCE OF 1535 PolicyQualifierInfo OPTIONAL } 1536 1537CertPolicyId ::= OBJECT IDENTIFIER 1538 1539PolicyQualifierInfo ::= SEQUENCE { 1540 policyQualifierId PolicyQualifierId, 1541 qualifier ANY } --DEFINED BY policyQualifierId } 1542 1543-- Implementations that recognize additional policy qualifiers shall 1544-- augment the following definition for PolicyQualifierId 1545 1546PolicyQualifierId ::= 1547 OBJECT IDENTIFIER --( id-qt-cps | id-qt-unotice ) 1548 1549-- CPS pointer qualifier 1550 1551CPSuri ::= IA5String 1552 1553-- user notice qualifier 1554 1555UserNotice ::= SEQUENCE { 1556 noticeRef NoticeReference OPTIONAL, 1557 explicitText DisplayText OPTIONAL} 1558 1559NoticeReference ::= SEQUENCE { 1560 organization DisplayText, 1561 noticeNumbers SEQUENCE OF INTEGER } 1562 1563DisplayText ::= CHOICE { 1564 visibleString VisibleString , 1565 bmpString BMPString , 1566 utf8String UTF8String } 1567 1568 1569-- policy mapping extension OID and syntax 1570-- id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } 1571 1572PolicyMappings ::= SEQUENCE OF SEQUENCE { 1573 issuerDomainPolicy CertPolicyId, 1574 subjectDomainPolicy CertPolicyId } 1575 1576 1577-- subject alternative name extension OID and syntax 1578-- id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } 1579 1580SubjectAltName ::= GeneralNames 1581 1582GeneralNames ::= SEQUENCE OF GeneralName 1583 1584GeneralName ::= CHOICE { 1585 otherName [0] AnotherName, 1586 rfc822Name [1] IA5String, 1587 dNSName [2] IA5String, 1588 x400Address [3] ANY, --ORAddress, 1589 directoryName [4] Name, 1590 ediPartyName [5] EDIPartyName, 1591 uniformResourceIdentifier [6] IA5String, 1592 iPAddress [7] OCTET STRING, 1593 registeredID [8] OBJECT IDENTIFIER } 1594 1595EntrustVersionInfo ::= SEQUENCE { 1596 entrustVers GeneralString, 1597 entrustInfoFlags EntrustInfoFlags } 1598 1599EntrustInfoFlags::= BIT STRING --{ 1600-- keyUpdateAllowed 1601-- newExtensions (1), -- not used 1602-- pKIXCertificate (2) } -- certificate created by pkix 1603 1604-- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as 1605-- TYPE-IDENTIFIER is not supported in the 88 ASN.1 syntax 1606 1607AnotherName ::= SEQUENCE { 1608 type OBJECT IDENTIFIER, 1609 value [0] EXPLICIT ANY } --DEFINED BY type-id } 1610 1611EDIPartyName ::= SEQUENCE { 1612 nameAssigner [0] DirectoryString OPTIONAL, 1613 partyName [1] DirectoryString } 1614 1615 1616-- issuer alternative name extension OID and syntax 1617-- id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } 1618 1619IssuerAltName ::= GeneralNames 1620 1621 1622-- id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } 1623 1624SubjectDirectoryAttributes ::= SEQUENCE OF Attribute 1625 1626 1627-- basic constraints extension OID and syntax 1628-- id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } 1629 1630BasicConstraints ::= SEQUENCE { 1631 cA BOOLEAN OPTIONAL, --DEFAULT FALSE, 1632 pathLenConstraint INTEGER OPTIONAL } 1633 1634 1635-- name constraints extension OID and syntax 1636-- id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } 1637 1638NameConstraints ::= SEQUENCE { 1639 permittedSubtrees [0] GeneralSubtrees OPTIONAL, 1640 excludedSubtrees [1] GeneralSubtrees OPTIONAL } 1641 1642GeneralSubtrees ::= SEQUENCE OF GeneralSubtree 1643 1644GeneralSubtree ::= SEQUENCE { 1645 base GeneralName, 1646 minimum [0] BaseDistance OPTIONAL, --DEFAULT 0, 1647 maximum [1] BaseDistance OPTIONAL } 1648 1649BaseDistance ::= INTEGER 1650 1651 1652-- policy constraints extension OID and syntax 1653-- id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } 1654 1655PolicyConstraints ::= SEQUENCE { 1656 requireExplicitPolicy [0] SkipCerts OPTIONAL, 1657 inhibitPolicyMapping [1] SkipCerts OPTIONAL } 1658 1659SkipCerts ::= INTEGER 1660 1661 1662-- CRL distribution points extension OID and syntax 1663-- id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} 1664 1665cRLDistributionPoints ::= SEQUENCE OF DistributionPoint 1666 1667DistributionPoint ::= SEQUENCE { 1668 distributionPoint [0] DistributionPointName OPTIONAL, 1669 reasons [1] ReasonFlags OPTIONAL, 1670 cRLIssuer [2] GeneralNames OPTIONAL } 1671 1672DistributionPointName ::= CHOICE { 1673 fullName [0] GeneralNames, 1674 nameRelativeToCRLIssuer [1] RelativeDistinguishedName } 1675 1676ReasonFlags ::= BIT STRING --{ 1677-- unused (0), 1678-- keyCompromise (1), 1679-- cACompromise (2), 1680-- affiliationChanged (3), 1681-- superseded (4), 1682-- cessationOfOperation (5), 1683-- certificateHold (6), 1684-- privilegeWithdrawn (7), 1685-- aACompromise (8) } 1686 1687 1688-- extended key usage extension OID and syntax 1689-- id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} 1690 1691ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId 1692 1693KeyPurposeId ::= OBJECT IDENTIFIER 1694 1695-- extended key purpose OIDs 1696-- id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } 1697-- id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } 1698-- id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } 1699-- id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } 1700-- id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } 1701-- id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } 1702-- id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } 1703-- id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } 1704 1705-- authority info access 1706 1707-- id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } 1708 1709AuthorityInfoAccessSyntax ::= 1710 SEQUENCE OF AccessDescription --SIZE (1..MAX) OF AccessDescription 1711 1712AccessDescription ::= SEQUENCE { 1713 accessMethod OBJECT IDENTIFIER, 1714 accessLocation GeneralName } 1715 1716-- subject info access 1717 1718-- id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 } 1719 1720SubjectInfoAccessSyntax ::= 1721 SEQUENCE OF AccessDescription --SIZE (1..MAX) OF AccessDescription 1722 1723-- pgp creation time 1724 1725PGPExtension ::= SEQUENCE { 1726 version Version, -- DEFAULT v1(0) 1727 keyCreation Time 1728} 1729ASN1 1730 } 1731 my $self = $asn->find($what); 1732 return $self; 1733} 1734 1735=head1 SEE ALSO 1736 1737See the examples of C<Convert::ASN1> and the <perl-ldap@perl.org> Mailing List. 1738An example on how to load certificates can be found in F<t\Crypt-X509.t>. 1739 1740=head1 ACKNOWLEDGEMENTS 1741 1742This module is based on the x509decode script, which was contributed to 1743Convert::ASN1 in 2002 by Norbert Klasen. 1744 1745=head1 AUTHORS 1746 1747Mike Jackson <mj@sci.fi>, 1748Alexander Jung <alexander.w.jung@gmail.com>, 1749Duncan Segrest <duncan@gigageek.info> 1750Oliver Welter <owelter@whiterabbitsecurity.com> 1751 1752=head1 COPYRIGHT 1753 1754Copyright (c) 2005 Mike Jackson <mj@sci.fi>. 1755Copyright (c) 2001-2002 Norbert Klasen, DAASI International GmbH. 1756 1757All rights reserved. This program is free software; you can redistribute 1758it and/or modify it under the same terms as Perl itself. 1759 1760=cut 17611; 1762__END__ 1763