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 = Certificate().decod(raw) >>> crt Certificate SEQUENCE[tbsCertificate: TBSCertificate SEQUENCE[ version: [0] EXPLICIT Version INTEGER v3 OPTIONAL; serialNumber: CertificateSerialNumber INTEGER 61595; signature: AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.840.113549.1.1.5... .. seealso:: :ref:`Better pretty printing ` As command line utility ----------------------- .. seealso:: :ref:`cmdline` Descriptive errors ------------------ If you have bad DER/BER, then errors will show you where error occurred:: $ python -m pyderasn --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 :: $ python -m pyderasn 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(( ("algorithm", ObjectIdentifier("1.2.840.113549.1.1.5")), ("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"), ): rdnSeq.append( RelativeDistinguishedName(( AttributeTypeAndValue(( ("type", AttributeType(oid)), ("value", AttributeValue(klass(text))), )), )) ) issuer = Name(("rdnSequence", rdnSeq)) tbs["issuer"] = issuer tbs["subject"] = issuer validity = Validity(( ("notBefore", Time( ("utcTime", UTCTime(datetime(2009, 10, 8, 0, 25, 53))), )), ("notAfter", Time( ("utcTime", UTCTime(datetime(2010, 10, 8, 0, 25, 53))), )), )) tbs["validity"] = validity spki = SubjectPublicKeyInfo() spki_algo_id = copy(sign_algo_id) 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. DEFINED BY fields ----------------- Here is only very simple example how you can define Any/OctetString fields automatic decoding:: class AttributeTypeAndValue(Sequence): schema = ( ((("type",), AttributeType(defines=((("value",), { id_at_countryName: PrintableString(), id_at_stateOrProvinceName: PrintableString(), id_at_localityName: PrintableString(), id_at_organizationName: PrintableString(), id_at_commonName: PrintableString(), }),)))), ("value", AttributeValue()), ) And when you will try to decode X.509 certificate with it, your pretty printer will show:: 34 [0,0, 149] . . issuer: Name CHOICE rdnSequence 34 [1,2, 146] . . . 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 id-at-countryName (2.5.4.6) 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY . . . . . . . 13:02:58:58 46 [1,1, 2] . . . . . . . DEFINED BY (2.5.4.6): PrintableString PrintableString XX 50 [1,1, 19] . . . . 1: RelativeDistinguishedName SET OF 52 [1,1, 17] . . . . . 0: AttributeTypeAndValue SEQUENCE 54 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8) 59 [0,0, 12] . . . . . . value: [UNIV 19] AttributeValue ANY . . . . . . . 13:0A:53:6F:6D:65:2D:53:74:61:74:65 59 [1,1, 10] . . . . . . . DEFINED BY (2.5.4.8): PrintableString PrintableString Some-State 71 [1,1, 13] . . . . 2: RelativeDistinguishedName SET OF 73 [1,1, 11] . . . . . 0: AttributeTypeAndValue SEQUENCE 75 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-localityName (2.5.4.7) 80 [0,0, 6] . . . . . . value: [UNIV 19] AttributeValue ANY . . . . . . . 13:04:43:69:74:79 80 [1,1, 4] . . . . . . . DEFINED BY (2.5.4.7): PrintableString PrintableString City 86 [1,1, 33] . . . . 3: RelativeDistinguishedName SET OF 88 [1,1, 31] . . . . . 0: AttributeTypeAndValue SEQUENCE 90 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-organizationName (2.5.4.10) 95 [0,0, 26] . . . . . . value: [UNIV 19] AttributeValue ANY . . . . . . . 13:18:49:6E:74:65:72:6E:65:74:20:57:69:64:67:69 . . . . . . . 74:73:20:50:74:79:20:4C:74:64 95 [1,1, 24] . . . . . . . DEFINED BY (2.5.4.10): PrintableString PrintableString Internet Widgits Pty Ltd .. seealso:: :ref:`definedby` Streaming and dealing with huge structures ------------------------------------------ .. seealso:: :ref:`streaming`