#!/usr/bin/perl # $Id: x509decode,v 1.1 2002/02/10 16:41:28 gbarr Exp $ # (c) 2001-2002 Norbert Klasen, DAASI International GmbH. All rights reserved. # This package is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # decode X.509 certificates # # varable naming # Convert::ASN1 objects are prefixed with asn_ # variables holding binary DER content are prefixed with der_ use strict; use Data::Dumper; $Data::Dumper::Indent=1; $Data::Dumper::Quotekeys=1; $Data::Dumper::Useqq=1; use Convert::ASN1 qw(:io :debug); # parse ASN.1 desciptions my $asn = Convert::ASN1->new; $asn->prepare(<error; -- ASN.1 from RFC2459 and X.509(2001) -- Adapted for use with Convert::ASN1 -- $Id: x509decode,v 1.1 2002/02/10 16:41:28 gbarr Exp $ -- attribute data types -- Attribute ::= SEQUENCE { type AttributeType, values SET OF AttributeValue -- at least one value is required -- } AttributeType ::= OBJECT IDENTIFIER AttributeValue ::= DirectoryString --ANY AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } -- naming data types -- Name ::= CHOICE { -- only one possibility for now rdnSequence RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName DistinguishedName ::= RDNSequence RelativeDistinguishedName ::= SET OF AttributeTypeAndValue --SET SIZE (1 .. MAX) OF -- Directory string type -- DirectoryString ::= CHOICE { teletexString TeletexString, --(SIZE (1..MAX)), printableString PrintableString, --(SIZE (1..MAX)), bmpString BMPString, --(SIZE (1..MAX)), universalString UniversalString, --(SIZE (1..MAX)), utf8String UTF8String, --(SIZE (1..MAX)), ia5String IA5String --added for EmailAddress } -- certificate and CRL specific structures begin here Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING } TBSCertificate ::= SEQUENCE { version [0] EXPLICIT Version OPTIONAL, --DEFAULT v1 serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version shall be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version shall be v2 or v3 extensions [3] EXPLICIT Extensions OPTIONAL -- If present, version shall be v3 } Version ::= INTEGER --{ v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } UniqueIdentifier ::= BIT STRING SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } Extensions ::= SEQUENCE OF Extension --SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN OPTIONAL, --DEFAULT FALSE, extnValue OCTET STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY } --extensions AuthorityKeyIdentifier ::= SEQUENCE { keyIdentifier [0] KeyIdentifier OPTIONAL, authorityCertIssuer [1] GeneralNames OPTIONAL, authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } -- authorityCertIssuer and authorityCertSerialNumber shall both -- be present or both be absent KeyIdentifier ::= OCTET STRING SubjectKeyIdentifier ::= KeyIdentifier -- key usage extension OID and syntax -- id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } KeyUsage ::= BIT STRING --{ -- digitalSignature (0), -- nonRepudiation (1), -- keyEncipherment (2), -- dataEncipherment (3), -- keyAgreement (4), -- keyCertSign (5), -- cRLSign (6), -- encipherOnly (7), -- decipherOnly (8) } -- private key usage period extension OID and syntax -- id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } PrivateKeyUsagePeriod ::= SEQUENCE { notBefore [0] GeneralizedTime OPTIONAL, notAfter [1] GeneralizedTime OPTIONAL } -- either notBefore or notAfter shall be present -- certificate policies extension OID and syntax -- id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } CertificatePolicies ::= SEQUENCE OF PolicyInformation PolicyInformation ::= SEQUENCE { policyIdentifier CertPolicyId, policyQualifiers SEQUENCE OF PolicyQualifierInfo } --OPTIONAL } CertPolicyId ::= OBJECT IDENTIFIER PolicyQualifierInfo ::= SEQUENCE { policyQualifierId PolicyQualifierId, qualifier ANY } --DEFINED BY policyQualifierId } -- Implementations that recognize additional policy qualifiers shall -- augment the following definition for PolicyQualifierId PolicyQualifierId ::= OBJECT IDENTIFIER --( id-qt-cps | id-qt-unotice ) -- CPS pointer qualifier CPSuri ::= IA5String -- user notice qualifier UserNotice ::= SEQUENCE { noticeRef NoticeReference OPTIONAL, explicitText DisplayText OPTIONAL} NoticeReference ::= SEQUENCE { organization DisplayText, noticeNumbers SEQUENCE OF INTEGER } DisplayText ::= CHOICE { visibleString VisibleString , bmpString BMPString , utf8String UTF8String } -- policy mapping extension OID and syntax -- id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } PolicyMappings ::= SEQUENCE OF SEQUENCE { issuerDomainPolicy CertPolicyId, subjectDomainPolicy CertPolicyId } -- subject alternative name extension OID and syntax -- id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } SubjectAltName ::= GeneralNames GeneralNames ::= SEQUENCE OF GeneralName GeneralName ::= CHOICE { otherName [0] AnotherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ANY, --ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } -- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as -- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax AnotherName ::= SEQUENCE { type OBJECT IDENTIFIER, value [0] EXPLICIT ANY } --DEFINED BY type-id } EDIPartyName ::= SEQUENCE { nameAssigner [0] DirectoryString OPTIONAL, partyName [1] DirectoryString } -- issuer alternative name extension OID and syntax -- id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } IssuerAltName ::= GeneralNames -- id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } SubjectDirectoryAttributes ::= SEQUENCE OF Attribute -- basic constraints extension OID and syntax -- id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } BasicConstraints ::= SEQUENCE { cA BOOLEAN OPTIONAL, --DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL } -- name constraints extension OID and syntax -- id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL } GeneralSubtrees ::= SEQUENCE OF GeneralSubtree GeneralSubtree ::= SEQUENCE { base GeneralName, minimum [0] BaseDistance OPTIONAL, --DEFAULT 0, maximum [1] BaseDistance OPTIONAL } BaseDistance ::= INTEGER -- policy constraints extension OID and syntax -- id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } PolicyConstraints ::= SEQUENCE { requireExplicitPolicy [0] SkipCerts OPTIONAL, inhibitPolicyMapping [1] SkipCerts OPTIONAL } SkipCerts ::= INTEGER -- CRL distribution points extension OID and syntax -- id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} cRLDistributionPoints ::= SEQUENCE OF DistributionPoint DistributionPoint ::= SEQUENCE { distributionPoint [0] DistributionPointName OPTIONAL, reasons [1] ReasonFlags OPTIONAL, cRLIssuer [2] GeneralNames OPTIONAL } DistributionPointName ::= CHOICE { fullName [0] GeneralNames, nameRelativeToCRLIssuer [1] RelativeDistinguishedName } ReasonFlags ::= BIT STRING --{ -- unused (0), -- keyCompromise (1), -- cACompromise (2), -- affiliationChanged (3), -- superseded (4), -- cessationOfOperation (5), -- certificateHold (6), -- privilegeWithdrawn (7), -- aACompromise (8) } -- extended key usage extension OID and syntax -- id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId KeyPurposeId ::= OBJECT IDENTIFIER -- extended key purpose OIDs -- id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } -- id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } -- id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } -- id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } -- id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } -- id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } -- id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } -- id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } ASN1 # decoders for basic types my $asn_BitString = Convert::ASN1->new(); $asn_BitString->prepare("bitString BIT STRING"); my $asn_OctetString = Convert::ASN1->new(); $asn_OctetString->prepare("octetString OCTET STRING"); # decoders for extensions my %extnoid2asn = ( '2.5.29.9' => $asn->find('SubjectDirectoryAttributes'), '2.5.29.14' => $asn_OctetString, #'SubjectKeyIdentifier', '2.5.29.15' => $asn_BitString, #'keyUsage', '2.5.29.16' => $asn->find('PrivateKeyUsagePeriod'), '2.5.29.17' => $asn->find('SubjectAltName'), '2.5.29.18' => $asn->find('IssuerAltName'), '2.5.29.19' => $asn->find('BasicConstraints'), # '2.5.29.20' => 'cRLNumber', # '2.5.29.21' => 'cRLReasons', # '2.5.29.23' => 'holdInstructionCode', # '2.5.29.24' => 'invalidityDate', # '2.5.29.27' => 'deltaCRLIndicator', # '2.5.29.28' => 'issuingDistributionPoint', # '2.5.29.29' => 'certificateIssuer', '2.5.29.30' => $asn->find('NameConstraints'), '2.5.29.31' => $asn->find('cRLDistributionPoints'), '2.5.29.32' => $asn->find('CertificatePolicies'), '2.5.29.33' => $asn->find('PolicyMappings'), '2.5.29.35' => $asn->find('AuthorityKeyIdentifier'), '2.5.29.36' => $asn->find('PolicyConstraints'), '2.5.29.37' => $asn->find('ExtKeyUsageSyntax'), # '2.5.29.40' => 'cRLStreamIdentifier', # '2.5.29.44' => 'cRLScope', # '2.5.29.45' => 'statusReferrals', # '2.5.29.46' => 'freshestCRL', # '2.5.29.47' => 'orderedList', # '2.5.29.51' => 'baseUpdateTime', # '2.5.29.53' => 'deltaInfo', # '2.5.29.54' => 'inhibitAnyPolicy', # netscape-cert-extensions '2.16.840.1.113730.1.1' => $asn_BitString, # netscape-cert-type '2.16.840.1.113730.1.2' => $asn->find('DirectoryString'), # netscape-base-url '2.16.840.1.113730.1.3' => $asn->find('DirectoryString'), # netscape-revocation-url '2.16.840.1.113730.1.4' => $asn->find('DirectoryString'), # netscape-ca-revocation-url '2.16.840.1.113730.1.7' => $asn->find('DirectoryString'), # netscape-cert-renewal-url '2.16.840.1.113730.1.8' => $asn->find('DirectoryString'), # netscape-ca-policy-url '2.16.840.1.113730.1.12' => $asn->find('DirectoryString'), # netscape-ssl-server-name '2.16.840.1.113730.1.13' => $asn->find('DirectoryString'), # netscape-comment ); my $asn_cert = $asn->find('Certificate'); while ( my $filename = shift ) { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat $filename; open FILE, "<$filename" or die "no such file"; binmode FILE; my $der_cert; read FILE, $der_cert, $size; close FILE; decodeCert( $der_cert ); } sub decodeCert() { my $der_cert = shift; #asn_dump( $der_cert ); my $cert = $asn_cert->decode($der_cert) or die $asn_cert->error; #extensions foreach my $extension ( @{$cert->{'tbsCertificate'}->{'extensions'}} ) { #print "extension: ", $oid2extension{$extension->{'extnID'}}, "\n"; if ( exists $extnoid2asn{$extension->{'extnID'}} ) { $extension->{'extnValue'} = ($extnoid2asn{$extension->{'extnID'}})->decode( $extension->{'extnValue'} ); } else { print STDERR "unknown ", $extension->{'critical'} ? "critical " : "", "extension: ", $extension->{'extnID'}, "\n"; asn_dump( $extension->{'extnValue'} ); } } print Dumper( $cert ); }