Skip to content

Commit

Permalink
Add new method SubjectRaw, add oids, CHANGE oid E => emailAddress
Browse files Browse the repository at this point in the history
Add new commonly used OIDs and rename the mapping for 1.2.840.113549.1.9.1
from the very uncommon short "E" to the long form emailAddress.

Add new method SubjectRaw that exports the subjects rdns with the raw OIDs
  • Loading branch information
oliwel committed Apr 26, 2021
1 parent f8c12e3 commit b40fbd2
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 59 deletions.
154 changes: 97 additions & 57 deletions lib/Crypt/X509.pm
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,52 @@ my $parser = undef;
my $asn = undef;
my $error = undef;
our %oid2enchash = (
'1.2.840.113549.1.1.1' => { 'enc' => 'RSA' },
'1.2.840.113549.1.1.2' => { 'enc' => 'RSA', 'hash' => 'MD2' },
'1.2.840.113549.1.1.3' => { 'enc' => 'RSA', 'hash' => 'MD4' },
'1.2.840.113549.1.1.4' => { 'enc' => 'RSA', 'hash' => 'MD5' },
'1.2.840.113549.1.1.5' => { 'enc' => 'RSA', 'hash' => 'SHA1' },
'1.2.840.113549.1.1.6' => { 'enc' => 'OAEP' },
'1.2.840.113549.1.1.11' => { 'enc' => 'RSA', 'hash' => 'SHA256' },
'1.2.840.113549.1.1.12' => { 'enc' => 'RSA', 'hash' => 'SHA384' },
'1.2.840.113549.1.1.13' => { 'enc' => 'RSA', 'hash' => 'SHA512' },
'1.2.840.113549.1.1.14' => { 'enc' => 'RSA', 'hash' => 'SHA224' }
'1.2.840.113549.1.1.1' => { 'enc' => 'RSA' },
'1.2.840.113549.1.1.2' => { 'enc' => 'RSA', 'hash' => 'MD2' },
'1.2.840.113549.1.1.3' => { 'enc' => 'RSA', 'hash' => 'MD4' },
'1.2.840.113549.1.1.4' => { 'enc' => 'RSA', 'hash' => 'MD5' },
'1.2.840.113549.1.1.5' => { 'enc' => 'RSA', 'hash' => 'SHA1' },
'1.2.840.113549.1.1.6' => { 'enc' => 'OAEP' },
'1.2.840.113549.1.1.11' => { 'enc' => 'RSA', 'hash' => 'SHA256' },
'1.2.840.113549.1.1.12' => { 'enc' => 'RSA', 'hash' => 'SHA384' },
'1.2.840.113549.1.1.13' => { 'enc' => 'RSA', 'hash' => 'SHA512' },
'1.2.840.113549.1.1.14' => { 'enc' => 'RSA', 'hash' => 'SHA224' },
'1.2.840.10045.2.1' => { 'enc' => 'EC' },
);

our %oid2attr = (
"2.5.4.3" => "CN",
"2.5.4.4" => "SN",
"2.5.4.42" => "GN",
"2.5.4.5" => "serialNumber",
"2.5.4.6" => "C",
"2.5.4.7" => "L",
"2.5.4.8" => "ST",
"2.5.4.10" => "O",
"2.5.4.11" => "OU",
"1.2.840.113549.1.9.1" => "E",
"0.9.2342.19200300.100.1.1" => "UID",
"0.9.2342.19200300.100.1.25" => "DC",
"0.2.262.1.10.7.20" => "nameDistinguisher"
"2.5.4.3" => "CN",
"2.5.4.4" => "SN",
"2.5.4.42" => "GN",
"2.5.4.5" => "serialNumber",
"2.5.4.6" => "C",
"2.5.4.7" => "L",
"2.5.4.8" => "ST",
'2.5.4.9' => 'streetAddress',
"2.5.4.10" => "O",
"2.5.4.11" => "OU",
"1.2.840.113549.1.9.1" => "emailAddress",
'1.2.840.113549.1.9.2' => 'unstructuredName',
"0.9.2342.19200300.100.1.1" => "UID",
"0.9.2342.19200300.100.1.25" => "DC",
"0.2.262.1.10.7.20" => "nameDistinguisher",
'2.5.4.12' => 'title',
'2.5.4.13' => 'description',
'2.5.4.14' => 'searchGuide',
'2.5.4.15' => 'businessCategory',
'2.5.4.16' => 'postalAddress',
'2.5.4.17' => 'postalCode',
'2.5.4.18' => 'postOfficeBox',
'2.5.4.19' => 'physicalDeliveryOfficeName',
'2.5.4.20' => 'telephoneNumber',
'2.5.4.23' => 'facsimileTelephoneNumber',
'2.5.4.41' => 'name',
'2.5.4.43' => 'initials',
'2.5.4.44' => 'generationQualifier',
'2.5.4.45' => 'uniqueIdentifier',
'2.5.4.46' => 'dnQualifier',
'2.5.4.51' => 'houseIdentifier',
'2.5.4.65' => 'pseudonym',
);

