300f310d300b0603550403130446616B65
The above is hex bytes of a DER-encoded X.509 certificate subject. when it is decoded, it should return string "Fake" which is certificate subject. How to do it in perl.
This is easy to decode manually:
"Fake"
Just to double-check, the output of perl -e "print pack qw/H* 300f310d300b0603550403130446616b65/" | openssl asn1parse -inform DER
is:
0:d=0 hl=2 l= 15 cons: SEQUENCE
2:d=1 hl=2 l= 13 cons: SET
4:d=2 hl=2 l= 11 cons: SEQUENCE
6:d=3 hl=2 l= 3 prim: OBJECT :commonName
11:d=3 hl=2 l= 4 prim: PRINTABLESTRING :Fake
Here is an ad hoc parser:
$_ = pack('H*', '300f310d300b0603550403130446616b65');
s@^\x30.\x31.\x30.\x06(.)@@s or die;
length >= ord($1) or die;
substr($_, 0, ord($1)) = "";
s@^\x13.@@s or die;
print "$_\n"; #: Fake
Let's try to parse it with the Perl DER parser library Convert::ASN1. For that we need type definitions for the ->prepare(...)
method. Let's try to guess the types.
I copy-pasted 300f310d300b0603550403130446616b65
to an online ASN.1 decoder: https://lapo.it/asn1js/#MA8xDTALBgNVBAMTBEZha2U , with the following result:
Certificate SEQUENCE (1 elem)
tbsCertificate TBSCertificate [?] SET (1 elem)
serialNumber CertificateSerialNumber [?] SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
PrintableString Fake
The keywords like tbsCertificate and CertificateSerialNumber pointed me to https://www.rfc-editor.org/rfc/rfc5280, but I noticed that the types above were autodetected incorrectly, because CertificateSerialNumber is an INTEGER there. By looking at the RFC document, I guessed these types:
RDNSequence = SEQUENCE (1 elem)
RelativeDistinguishedName = SET (1 elem)
AttributeTypeAndValue = SEQUENCE (2 elem)
type OBJECT IDENTIFIER = 2.5.4.3 commonName (X.520 DN component)
value PrintableString = Fake
The corresponding ASN.1 type definitions extracted from the RFC document https://www.rfc-editor.org/rfc/rfc5280:
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue
}
AttributeType ::= OBJECT IDENTIFIER
AttributeValue ::= ANY -- DEFINED BY AttributeType
Let's try to feed these types to the ->prepare(...)
method of the Perl module Convert::ASN1. With a Google search for Convert::ASN1 RDNSequence
, I've found this example code: https://github.com/gbarr/perl-Convert-ASN1/blob/master/examples/x509decode , based on this and the Convert::ASN1 documentation I wrote the following Perl script:
use warnings;
use strict;
use Convert::ASN1; # sudo apt-get install libconvert-asn1-perl
use Data::Dumper qw();
my $asn = Convert::ASN1->new;
die "fatal: ASN.1 prepare: " . $asn->error unless $asn->prepare(q<
-- ASN.1 from RFC2459 and X.509(2001)
-- Adapted for use with Convert::ASN1
-- 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
}
>);
my $asn_rdns = $asn->find('RDNSequence');
my $der = $_ = pack('H*', '300f310d300b0603550403130446616b65');
my $value = $asn_rdns->decode($der);
die "fatal: ASN.1 decode: " . $asn_rdns->error unless $value;
print Data::Dumper::Dumper($value);
Output:
$VAR1 = [
[
{
'value' => {
'printableString' => 'Fake'
},
'type' => '2.5.4.3'
}
]
];
Here you go.