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...
187 :ref:`Look here <pprint_example>` for better pretty printing.
191 As command line utility
192 -----------------------
194 You can decode DER/BER files using command line abilities and get the
195 same picture as above by executing::
197 % python -m pyderasn --schema tests.test_crts:Certificate path/to/file
199 If there is no schema for you file, then you can try parsing it without,
200 but of course IMPLICIT tags will often make it impossible. But result is
201 good enough for the certificate above::
203 % python -m pyderasn path/to/file
204 0 [1,3,1604] . >: SEQUENCE OF
205 4 [1,3,1453] . . >: SEQUENCE OF
206 8 [0,0, 5] . . . . >: [0] ANY
207 . . . . . A0:03:02:01:02
208 13 [1,1, 3] . . . . >: INTEGER 61595
209 18 [1,1, 13] . . . . >: SEQUENCE OF
210 20 [1,1, 9] . . . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
211 31 [1,1, 0] . . . . . . >: NULL
212 33 [1,3, 274] . . . . >: SEQUENCE OF
213 37 [1,1, 11] . . . . . . >: SET OF
214 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
215 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER 2.5.4.6
216 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
218 1409 [1,1, 50] . . . . . . >: SEQUENCE OF
219 1411 [1,1, 8] . . . . . . . . >: OBJECT IDENTIFIER 1.3.6.1.5.5.7.1.1
220 1421 [1,1, 38] . . . . . . . . >: OCTET STRING 38 bytes
221 . . . . . . . . . 30:24:30:22:06:08:2B:06:01:05:05:07:30:01:86:16
222 . . . . . . . . . 68:74:74:70:3A:2F:2F:6F:63:73:70:2E:69:70:73:63
223 . . . . . . . . . 61:2E:63:6F:6D:2F
224 1461 [1,1, 13] . . >: SEQUENCE OF
225 1463 [1,1, 9] . . . . >: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
226 1474 [1,1, 0] . . . . >: NULL
227 1476 [1,2, 129] . . >: BIT STRING 1024 bits
228 . . . 68:EE:79:97:97:DD:3B:EF:16:6A:06:F2:14:9A:6E:CD
229 . . . 9E:12:F7:AA:83:10:BD:D1:7C:98:FA:C7:AE:D4:0E:2C
235 If you have got dictionaries with ObjectIdentifiers, like example one
236 from ``tests/test_crts.py``::
239 "1.2.840.113549.1.1.1": "id-rsaEncryption",
240 "1.2.840.113549.1.1.5": "id-sha1WithRSAEncryption",
242 "2.5.4.10": "id-at-organizationName",
243 "2.5.4.11": "id-at-organizationalUnitName",
246 then you can pass it to pretty printer to see human readable OIDs::
248 % python -m pyderasn --oids tests.test_crts:some_oids path/to/file
250 37 [1,1, 11] . . . . . . >: SET OF
251 39 [1,1, 9] . . . . . . . . >: SEQUENCE OF
252 41 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
253 46 [1,1, 2] . . . . . . . . . . >: PrintableString PrintableString ES
254 50 [1,1, 18] . . . . . . >: SET OF
255 52 [1,1, 16] . . . . . . . . >: SEQUENCE OF
256 54 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
257 59 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
258 70 [1,1, 18] . . . . . . >: SET OF
259 72 [1,1, 16] . . . . . . . . >: SEQUENCE OF
260 74 [1,1, 3] . . . . . . . . . . >: OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
261 79 [1,1, 9] . . . . . . . . . . >: PrintableString PrintableString Barcelona
267 Each decoded element has so-called decode path: sequence of structure
268 names it is passing during the decode process. Each element has its own
269 unique path inside the whole ASN.1 tree. You can print it out with
270 ``--print-decode-path`` option::
272 % python -m pyderasn --schema path.to:Certificate --print-decode-path path/to/file
273 0 [1,3,1604] Certificate SEQUENCE []
274 4 [1,3,1453] . tbsCertificate: TBSCertificate SEQUENCE [tbsCertificate]
275 10-2 [1,1, 1] . . version: [0] EXPLICIT Version INTEGER v3 OPTIONAL [tbsCertificate:version]
276 13 [1,1, 3] . . serialNumber: CertificateSerialNumber INTEGER 61595 [tbsCertificate:serialNumber]
277 18 [1,1, 13] . . signature: AlgorithmIdentifier SEQUENCE [tbsCertificate:signature]
278 20 [1,1, 9] . . . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5 [tbsCertificate:signature:algorithm]
279 31 [0,0, 2] . . . parameters: [UNIV 5] ANY OPTIONAL [tbsCertificate:signature:parameters]
281 33 [0,0, 278] . . issuer: Name CHOICE rdnSequence [tbsCertificate:issuer]
282 33 [1,3, 274] . . . rdnSequence: RDNSequence SEQUENCE OF [tbsCertificate:issuer:rdnSequence]
283 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF [tbsCertificate:issuer:rdnSequence:0]
284 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE [tbsCertificate:issuer:rdnSequence:0:0]
285 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER 2.5.4.6 [tbsCertificate:issuer:rdnSequence:0:0:type]
286 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY [tbsCertificate:issuer:rdnSequence:0:0:value]
287 . . . . . . . 13:02:45:53
288 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]
291 Now you can print only the specified tree, for example signature algorithm::
293 % python -m pyderasn --schema path.to:Certificate --decode-path-only tbsCertificate:signature path/to/file
294 18 [1,1, 13] AlgorithmIdentifier SEQUENCE
295 20 [1,1, 9] . algorithm: OBJECT IDENTIFIER 1.2.840.113549.1.1.5
296 31 [0,0, 2] . parameters: [UNIV 5] ANY OPTIONAL
302 If you have bad DER/BER, then errors will show you where error occurred::
304 % python -m pyderasn --schema tests.test_crts:Certificate path/to/bad/file
305 Traceback (most recent call last):
307 pyderasn.DecodeError: UTCTime (tbsCertificate:validity:notAfter:utcTime) (at 328) invalid UTCTime format
311 % python -m pyderasn path/to/bad/file
313 pyderasn.DecodeError: UTCTime (0:SequenceOf:4:SequenceOf:1:UTCTime) (at 328) invalid UTCTime format
315 You can see, so called, decode path inside the structures:
316 ``tbsCertificate`` -> ``validity`` -> ``notAfter`` -> ``utcTime`` and
317 that object at byte 328 is invalid.
319 X.509 certificate creation
320 --------------------------
322 Let's create some simple self-signed X.509 certificate from the ground::
324 tbs = TBSCertificate()
325 tbs["serialNumber"] = CertificateSerialNumber(10143011886257155224)
327 sign_algo_id = AlgorithmIdentifier((
328 ("algorithm", ObjectIdentifier("1.2.840.113549.1.1.5")),
329 ("parameters", Any(Null())),
331 tbs["signature"] = sign_algo_id
333 rdnSeq = RDNSequence()
334 for oid, klass, text in (
335 ("2.5.4.6", PrintableString, "XX"),
336 ("2.5.4.8", PrintableString, "Some-State"),
337 ("2.5.4.7", PrintableString, "City"),
338 ("2.5.4.10", PrintableString, "Internet Widgits Pty Ltd"),
339 ("2.5.4.3", PrintableString, "false.example.com"),
340 ("1.2.840.113549.1.9.1", IA5String, "false@example.com"),
343 RelativeDistinguishedName((
344 AttributeTypeAndValue((
345 ("type", AttributeType(oid)),
346 ("value", AttributeValue(klass(text))),
350 issuer = Name(("rdnSequence", rdnSeq))
351 tbs["issuer"] = issuer
352 tbs["subject"] = issuer
354 validity = Validity((
356 ("utcTime", UTCTime(datetime(2009, 10, 8, 0, 25, 53))),
359 ("utcTime", UTCTime(datetime(2010, 10, 8, 0, 25, 53))),
362 tbs["validity"] = validity
364 spki = SubjectPublicKeyInfo()
365 spki_algo_id = sign_algo_id.copy()
366 spki_algo_id["algorithm"] = ObjectIdentifier("1.2.840.113549.1.1.1")
367 spki["algorithm"] = spki_algo_id
368 spki["subjectPublicKey"] = BitString(hexdec("".join((
369 "3048024100cdb7639c3278f006aa277f6eaf42902b592d8cbcbe38a1c92ba4695",
370 "a331b1deadeadd8e9a5c27e8c4c2fd0a8889657722a4f2af7589cf2c77045dc8f",
371 "deec357d0203010001",
373 tbs["subjectPublicKeyInfo"] = spki
376 crt["tbsCertificate"] = tbs
377 crt["signatureAlgorithm"] = sign_algo_id
378 crt["signatureValue"] = BitString(hexdec("".join((
379 "a67b06ec5ece92772ca413cba3ca12568fdc6c7b4511cd40a7f659980402df2b",
380 "998bb9a4a8cbeb34c0f0a78cf8d91ede14a5ed76bf116fe360aafa8821490435",
384 And we will get the same certificate used in Go's library tests.
389 Here is only very simple example how you can define Any/OctetString
390 fields automatic decoding::
392 class AttributeTypeAndValue(Sequence):
394 ((("type",), AttributeType(defines=((("value",), {
395 id_at_countryName: PrintableString(),
396 id_at_stateOrProvinceName: PrintableString(),
397 id_at_localityName: PrintableString(),
398 id_at_organizationName: PrintableString(),
399 id_at_commonName: PrintableString(),
401 ("value", AttributeValue()),
404 And when you will try to decode X.509 certificate with it, your pretty
407 34 [0,0, 149] . . issuer: Name CHOICE rdnSequence
408 34 [1,2, 146] . . . rdnSequence: RDNSequence SEQUENCE OF
409 37 [1,1, 11] . . . . 0: RelativeDistinguishedName SET OF
410 39 [1,1, 9] . . . . . 0: AttributeTypeAndValue SEQUENCE
411 41 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-countryName (2.5.4.6)
412 46 [0,0, 4] . . . . . . value: [UNIV 19] AttributeValue ANY
413 . . . . . . . 13:02:58:58
414 46 [1,1, 2] . . . . . . . DEFINED BY (2.5.4.6): PrintableString PrintableString XX
415 50 [1,1, 19] . . . . 1: RelativeDistinguishedName SET OF
416 52 [1,1, 17] . . . . . 0: AttributeTypeAndValue SEQUENCE
417 54 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-stateOrProvinceName (2.5.4.8)
418 59 [0,0, 12] . . . . . . value: [UNIV 19] AttributeValue ANY
419 . . . . . . . 13:0A:53:6F:6D:65:2D:53:74:61:74:65
420 59 [1,1, 10] . . . . . . . DEFINED BY (2.5.4.8): PrintableString PrintableString Some-State
421 71 [1,1, 13] . . . . 2: RelativeDistinguishedName SET OF
422 73 [1,1, 11] . . . . . 0: AttributeTypeAndValue SEQUENCE
423 75 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-localityName (2.5.4.7)
424 80 [0,0, 6] . . . . . . value: [UNIV 19] AttributeValue ANY
425 . . . . . . . 13:04:43:69:74:79
426 80 [1,1, 4] . . . . . . . DEFINED BY (2.5.4.7): PrintableString PrintableString City
427 86 [1,1, 33] . . . . 3: RelativeDistinguishedName SET OF
428 88 [1,1, 31] . . . . . 0: AttributeTypeAndValue SEQUENCE
429 90 [1,1, 3] . . . . . . type: AttributeType OBJECT IDENTIFIER id-at-organizationName (2.5.4.10)
430 95 [0,0, 26] . . . . . . value: [UNIV 19] AttributeValue ANY
431 . . . . . . . 13:18:49:6E:74:65:72:6E:65:74:20:57:69:64:67:69
432 . . . . . . . 74:73:20:50:74:79:20:4C:74:64
433 95 [1,1, 24] . . . . . . . DEFINED BY (2.5.4.10): PrintableString PrintableString Internet Widgits Pty Ltd
435 :ref:`Read more <definedby>` about that feature.