]> Cypherpunks.ru repositories - pygost.git/blob - pygost/asn1schemas/cert-selfsigned-example.py
1f505aae09e9e327fa9a2ea407690715634b6224
[pygost.git] / pygost / asn1schemas / cert-selfsigned-example.py
1 #!/usr/bin/env python3
2 """Create example self-signed X.509 certificate
3 """
4
5 from argparse import ArgumentParser
6 from base64 import standard_b64decode
7 from base64 import standard_b64encode
8 from datetime import datetime
9 from datetime import timedelta
10 from os import urandom
11 from textwrap import fill
12
13 from pyderasn import Any
14 from pyderasn import BitString
15 from pyderasn import Boolean
16 from pyderasn import IA5String
17 from pyderasn import Integer
18 from pyderasn import OctetString
19 from pyderasn import PrintableString
20 from pyderasn import UTCTime
21
22 from pygost.asn1schemas.oids import id_at_commonName
23 from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier
24 from pygost.asn1schemas.oids import id_ce_basicConstraints
25 from pygost.asn1schemas.oids import id_ce_subjectAltName
26 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
27 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
28 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
29 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB
30 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC
31 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD
32 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
33 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
34 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB
35 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC
36 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
37 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
38 from pygost.asn1schemas.prvkey import PrivateKey
39 from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
40 from pygost.asn1schemas.prvkey import PrivateKeyInfo
41 from pygost.asn1schemas.x509 import AlgorithmIdentifier
42 from pygost.asn1schemas.x509 import AttributeType
43 from pygost.asn1schemas.x509 import AttributeTypeAndValue
44 from pygost.asn1schemas.x509 import AttributeValue
45 from pygost.asn1schemas.x509 import AuthorityKeyIdentifier
46 from pygost.asn1schemas.x509 import BasicConstraints
47 from pygost.asn1schemas.x509 import Certificate
48 from pygost.asn1schemas.x509 import CertificateSerialNumber
49 from pygost.asn1schemas.x509 import Extension
50 from pygost.asn1schemas.x509 import Extensions
51 from pygost.asn1schemas.x509 import GeneralName
52 from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
53 from pygost.asn1schemas.x509 import KeyIdentifier
54 from pygost.asn1schemas.x509 import Name
55 from pygost.asn1schemas.x509 import RDNSequence
56 from pygost.asn1schemas.x509 import RelativeDistinguishedName
57 from pygost.asn1schemas.x509 import SubjectAltName
58 from pygost.asn1schemas.x509 import SubjectKeyIdentifier
59 from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
60 from pygost.asn1schemas.x509 import TBSCertificate
61 from pygost.asn1schemas.x509 import Time
62 from pygost.asn1schemas.x509 import Validity
63 from pygost.asn1schemas.x509 import Version
64 from pygost.gost3410 import CURVES
65 from pygost.gost3410 import prv_unmarshal
66 from pygost.gost3410 import pub_marshal
67 from pygost.gost3410 import public_key
68 from pygost.gost3410 import sign
69 from pygost.gost34112012256 import GOST34112012256
70 from pygost.gost34112012512 import GOST34112012512
71
72 parser = ArgumentParser(description="Self-signed X.509 certificate creator")
73 parser.add_argument(
74     "--ca",
75     action="store_true",
76     help="Enable BasicConstraints.cA",
77 )
78 parser.add_argument(
79     "--cn",
80     required=True,
81     help="Subject's CommonName",
82 )
83 parser.add_argument(
84     "--ai",
85     required=True,
86     help="Signing algorithm: {256[ABCD],512[ABC]}",
87 )
88 parser.add_argument(
89     "--issue-with",
90     help="Path to PEM with CA to issue the child",
91 )
92 args = parser.parse_args()
93 AIs = {
94     "256A": {
95         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
96         "key_algorithm": id_tc26_gost3410_2012_256,
97         "prv_len": 32,
98         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
99         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
100         "hasher": GOST34112012256,
101     },
102     "256B": {
103         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
104         "key_algorithm": id_tc26_gost3410_2012_256,
105         "prv_len": 32,
106         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
107         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
108         "hasher": GOST34112012256,
109     },
110     "256C": {
111         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
112         "key_algorithm": id_tc26_gost3410_2012_256,
113         "prv_len": 32,
114         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
115         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
116         "hasher": GOST34112012256,
117     },
118     "256D": {
119         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
120         "key_algorithm": id_tc26_gost3410_2012_256,
121         "prv_len": 32,
122         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
123         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
124         "hasher": GOST34112012256,
125     },
126     "512A": {
127         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
128         "key_algorithm": id_tc26_gost3410_2012_512,
129         "prv_len": 64,
130         "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
131         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
132         "hasher": GOST34112012512,
133     },
134     "512B": {
135         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
136         "key_algorithm": id_tc26_gost3410_2012_512,
137         "prv_len": 64,
138         "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
139         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
140         "hasher": GOST34112012512,
141     },
142     "512C": {
143         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
144         "key_algorithm": id_tc26_gost3410_2012_512,
145         "prv_len": 64,
146         "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
147         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
148         "hasher": GOST34112012512,
149     },
150 }
151 ai = AIs[args.ai]
152
153 ca_prv = None
154 ca_cert = None
155 ca_subj = None
156 ca_ai = None
157 if args.issue_with is not None:
158     with open(args.issue_with, "rb") as fd:
159         lines = fd.read().decode("ascii").split("-----")
160     idx = lines.index("BEGIN PRIVATE KEY")
161     if idx == -1:
162         raise ValueError("PEM has no PRIVATE KEY")
163     prv_raw = standard_b64decode(lines[idx + 1])
164     idx = lines.index("BEGIN CERTIFICATE")
165     if idx == -1:
166         raise ValueError("PEM has no CERTIFICATE")
167     cert_raw = standard_b64decode(lines[idx + 1])
168     pki = PrivateKeyInfo().decod(prv_raw)
169     ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
170     ca_cert = Certificate().decod(cert_raw)
171     tbs = ca_cert["tbsCertificate"]
172     ca_subj = tbs["subject"]
173     curve_oid = GostR34102012PublicKeyParameters().decod(bytes(
174         tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"]
175     ))["publicKeyParamSet"]
176     ca_ai = next(iter([
177         params for params in AIs.values()
178         if params["publicKeyParamSet"] == curve_oid
179     ]))
180
181
182 def pem(obj):
183     return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
184
185
186 key_params = GostR34102012PublicKeyParameters((
187     ("publicKeyParamSet", ai["publicKeyParamSet"]),
188 ))
189
190 prv_raw = urandom(ai["prv_len"])
191 print("-----BEGIN PRIVATE KEY-----")
192 print(pem(PrivateKeyInfo((
193     ("version", Integer(0)),
194     ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
195         ("algorithm", ai["key_algorithm"]),
196         ("parameters", Any(key_params)),
197     ))),
198     ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
199 ))))
200 print("-----END PRIVATE KEY-----")
201
202 prv = prv_unmarshal(prv_raw)
203 curve = ai["curve"]
204 pub_raw = pub_marshal(public_key(curve, prv))
205 subj = Name(("rdnSequence", RDNSequence([
206     RelativeDistinguishedName((
207         AttributeTypeAndValue((
208             ("type", AttributeType(id_at_commonName)),
209             ("value", AttributeValue(PrintableString(args.cn))),
210         )),
211     ))
212 ])))
213 not_before = datetime.utcnow()
214 not_after = not_before + timedelta(days=365)
215 ai_sign = AlgorithmIdentifier((
216     ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]),
217 ))
218 exts = [
219     Extension((
220         ("extnID", id_ce_subjectKeyIdentifier),
221
222         ("extnValue", OctetString(
223             SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
224         )),
225     )),
226     Extension((
227         ("extnID", id_ce_subjectAltName),
228         ("extnValue", OctetString(
229             SubjectAltName((
230                 GeneralName(("dNSName", IA5String(args.cn))),
231             )).encode()
232         )),
233     )),
234 ]
235 if args.ca:
236     exts.append(Extension((
237         ("extnID", id_ce_basicConstraints),
238         ("extnValue", OctetString(BasicConstraints((("cA", Boolean(True)),)).encode())),
239     )))
240 if ca_ai is not None:
241     caKeyId = [
242         bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"])))
243         for ext in ca_cert["tbsCertificate"]["extensions"]
244         if ext["extnID"] == id_ce_subjectKeyIdentifier
245     ][0]
246     exts.append(Extension((
247         ("extnID", id_ce_authorityKeyIdentifier),
248         ("extnValue", OctetString(AuthorityKeyIdentifier((
249             ("keyIdentifier", KeyIdentifier(caKeyId)),
250         )).encode())),
251     )))
252
253 tbs = TBSCertificate((
254     ("version", Version("v3")),
255     ("serialNumber", CertificateSerialNumber(12345)),
256     ("signature", ai_sign),
257     ("issuer", subj if ca_ai is None else ca_subj),
258     ("validity", Validity((
259         ("notBefore", Time(("utcTime", UTCTime(not_before)))),
260         ("notAfter", Time(("utcTime", UTCTime(not_after)))),
261     ))),
262     ("subject", subj),
263     ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
264         ("algorithm", AlgorithmIdentifier((
265             ("algorithm", ai["key_algorithm"]),
266             ("parameters", Any(key_params)),
267         ))),
268         ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
269     ))),
270     ("extensions", Extensions(exts)),
271 ))
272 cert = Certificate((
273     ("tbsCertificate", tbs),
274     ("signatureAlgorithm", ai_sign),
275     ("signatureValue", BitString(
276         sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1])
277         if ca_ai is None else
278         sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1])
279     )),
280 ))
281 print("-----BEGIN CERTIFICATE-----")
282 print(pem(cert))
283 print("-----END CERTIFICATE-----")