9 Let's try to parse X.509 certificate. We have to define our structures
10 based on ASN.1 schema descriptions.
15 * - ASN.1 specification
19 Certificate ::= SEQUENCE {
20 tbsCertificate TBSCertificate,
21 signatureAlgorithm AlgorithmIdentifier,
22 signatureValue BIT STRING }
25 class Certificate(Sequence):
27 ("tbsCertificate", TBSCertificate()),
28 ("signatureAlgorithm", AlgorithmIdentifier()),
29 ("signatureValue", BitString()),
33 AlgorithmIdentifier ::= SEQUENCE {
34 algorithm OBJECT IDENTIFIER,
35 parameters ANY DEFINED BY algorithm OPTIONAL }
38 class AlgorithmIdentifier(Sequence):
40 ("algorithm", ObjectIdentifier()),
41 ("parameters", Any(optional=True)),
45 TBSCertificate ::= SEQUENCE {
46 version [0] EXPLICIT Version DEFAULT v1,
47 serialNumber CertificateSerialNumber,
48 signature AlgorithmIdentifier,
52 subjectPublicKeyInfo SubjectPublicKeyInfo,
53 issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
54 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
55 extensions [3] EXPLICIT Extensions OPTIONAL }
58 class TBSCertificate(Sequence):
60 ("version", Version(expl=tag_ctxc(0), default="v1")),
61 ("serialNumber", CertificateSerialNumber()),
62 ("signature", AlgorithmIdentifier()),
64 ("validity", Validity()),
66 ("subjectPublicKeyInfo", SubjectPublicKeyInfo()),
67 ("issuerUniqueID", UniqueIdentifier(impl=tag_ctxp(1), optional=True)),
68 ("subjectUniqueID", UniqueIdentifier(impl=tag_ctxp(2), optional=True)),
69 ("extensions", Extensions(expl=tag_ctxc(3), optional=True)),
73 Version ::= INTEGER { v1(0), v2(1), v3(2) }
76 class Version(Integer):
77 schema = (("v1", 0), ("v2", 1), ("v3", 2))
80 CertificateSerialNumber ::= INTEGER
83 class CertificateSerialNumber(Integer):
87 Validity ::= SEQUENCE {
92 generalTime GeneralizedTime }
95 class Validity(Sequence):
97 ("notBefore", Time()),
102 ("utcTime", UTCTime()),
103 ("generalTime", GeneralizedTime()),
107 SubjectPublicKeyInfo ::= SEQUENCE {
108 algorithm AlgorithmIdentifier,
109 subjectPublicKey BIT STRING }
112 class SubjectPublicKeyInfo(Sequence):
114 ("algorithm", AlgorithmIdentifier()),
115 ("subjectPublicKey", BitString()),
119 UniqueIdentifier ::= BIT STRING
122 class UniqueIdentifier(BitString):
126 Name ::= CHOICE { rdnSequence RDNSequence }
128 RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
130 RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
132 AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue }
134 AttributeType ::= OBJECT IDENTIFIER
136 AttributeValue ::= ANY -- DEFINED BY AttributeType
140 schema = (("rdnSequence", RDNSequence()),)
141 class RDNSequence(SequenceOf):
142 schema = RelativeDistinguishedName()
143 class RelativeDistinguishedName(SetOf):
144 schema = AttributeTypeAndValue()
145 bounds = (1, float("+inf"))
146 class AttributeTypeAndValue(Sequence):
148 ("type", AttributeType()),
149 ("value", AttributeValue()),
151 class AttributeType(ObjectIdentifier):
153 class AttributeValue(Any):
157 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
159 Extension ::= SEQUENCE {
160 extnID OBJECT IDENTIFIER,
161 critical BOOLEAN DEFAULT FALSE,
162 extnValue OCTET STRING
166 class Extensions(SequenceOf):
168 bounds = (1, float("+inf"))
169 class Extension(Sequence):
171 ("extnID", ObjectIdentifier()),
172 ("critical", Boolean(default=False)),
173 ("extnValue", OctetString()),
176 We are ready to decode PayPal's certificate from Go `encoding/asn1
177 <https://golang.org/pkg/encoding/asn1/>`__ test suite (assuming that
178 it's DER encoded representation is already in ``raw`` variable)::
180 >>> crt, tail = Certificate().decode(raw)
182 Certificate SEQUENCE[tbsCertificate: TBSCertificate SEQUENCE[
183 version: [0] EXPLICIT Version INTEGER v3 OPTIONAL;
184 serialNumber: CertificateSerialNumber INTEGER 61595;
185 signature: AlgorithmIdentifier SEQUENCE[OBJECT IDENTIFIER 1.2.840.113549.1.1.5...
190 There is huge output. Let's pretty print it::
192 >>> print(pprint(crt))
193 0 [1,3,1604] Certificate SEQUENCE
194 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE
195 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
196 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595
197 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE
198 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
199 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL
201 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
202 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF
203 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
204 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
205 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6
206 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
207 . . . . . . . 13:02:45:53
209 1461 [1,1, 13] . signatureAlgorithm: AlgorithmIdentifier SEQUENCE
210 1463 [1,1, 9] . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
211 1474 [0,0, 2] . . parameters: [UNIV 5] ANY OPTIONAL
213 1476 [1,2, 129] . signatureValue: BIT STRING 1024 bits
214 . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
215 . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
220 Let's parse that output, human::
222 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL
223 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
224 0 1 2 3 4 5 6 7 8 9 10 11
228 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
234 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence
240 52-2∞ B [1,1,1054]∞ . . . . eContent: [0] EXPLICIT BER OCTET STRING 1046 bytes
245 Offset of the object, where its DER/BER encoding begins.
246 Pay attention that it does **not** include explicit tag.
248 If explicit tag exists, then this is its length (tag + encoded length).
250 Length of object's tag. For example CHOICE does not have its own tag,
253 Length of encoded length.
255 Length of encoded value.
257 Visual indentation to show the depth of object in the hierarchy.
259 Object's name inside SEQUENCE/CHOICE.
261 If either IMPLICIT or EXPLICIT tag is set, then it will be shown
262 here. "IMPLICIT" is omitted.
264 Object's class name, if set. Omitted if it is just an ordinary simple
265 value (like with ``algorithm`` in example above).
269 Object's value, if set. Can consist of multiple words (like OCTET/BIT
270 STRINGs above). We see ``v3`` value in Version, because it is named.
271 ``rdnSequence`` is the choice of CHOICE type.
273 Possible other flags like OPTIONAL and DEFAULT, if value equals to the
274 default one, specified in the schema.
276 Shows does object contains any kind of BER encoded data (possibly
277 Sequence holding BER-encoded underlying value).
279 Only applicable to BER encoded data. Indefinite length encoding mark.
281 Only applicable to BER encoded data. If object has BER-specific
282 encoding, then ``BER`` will be shown. It does not depend on indefinite
283 length encoding. ``EOC``, ``BOOLEAN``, ``BIT STRING``, ``OCTET STRING``
284 (and its derivatives), ``SET``, ``SET OF`` could be BERed.
286 As command line utility
287 -----------------------
289 You can decode DER/BER files using command line abilities and get the
290 same picture as above by executing::
292 % python -m pyderasn --schema tests.test_crts:Certificate path/to/file
294 If there is no schema for you file, then you can try parsing it without,
295 but of course IMPLICIT tags will often make it impossible. But result is
296 good enough for the certificate above::
298 % python -m pyderasn path/to/file
299 0 [1,3,1604] . >: SEQUENCE OF
300 4 [1,3,1453] . . >: SEQUENCE OF
301 8 [0,0, 5] . . . . >: [0] ANY
302 . . . . . A0:03:02:01:02
303 13 [1,1, 3] . . . . >: INTEGER 61595
304 18 [1,1, 13] . . . . >: SEQUENCE OF
305 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
306 31 [1,1, 0] . . . . . . >: NULL
307 33 [1,3, 274] . . . . >: SEQUENCE OF
308 37 [1,1, 11] . . . . . . >: SET OF
309 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
310 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
311 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
313 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
314 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
315 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
316 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
317 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
318 . . . . . . . . . 61:2E:63:6F:6D:2F
319 1461 [1,1, 13] . . >: SEQUENCE OF
320 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
321 1474 [1,1, 0] . . . . >: NULL
322 1476 [1,2, 129] . . >: BIT STRING 1024 bits
323 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
324 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
330 If you have got dictionaries with ObjectIdentifiers, like example one
331 from ``tests/test_crts.py``::
334 "1.2.840.113549.1.1.1": "id-rsaEncryption",
335 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
337 "2.5.4.10": "id-at-organizationName",
338 "2.5.4.11": "id-at-organizationalUnitName",
341 then you can pass it to pretty printer to see human readable OIDs::
343 % python -m pyderasn --oids tests.test_crts:some_oids path/to/file
345 37 [1,1, 11] . . . . . . >: SET OF
346 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
347 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
348 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
349 50 [1,1, 18] . . . . . . >: SET OF
350 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
351 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
352 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
353 70 [1,1, 18] . . . . . . >: SET OF
354 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
355 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
356 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
362 Each decoded element has so-called decode path: sequence of structure
363 names it is passing during the decode process. Each element has its own
364 unique path inside the whole ASN.1 tree. You can print it out with
365 ``--print-decode-path`` option::
367 % python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
368 0 [1,3,1604] Certificate SEQUENCE []
369 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
370 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
371 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
372 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
373 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
374 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
376 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
377 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
378 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
379 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
380 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
381 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
382 . . . . . . . 13:02:45:53
383 46 [1,1, 2] . . . . . . . DEFINED BY 2.5.4.6: CountryName PrintableString ES [tbsCertificate:issuer:rdnSequence:0:0:value:DEFINED BY 2.5.4.6]
386 Now you can print only the specified tree, for example signature algorithm::
388 % python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
389 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
390 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
391 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
397 If you have bad DER/BER, then errors will show you where error occurred::
399 % python -m pyderasn --schema tests.test_crts:Certificate path/to/bad/file
400 Traceback (most recent call last):
402 pyderasn.DecodeError: UTCTime (tbsCertificate:validity:notAfter:utcTime) (at 328) invalid UTCTime format
406 % python -m pyderasn path/to/bad/file
408 pyderasn.DecodeError: UTCTime (0:SequenceOf:4:SequenceOf:1:UTCTime) (at 328) invalid UTCTime format
410 You can see, so called, decode path inside the structures:
411 ``tbsCertificate`` -> ``validity`` -> ``notAfter`` -> ``utcTime`` and
412 that object at byte 328 is invalid.
414 X.509 certificate creation
415 --------------------------
417 Let's create some simple self-signed X.509 certificate from the ground::
419 tbs = TBSCertificate()
420 tbs["serialNumber"] = CertificateSerialNumber(10143011886257155224)
422 sign_algo_id = AlgorithmIdentifier((
423 ("algorithm", ObjectIdentifier("1.2.840.113549.1.1.5")),
424 ("parameters", Any(Null())),
426 tbs["signature"] = sign_algo_id
428 rdnSeq = RDNSequence()
429 for oid, klass, text in (
430 ("2.5.4.6", PrintableString, "XX"),
431 ("2.5.4.8", PrintableString, "Some-State"),
432 ("2.5.4.7", PrintableString, "City"),
433 ("2.5.4.10", PrintableString, "Internet Widgits Pty Ltd"),
434 ("2.5.4.3", PrintableString, "false.example.com"),
435 ("1.2.840.113549.1.9.1", IA5String, "false@example.com"),
438 RelativeDistinguishedName((
439 AttributeTypeAndValue((
440 ("type", AttributeType(oid)),
441 ("value", AttributeValue(klass(text))),
445 issuer = Name(("rdnSequence", rdnSeq))
446 tbs["issuer"] = issuer
447 tbs["subject"] = issuer
449 validity = Validity((
451 ("utcTime", UTCTime(datetime(2009, 10, 8, 0, 25, 53))),
454 ("utcTime", UTCTime(datetime(2010, 10, 8, 0, 25, 53))),
457 tbs["validity"] = validity
459 spki = SubjectPublicKeyInfo()
460 spki_algo_id = sign_algo_id.copy()
461 spki_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.1")
462 spki["algorithm"] = spki_algo_id
463 spki["subjectPublicKey"] = BitString(hexdec("".join((
464 "3048024100cdb7639c3278f006aa277f6eaf42902b592d8cbcbe38a1c92ba4695",
465 "a331b1deadeadd8e9a5c27e8c4c2fd0a8889657722a4f2af7589cf2c77045dc8f",
466 "deec357d0203010001",
468 tbs["subjectPublicKeyInfo"] = spki
471 crt["tbsCertificate"] = tbs
472 crt["signatureAlgorithm"] = sign_algo_id
473 crt["signatureValue"] = BitString(hexdec("".join((
474 "a67b06ec5ece92772ca413cba3ca12568fdc6c7b4511cd40a7f659980402df2b",
475 "998bb9a4a8cbeb34c0f0a78cf8d91ede14a5ed76bf116fe360aafa8821490435",
479 And we will get the same certificate used in Go's library tests.
484 Here is only very simple example how you can define Any/OctetString
485 fields automatic decoding::
487 class AttributeTypeAndValue(Sequence):
489 ((("type",), AttributeType(defines=((("value",), {
490 id_at_countryName: PrintableString(),
491 id_at_stateOrProvinceName: PrintableString(),
492 id_at_localityName: PrintableString(),
493 id_at_organizationName: PrintableString(),
494 id_at_commonName: PrintableString(),
496 ("value", AttributeValue()),
499 And when you will try to decode X.509 certificate with it, your pretty
502 34 [0,0, 149] . . issuer: Name CHOICE rdnSequence
503 34 [1,2, 146] . . . rdnSequence: RDNSequence SEQUENCE OF
504 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
505 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
506 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
507 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
508 . . . . . . . 13:02:58:58
509 46 [1,1, 2] . . . . . . . DEFINED BY (2.5.4.6): PrintableString PrintableString XX
510 50 [1,1, 19] . . . . 1: RelativeDistinguishedName SET OF
511 52 [1,1, 17] . . . . . 0: AttributeTypeAndValue SEQUENCE
512 54 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
513 59 [0,0, 12] . . . . . . value: [UNIV 19] AttributeValue ANY
514 . . . . . . . 13:0A:53:6F:6D:65:2D:53:74:61:74:65
515 59 [1,1, 10] . . . . . . . DEFINED BY (2.5.4.8): PrintableString PrintableString Some-State
516 71 [1,1, 13] . . . . 2: RelativeDistinguishedName SET OF
517 73 [1,1, 11] . . . . . 0: AttributeTypeAndValue SEQUENCE
518 75 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
519 80 [0,0, 6] . . . . . . value: [UNIV 19] AttributeValue ANY
520 . . . . . . . 13:04:43:69:74:79
521 80 [1,1, 4] . . . . . . . DEFINED BY (2.5.4.7): PrintableString PrintableString City
522 86 [1,1, 33] . . . . 3: RelativeDistinguishedName SET OF
523 88 [1,1, 31] . . . . . 0: AttributeTypeAndValue SEQUENCE
524 90 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-organizationName (2.5.4.10)
525 95 [0,0, 26] . . . . . . value: [UNIV 19] AttributeValue ANY
526 . . . . . . . 13:18:49:6E:74:65:72:6E:65:74:20:57:69:64:67:69
527 . . . . . . . 74:73:20:50:74:79:20:4C:74:64
528 95 [1,1, 24] . . . . . . . DEFINED BY (2.5.4.10): PrintableString PrintableString Internet Widgits Pty Ltd
530 :ref:`Read more <definedby>` about that feature.