Examples ======== .. contents:: Schema definition ----------------- Let's try to parse X.509 certificate. We have to define our structures based on ASN.1 schema descriptions. .. list-table:: :header-rows: 1 * - ASN.1 specification - pyderasn's code * - :: Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING } - :: class Certificate(Sequence): schema = ( ("tbsCertificate", TBSCertificate()), ("signatureAlgorithm", AlgorithmIdentifier()), ("signatureValue", BitString()), ) * - :: AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } - :: class AlgorithmIdentifier(Sequence): schema = ( ("algorithm", ObjectIdentifier()), ("parameters", Any(optional=True)), ) * - :: TBSCertificate ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, extensions [3] EXPLICIT Extensions OPTIONAL } - :: class TBSCertificate(Sequence): schema = ( ("version", Version(expl=tag_ctxc(0), default="v1")), ("serialNumber", CertificateSerialNumber()), ("signature", AlgorithmIdentifier()), ("issuer", Name()), ("validity", Validity()), ("subject", Name()), ("subjectPublicKeyInfo", SubjectPublicKeyInfo()), ("issuerUniqueID", UniqueIdentifier(impl=tag_ctxp(1), optional=True)), ("subjectUniqueID", UniqueIdentifier(impl=tag_ctxp(2), optional=True)), ("extensions", Extensions(expl=tag_ctxc(3), optional=True)), ) * - :: Version ::= INTEGER { v1(0), v2(1), v3(2) } - :: class Version(Integer): schema = (("v1", 0), ("v2", 1), ("v3", 2)) * - :: CertificateSerialNumber ::= INTEGER - :: class CertificateSerialNumber(Integer): pass * - :: Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } - :: class Validity(Sequence): schema = ( ("notBefore", Time()), ("notAfter", Time()), ) class Time(Choice): schema = ( ("utcTime", UTCTime()), ("generalTime", GeneralizedTime()), ) * - :: SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } - :: class SubjectPublicKeyInfo(Sequence): schema = ( ("algorithm", AlgorithmIdentifier()), ("subjectPublicKey", BitString()), ) * - :: UniqueIdentifier ::= BIT STRING - :: class UniqueIdentifier(BitString): pass * - :: Name ::= CHOICE { rdnSequence RDNSequence } 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 - :: class Name(Choice): schema = (("rdnSequence", RDNSequence()),) class RDNSequence(SequenceOf): schema = RelativeDistinguishedName() class RelativeDistinguishedName(SetOf): schema = AttributeTypeAndValue() bounds = (1, float("+inf")) class AttributeTypeAndValue(Sequence): schema = ( ("type", AttributeType()), ("value", AttributeValue()), ) class AttributeType(ObjectIdentifier): pass class AttributeValue(Any): pass * - :: Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } - :: class Extensions(SequenceOf): schema = Extension() bounds = (1, float("+inf")) class Extension(Sequence): schema = ( ("extnID", ObjectIdentifier()), ("critical", Boolean(default=False)), ("extnValue", OctetString()), ) We are ready to decode PayPal's certificate from Go `encoding/asn1 `__ test suite (assuming that it's DER encoded representation is already in ``raw`` variable):: >>> crt, tail = Certificate().decode(raw) >>> crt Certificate SEQUENCE[TBSCertificate SEQUENCE[[0] EXPLICIT Version INTEGER v3 OPTIONAL, CertificateSerialNumber INTEGER 61595, AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.840.113549.1.1.5... Pretty printing --------------- There is huge output. Let's pretty print it:: >>> print(pprint(crt)) 0 [1,3,1604] Certificate SEQUENCE 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL . . . . 05:00 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY . . . . . . . 13:02:45:53 [...] 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL . . . 05:00 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C [...] Trailing data: 0a Let's parse that output, human:: 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ 0 1 2 3 4 5 6 7 8 9 10 11 :: 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 ^ ^ ^ ^ ^ ^ ^ ^ 0 2 3 4 5 6 9 10 :: 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence ^ ^ ^ ^ ^ ^ ^ ^ ^ 0 2 3 4 5 6 8 9 10 :0: Offset of the object, where its DER encoding begins. Pay attention that it does **not** include explicit tag. :1: If explicit tag exists, then this is its length (tag + encoded length). :2: Length of object's tag. For example CHOICE does not have its own tag, so it is zero. :3: Length of encoded length. :4: Length of encoded value. :5: Visual indentation to show the depth of object in the hierarchy. :6: Object's name inside SEQUENCE/CHOICE. :7: If either IMPLICIT or EXPLICIT tag is set, then it will be shown here. "IMPLICIT" is omitted. :8: Object's class name, if set. Omitted if it is just an ordinary simple value (like with ``algorithm`` in example above). :9: Object's ASN.1 type. :10: Object's value, if set. Can consist of multiple words (like OCTET/BIT STRINGs above). We see ``v3`` value in Version, because it is named. ``rdnSequence`` is the choice of CHOICE type. :11: Possible other flags like OPTIONAL and DEFAULT, if value equals to the default one, specified in the schema. As command line utility ----------------------- You can decode DER files using command line abilities and get the same picture as above by executing:: % pyderasn.py --schema tests.test_crts:Certificate path/to/file If there is no schema for you file, then you can try parsing it without, but of course IMPLICIT tags will often make it impossible. But result is good enough for the certificate above:: % pyderasn.py path/to/file 0 [1,3,1604] . >: SEQUENCE OF 4 [1,3,1453] . . >: SEQUENCE OF 8 [0,0, 5] . . . . >: [0] ANY . . . . . A0:03:02:01:02 13 [1,1, 3] . . . . >: INTEGER 61595 18 [1,1, 13] . . . . >: SEQUENCE OF 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 31 [1,1, 0] . . . . . . >: NULL 33 [1,3, 274] . . . . >: SEQUENCE OF 37 [1,1, 11] . . . . . . >: SET OF 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES [...] 1409 [1,1, 50] . . . . . . >: SEQUENCE OF 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63 . . . . . . . . . 61:2E:63:6F:6D:2F 1461 [1,1, 13] . . >: SEQUENCE OF 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 1474 [1,1, 0] . . . . >: NULL 1476 [1,2, 129] . . >: BIT STRING 1024 bits . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C [...] If you have got dictionaries with ObjectIdentifiers, like example one from ``tests/test_crts.py``:: some_oids = { "1.2.840.113549.1.1.1": "id-rsaEncryption", "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption", [...] "2.5.4.10": "id-at-organizationName", "2.5.4.11": "id-at-organizationalUnitName", } then you can pass it to pretty printer to see human readable OIDs:: % pyderasn.py --oids tests.test_crts:some_oids path/to/file [...] 37 [1,1, 11] . . . . . . >: SET OF 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6) 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES 50 [1,1, 18] . . . . . . >: SET OF 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8) 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona 70 [1,1, 18] . . . . . . >: SET OF 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7) 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona [...] Descriptive errors ------------------ If you have bad DER, then errors will show you where error occurred:: % pyderasn.py --schema tests.test_crts:Certificate path/to/bad/file Traceback (most recent call last): [...] pyderasn.DecodeError: UTCTime (tbsCertificate.validity.notAfter.utcTime) (at 328) invalid UTCTime format :: % pyderasn.py path/to/bad/file [...] pyderasn.DecodeError: UTCTime (0.SequenceOf.4.SequenceOf.1.UTCTime) (at 328) invalid UTCTime format You can see, so called, decode path inside the structures: ``tbsCertificate`` -> ``validity`` -> ``notAfter`` -> ``utcTime`` and that object at byte 328 is invalid. X.509 certificate creation -------------------------- Let's create some simple self-signed X.509 certificate from the ground:: tbs = TBSCertificate() tbs["serialNumber"] = CertificateSerialNumber(10143011886257155224) sign_algo_id = AlgorithmIdentifier() sign_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.5") sign_algo_id["parameters"] = Any(Null()) tbs["signature"] = sign_algo_id rdnSeq = RDNSequence() for oid, klass, text in ( ("2.5.4.6", PrintableString, "XX"), ("2.5.4.8", PrintableString, "Some-State"), ("2.5.4.7", PrintableString, "City"), ("2.5.4.10", PrintableString, "Internet Widgits Pty Ltd"), ("2.5.4.3", PrintableString, "false.example.com"), ("1.2.840.113549.1.9.1", IA5String, "false@example.com"), ): attr = AttributeTypeAndValue() attr["type"] = AttributeType(oid) attr["value"] = AttributeValue(klass(text)) rdn = RelativeDistinguishedName() rdn.append(attr) rdnSeq.append(rdn) issuer = Name() issuer["rdnSequence"] = rdnSeq tbs["issuer"] = issuer tbs["subject"] = issuer validity = Validity() validity["notBefore"] = Time(("utcTime", UTCTime(datetime(2009, 10, 8, 0, 25, 53)))) validity["notAfter"] = Time(("utcTime", UTCTime(datetime(2010, 10, 8, 0, 25, 53)))) tbs["validity"] = validity spki = SubjectPublicKeyInfo() spki_algo_id = sign_algo_id.copy() spki_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.1") spki["algorithm"] = spki_algo_id spki["subjectPublicKey"] = BitString(hexdec("".join(( "3048024100cdb7639c3278f006aa277f6eaf42902b592d8cbcbe38a1c92ba4695", "a331b1deadeadd8e9a5c27e8c4c2fd0a8889657722a4f2af7589cf2c77045dc8f", "deec357d0203010001", )))) tbs["subjectPublicKeyInfo"] = spki crt = Certificate() crt["tbsCertificate"] = tbs crt["signatureAlgorithm"] = sign_algo_id crt["signatureValue"] = BitString(hexdec("".join(( "a67b06ec5ece92772ca413cba3ca12568fdc6c7b4511cd40a7f659980402df2b", "998bb9a4a8cbeb34c0f0a78cf8d91ede14a5ed76bf116fe360aafa8821490435", )))) crt.encode() And we will get the same certificate used in Go's library tests.