=head1 NAME
Expand Down Expand Up @@ -394,6 +414,26 @@ sub Subject {
return $subjdn;
}


sub SubjectRaw {

my $self = shift;
my @subject;
foreach my $rdn (@{$self->{'tbsCertificate'}->{'subject'}->{'rdnSequence'}}) {
my @sequence = map {
$_->{format} = (keys %{$_->{value}})[0];
$_->{value} = (values %{$_->{value}})[0];
$_;
} @{$rdn};
if (scalar @sequence > 1) {
push @subject, \@sequence;
} else {
push @subject, $sequence[0];
}
}
return \@subject;
}

sub _subject_part {
my $self = shift;
my $oid = shift;
Expand Down Expand Up @@ -490,7 +530,7 @@ sub subject_cn {
=head2 subject_email
Returns the string value for subject's email address (= the value with the
OID 1.2.840.113549.1.9.1 or in DN Syntax everything after C<E=>).
OID 1.2.840.113549.1.9.1 or in DN Syntax everything after C<emailAddress=>).
Only the first entry is returned. C<undef> if subject contains no email attribute.
=cut back
Expand Down Expand Up @@ -656,7 +696,7 @@ sub KeyUsage {
; # no extensions in certificate
foreach $ext ( @{$exts} ) {
if ( $ext->{'extnID'} eq '2.5.29.15' ) { #OID for keyusage
my $parsKeyU = _init('KeyUsage'); # get a parser for this
my $parsKeyU = _init('KeyUsage'); # get a parser for this
my $keyusage = $parsKeyU->decode( $ext->{'extnValue'} ); # decode the value
if ( $parsKeyU->error ) {
$self->{"_error"} = $parsKeyU->error;
Expand Down Expand Up @@ -695,12 +735,12 @@ If the extension is marked critical, this is also reported.
=cut back
our %oid2extkeyusage = (
'1.3.6.1.5.5.7.3.1' => 'serverAuth',
'1.3.6.1.5.5.7.3.2' => 'clientAuth',
'1.3.6.1.5.5.7.3.3' => 'codeSigning',
'1.3.6.1.5.5.7.3.4' => 'emailProtection',
'1.3.6.1.5.5.7.3.8' => 'timeStamping',
'1.3.6.1.5.5.7.3.9' => 'OCSPSigning',
'1.3.6.1.5.5.7.3.1' => 'serverAuth',
'1.3.6.1.5.5.7.3.2' => 'clientAuth',
'1.3.6.1.5.5.7.3.3' => 'codeSigning',
'1.3.6.1.5.5.7.3.4' => 'emailProtection',
'1.3.6.1.5.5.7.3.8' => 'timeStamping',
'1.3.6.1.5.5.7.3.9' => 'OCSPSigning',
);

sub ExtKeyUsage {
Expand All @@ -712,7 +752,7 @@ sub ExtKeyUsage {
foreach $ext ( @{$exts} ) {
if ( $ext->{'extnID'} eq '2.5.29.37' ) { #OID for ExtKeyUsage
return $ext->{'oids'} if defined $ext->{'oids'};
my $parsExtKeyUsage = _init('ExtKeyUsageSyntax'); # get a parser for this
my $parsExtKeyUsage = _init('ExtKeyUsageSyntax'); # get a parser for this
my $oids = $parsExtKeyUsage->decode( $ext->{'extnValue'} ); # decode the value
if ( $parsExtKeyUsage->error ) {
$self->{"_error"} = $parsExtKeyUsage->error;
Expand Down Expand Up @@ -749,7 +789,7 @@ sub SubjectAltName {
; # no extensions in certificate
foreach $ext ( @{$exts} ) {
if ( $ext->{'extnID'} eq '2.5.29.17' ) { #OID for SubjectAltName
my $parsSubjAlt = _init('SubjectAltName'); # get a parser for this
my $parsSubjAlt = _init('SubjectAltName'); # get a parser for this
my $altnames = $parsSubjAlt->decode( $ext->{'extnValue'} ); # decode the value
if ( $parsSubjAlt->error ) {
$self->{"_error"} = $parsSubjAlt->error;
Expand Down Expand Up @@ -1037,23 +1077,23 @@ sub CRLDistributionPoints2 {
if ( $extension->{'extnID'} eq '2.5.29.31' ) { # OID for ARRAY of cRLDistributionPoints
my $parser = _init('cRLDistributionPoints'); # get a parser for CDPs
my $points = $parser->decode( $extension->{'extnValue'} ); # decode the values (returns an array)
for my $each_dp ( @{$points} ) { # this loops through multiple "distributionPoint" values
for my $each_dp ( @{$points} ) { # this loops through multiple "distributionPoint" values
$dp_cnt++;
for my $each_fullName ( @{ $each_dp->{'distributionPoint'}->{'fullName'} } )
{ # this loops through multiple "fullName" values
{ # this loops through multiple "fullName" values
if ( exists $each_fullName->{directoryName} ) {

# found a rdnSequence
my $rdn = join ',', reverse @{ my_CRL_rdn( $each_fullName->{directoryName}->{rdnSequence} ) };
push @{ $CDPs{$dp_cnt} }, "Directory Address: $rdn";
# found a rdnSequence
my $rdn = join ',', reverse @{ my_CRL_rdn( $each_fullName->{directoryName}->{rdnSequence} ) };
push @{ $CDPs{$dp_cnt} }, "Directory Address: $rdn";
} elsif ( exists $each_fullName->{uniformResourceIdentifier} ) {

# found a URI
push @{ $CDPs{$dp_cnt} }, "URL: " . $each_fullName->{uniformResourceIdentifier};
# found a URI
push @{ $CDPs{$dp_cnt} }, "URL: " . $each_fullName->{uniformResourceIdentifier};
} else {

# found some other type of CDP value
# return undef;
# found some other type of CDP value
# return undef;
}
}
}
Expand Down Expand Up @@ -1164,7 +1204,7 @@ sub SubjectDirectoryAttributes {
for my $type ( @{$subject_dir_attrs} ) {
for my $value ( @{ $type->{'values'} } ) {
for my $key ( keys %{$value} ) {
push @{$attributes}, $type->{'type'} . " = " . $value->{$key} . " ($key)";
push @{$attributes}, $type->{'type'} . " = " . $value->{$key} . " ($key)";
}
}
}
Expand All @@ -1191,7 +1231,7 @@ sub BasicConstraints {
for $extension ( @{$extensions} ) {
if ( $extension->{'extnID'} eq '2.5.29.19' ) { # OID for BasicConstraints
if ( $extension->{'critical'} ) { push @{$constraints}, "critical"; } # mark this as critical as appropriate
my $parser = _init('BasicConstraints'); # get a parser for this
my $parser = _init('BasicConstraints'); # get a parser for this
my $basic_constraints = $parser->decode( $extension->{'extnValue'} ); # decode the value
for my $key ( keys %{$basic_constraints} ) {
push @{$constraints}, "$key = " . $basic_constraints->{$key};
Expand Down Expand Up @@ -1310,14 +1350,14 @@ sub PGPExtension {
if ( $extension->{'extnID'} eq '1.3.6.1.4.1.3401.8.1.1' ) { # OID for PGPExtension
my $parser = _init('PGPExtension'); # get a parser for this
my $pgpextension = $parser->decode( $extension->{'extnValue'} ); # decode the value
if ($pgpextension->{version} != 0) {
$self->{"_error"} = sprintf("got PGPExtension version %d. We only know how to deal with v1 (0)", $pgpextension->{version});
} else {
foreach my $timetype ('generalTime', 'utcTime') {
return $pgpextension->{keyCreation}->{$timetype}
if exists $pgpextension->{keyCreation}->{$timetype};
}
}
if ($pgpextension->{version} != 0) {
$self->{"_error"} = sprintf("got PGPExtension version %d. We only know how to deal with v1 (0)", $pgpextension->{version});
} else {
foreach my $timetype ('generalTime', 'utcTime') {
return $pgpextension->{keyCreation}->{$timetype}
if exists $pgpextension->{keyCreation}->{$timetype};
}
}
}
}
return undef;
Expand Down Expand Up @@ -1542,14 +1582,14 @@ SubjectAltName ::= GeneralNames
GeneralNames ::= SEQUENCE OF GeneralName
GeneralName ::= CHOICE {
otherName [0] AnotherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
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,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }
EntrustVersionInfo ::= SEQUENCE {
Expand Down Expand Up @@ -1588,7 +1628,7 @@ SubjectDirectoryAttributes ::= SEQUENCE OF Attribute
-- id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
BasicConstraints ::= SEQUENCE {
cA BOOLEAN OPTIONAL, --DEFAULT FALSE,
cA BOOLEAN OPTIONAL, --DEFAULT FALSE,
pathLenConstraint INTEGER OPTIONAL }
Expand Down
11 changes: 9 additions & 2 deletions t/Crypt-X509.t
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl Crypt-X509.t'
use Test::More tests => 70;
use Test::More tests => 71;
use Math::BigInt;
BEGIN { use_ok('Crypt::X509') }

Expand All @@ -21,12 +21,18 @@ is( join( ':', @{ $decoded2->ExtKeyUsage } ), "clientAuth:emailProtection", 'Ext
# this has also to work twice
is( join( ':', @{ $decoded2->KeyUsage } ), "critical:digitalSignature:keyEncipherment:dataEncipherment", 'Keyusagecheck again' );
is( join( ':', @{ $decoded2->ExtKeyUsage } ), "clientAuth:emailProtection", 'Extkeyusagecheck again' );
is( join( ',', @{ $decoded2->Subject } ), "E=alexander.jung\@allianz.de,C=DE,O=Allianz Group,CN=Alexander Jung", 'Subject parsed' );
is( join( ',', @{ $decoded2->Subject } ), "emailAddress=alexander.jung\@allianz.de,C=DE,O=Allianz Group,CN=Alexander Jung", 'Subject parsed' );
is( $decoded2->subject_country, "DE", "Subject_country" );
is( $decoded2->subject_state, undef, "Subject_state" );
is( $decoded2->subject_org, "Allianz Group", "Subject_org" );
is( $decoded2->subject_ou, undef, "Subject_ou" );
is( $decoded2->subject_email, "alexander.jung\@allianz.de", "Subject_email" );
is_deeply( $decoded2->SubjectRaw, [
{'type' => '1.2.840.113549.1.9.1','value' => '[email protected]','format' => 'ia5String'},
{'type' => '2.5.4.6','value' => 'DE','format' => 'printableString'},
{'type' => '2.5.4.10','value' => 'Allianz Group','format' => 'printableString'},
{'type' => '2.5.4.3','value' => 'Alexander Jung','format' => 'printableString'}
]);
is( join( ',', @{ $decoded2->Issuer } ), "C=DE,O=Allianz Group,CN=Allianz Dresdner CA", "Issuer Parsed");
is( $decoded2->issuer_cn, "Allianz Dresdner CA", "Issuer_cn" );
is( $decoded2->issuer_country, "DE", "Isssuer_country" );
Expand All @@ -41,6 +47,7 @@ is( length( $decoded2->signature ), 256, "Signature Length" )
is( join( ',', @{ $decoded2->SubjectAltName } ), "rfc822Name=alexander.jung\@allianz.de", 'SubjectAltName parsed' );
is_deeply( $decoded2->DecodedSubjectAltNames, [[{rfc822Name => '[email protected]'}]], 'DecodedSubjectAltName parsed' );


$cert = loadcert('t/aj2.cer');
$decoded3 = Crypt::X509->new( cert => $cert );
is( $decoded3->error, undef, 'decode successful' );
Expand Down

0 comments on commit b40fbd2

Please sign in to comment.