3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 This utility takes a series of https://crt.sh/ identifiers and writes to
9 stdout all of those certs' distinguished name or SPKI fields in hex, with an
10 array of all those. You'll need to post-process this list to handle any
21 from cryptography
import x509
22 from cryptography
.hazmat
.backends
import default_backend
23 from cryptography
.hazmat
.primitives
import hashes
24 from cryptography
.x509
.oid
import NameOID
25 from pyasn1
.codec
.der
import decoder
, encoder
26 from pyasn1_modules
import pem
, rfc5280
28 assert sys
.version_info
>= (3, 2), "Requires Python 3.2 or later"
31 def hex_string_for_struct(bytes
):
32 return ["0x{:02X}".format(x
) for x
in bytes
]
35 def hex_string_human_readable(bytes
):
36 return ["{:02X}".format(x
) for x
in bytes
]
39 def nameOIDtoString(oid
):
40 if oid
== NameOID
.COUNTRY_NAME
:
42 if oid
== NameOID
.COMMON_NAME
:
44 if oid
== NameOID
.LOCALITY_NAME
:
46 if oid
== NameOID
.ORGANIZATION_NAME
:
48 if oid
== NameOID
.ORGANIZATIONAL_UNIT_NAME
:
50 raise Exception("Unknown OID: {}".format(oid
))
53 def print_block(pemData
, identifierType
="DN", crtshId
=None):
54 substrate
= pem
.readPemFromFile(io
.StringIO(pemData
.decode("utf-8")))
55 cert
, _
= decoder
.decode(substrate
, asn1Spec
=rfc5280
.Certificate())
58 if identifierType
== "DN":
59 der_subject
= encoder
.encode(cert
["tbsCertificate"]["subject"])
60 octets
= hex_string_for_struct(der_subject
)
61 elif identifierType
== "SPKI":
62 der_spki
= encoder
.encode(cert
["tbsCertificate"]["subjectPublicKeyInfo"])
63 octets
= hex_string_for_struct(der_spki
)
65 raise Exception("Unknown identifier type: " + identifierType
)
67 cert
= x509
.load_pem_x509_certificate(pemData
, default_backend())
68 common_name
= cert
.subject
.get_attributes_for_oid(NameOID
.COMMON_NAME
)[0]
69 block_name
= "CA{}{}".format(
70 re
.sub(r
"[-:=_. ]", "", common_name
.value
), identifierType
73 fingerprint
= hex_string_human_readable(cert
.fingerprint(hashes
.SHA256()))
76 "/{id}={value}".format(id=nameOIDtoString(part
.oid
), value
=part
.value
)
77 for part
in cert
.subject
79 distinguished_name
= "".join(dn_parts
)
81 print("// {dn}".format(dn
=distinguished_name
))
82 print("// SHA256 Fingerprint: " + ":".join(fingerprint
[:16]))
83 print("// " + ":".join(fingerprint
[16:]))
85 print("// https://crt.sh/?id={crtsh} (crt.sh ID={crtsh})".format(crtsh
=crtshId
))
86 print("static const uint8_t {}[{}] = ".format(block_name
, len(octets
)) + "{")
88 while len(octets
) > 0:
89 print(" " + ", ".join(octets
[:13]) + ",")
98 if __name__
== "__main__":
99 parser
= argparse
.ArgumentParser()
103 help="Create a list of subject public key info fields",
108 help="Create a list of subject distinguished name fields",
110 parser
.add_argument("-listname", help="Name of the final DataAndLength block")
112 "certId", nargs
="+", help="A list of PEM files on disk or crt.sh IDs"
114 args
= parser
.parse_args()
116 if not args
.dn
and not args
.spki
:
118 raise Exception("You must select either DN or SPKI matching")
123 "// Script from security/manager/tools/crtshToIdentifyingStruct/"
124 + "crtshToIdentifyingStruct.py"
126 print("// Invocation: {}".format(" ".join(sys
.argv
)))
129 identifierType
= None
131 identifierType
= "DN"
133 identifierType
= "SPKI"
135 for certId
in args
.certId
:
136 # Try a local file first, then crt.sh
138 with
open(certId
, "rb") as pemFile
:
140 print_block(pemFile
.read(), identifierType
=identifierType
)
143 r
= requests
.get("https://crt.sh/?d={}".format(certId
))
146 print_block(r
.content
, crtshId
=certId
, identifierType
=identifierType
)
149 print("static const DataAndLength " + args
.listname
+ "[]= {")
150 for structName
in blocks
:
151 if len(structName
) < 33:
152 print(" { " + "{name}, sizeof({name}) ".format(name
=structName
) + "},")
154 print(" { " + "{},".format(structName
))
155 print(" sizeof({})".format(structName
) + " },")