2 """Create example self-signed X.509 certificate
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 sys import exit as sys_exit
12 from sys import stdout
13 from textwrap import fill
15 from pyderasn import Any
16 from pyderasn import BitString
17 from pyderasn import Boolean
18 from pyderasn import IA5String
19 from pyderasn import Integer
20 from pyderasn import OctetString
21 from pyderasn import PrintableString
22 from pyderasn import UTCTime
24 from pygost.asn1schemas.oids import id_at_commonName
25 from pygost.asn1schemas.oids import id_at_countryName
26 from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier
27 from pygost.asn1schemas.oids import id_ce_basicConstraints
28 from pygost.asn1schemas.oids import id_ce_keyUsage
29 from pygost.asn1schemas.oids import id_ce_subjectAltName
30 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
31 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
32 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
33 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB
34 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC
35 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD
36 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
37 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
38 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB
39 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC
40 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
41 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
42 from pygost.asn1schemas.prvkey import PrivateKey
43 from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
44 from pygost.asn1schemas.prvkey import PrivateKeyInfo
45 from pygost.asn1schemas.x509 import AlgorithmIdentifier
46 from pygost.asn1schemas.x509 import AttributeType
47 from pygost.asn1schemas.x509 import AttributeTypeAndValue
48 from pygost.asn1schemas.x509 import AttributeValue
49 from pygost.asn1schemas.x509 import AuthorityKeyIdentifier
50 from pygost.asn1schemas.x509 import BasicConstraints
51 from pygost.asn1schemas.x509 import Certificate
52 from pygost.asn1schemas.x509 import CertificateSerialNumber
53 from pygost.asn1schemas.x509 import Extension
54 from pygost.asn1schemas.x509 import Extensions
55 from pygost.asn1schemas.x509 import GeneralName
56 from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
57 from pygost.asn1schemas.x509 import KeyIdentifier
58 from pygost.asn1schemas.x509 import KeyUsage
59 from pygost.asn1schemas.x509 import Name
60 from pygost.asn1schemas.x509 import RDNSequence
61 from pygost.asn1schemas.x509 import RelativeDistinguishedName
62 from pygost.asn1schemas.x509 import SubjectAltName
63 from pygost.asn1schemas.x509 import SubjectKeyIdentifier
64 from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
65 from pygost.asn1schemas.x509 import TBSCertificate
66 from pygost.asn1schemas.x509 import Time
67 from pygost.asn1schemas.x509 import Validity
68 from pygost.asn1schemas.x509 import Version
69 from pygost.gost3410 import CURVES
70 from pygost.gost3410 import prv_unmarshal
71 from pygost.gost3410 import pub_marshal
72 from pygost.gost3410 import public_key
73 from pygost.gost3410 import sign
74 from pygost.gost34112012256 import GOST34112012256
75 from pygost.gost34112012512 import GOST34112012512
76 from pygost.utils import bytes2long
78 parser = ArgumentParser(description="Self-signed X.509 certificate creator")
82 help="Enable BasicConstraints.cA",
87 help="Subject's CommonName",
91 help="Subject's Country",
100 help="Signing algorithm: {256[ABCD],512[ABC]}",
104 help="Path to PEM with CA to issue the child",
108 help="Path to PEM with the key to reuse",
112 help="Path to PEM with the resulting key",
117 help="Only generate the key",
121 help="Path to PEM with the resulting certificate",
123 args = parser.parse_args()
126 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
127 "key_algorithm": id_tc26_gost3410_2012_256,
129 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
130 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
131 "hasher": GOST34112012256,
134 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
135 "key_algorithm": id_tc26_gost3410_2012_256,
137 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
138 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
139 "hasher": GOST34112012256,
142 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
143 "key_algorithm": id_tc26_gost3410_2012_256,
145 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
146 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
147 "hasher": GOST34112012256,
150 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
151 "key_algorithm": id_tc26_gost3410_2012_256,
153 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
154 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
155 "hasher": GOST34112012256,
158 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
159 "key_algorithm": id_tc26_gost3410_2012_512,
161 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
162 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
163 "hasher": GOST34112012512,
166 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
167 "key_algorithm": id_tc26_gost3410_2012_512,
169 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
170 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
171 "hasher": GOST34112012512,
174 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
175 "key_algorithm": id_tc26_gost3410_2012_512,
177 "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
178 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
179 "hasher": GOST34112012512,
188 if args.issue_with is not None:
189 with open(args.issue_with, "rb") as fd:
190 lines = fd.read().decode("ascii").split("-----")
191 idx = lines.index("BEGIN PRIVATE KEY")
193 raise ValueError("PEM has no PRIVATE KEY")
194 prv_raw = standard_b64decode(lines[idx + 1])
195 idx = lines.index("BEGIN CERTIFICATE")
197 raise ValueError("PEM has no CERTIFICATE")
198 cert_raw = standard_b64decode(lines[idx + 1])
199 pki = PrivateKeyInfo().decod(prv_raw)
200 ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
201 ca_cert = Certificate().decod(cert_raw)
202 tbs = ca_cert["tbsCertificate"]
203 ca_subj = tbs["subject"]
204 curve_oid = GostR34102012PublicKeyParameters().decod(bytes(
205 tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"]
206 ))["publicKeyParamSet"]
208 params for params in AIs.values()
209 if params["publicKeyParamSet"] == curve_oid
212 key_params = GostR34102012PublicKeyParameters((
213 ("publicKeyParamSet", ai["publicKeyParamSet"]),
218 return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
221 if args.reuse_key is not None:
222 with open(args.reuse_key, "rb") as fd:
223 lines = fd.read().decode("ascii").split("-----")
224 idx = lines.index("BEGIN PRIVATE KEY")
226 raise ValueError("PEM has no PRIVATE KEY")
227 prv_raw = standard_b64decode(lines[idx + 1])
228 pki = PrivateKeyInfo().decod(prv_raw)
229 prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
231 prv_raw = urandom(ai["prv_len"])
232 out = stdout if args.out_key is None else open(args.out_key, "w")
233 print("-----BEGIN PRIVATE KEY-----", file=out)
234 print(pem(PrivateKeyInfo((
235 ("version", Integer(0)),
236 ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
237 ("algorithm", ai["key_algorithm"]),
238 ("parameters", Any(key_params)),
240 ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
242 print("-----END PRIVATE KEY-----", file=out)
245 prv = prv_unmarshal(prv_raw)
248 pub_raw = pub_marshal(public_key(curve, prv))
249 rdn = [RelativeDistinguishedName((
250 AttributeTypeAndValue((
251 ("type", AttributeType(id_at_commonName)),
252 ("value", AttributeValue(PrintableString(args.cn))),
256 rdn.append(RelativeDistinguishedName((
257 AttributeTypeAndValue((
258 ("type", AttributeType(id_at_countryName)),
259 ("value", AttributeValue(PrintableString(args.country))),
262 subj = Name(("rdnSequence", RDNSequence(rdn)))
263 not_before = datetime.utcnow()
264 not_after = not_before + timedelta(days=365 * (10 if args.ca else 1))
265 ai_sign = AlgorithmIdentifier((
266 ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]),
270 ("extnID", id_ce_subjectKeyIdentifier),
271 ("extnValue", OctetString(
272 SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
276 ("extnID", id_ce_keyUsage),
277 ("critical", Boolean(True)),
278 ("extnValue", OctetString(KeyUsage(
279 ("keyCertSign" if args.ca else "digitalSignature",),
284 exts.append(Extension((
285 ("extnID", id_ce_basicConstraints),
286 ("critical", Boolean(True)),
287 ("extnValue", OctetString(BasicConstraints((
288 ("cA", Boolean(True)),
292 exts.append(Extension((
293 ("extnID", id_ce_subjectAltName),
294 ("extnValue", OctetString(
296 GeneralName(("dNSName", IA5String(args.cn))),
300 if ca_ai is not None:
302 bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"])))
303 for ext in ca_cert["tbsCertificate"]["extensions"]
304 if ext["extnID"] == id_ce_subjectKeyIdentifier
306 exts.append(Extension((
307 ("extnID", id_ce_authorityKeyIdentifier),
308 ("extnValue", OctetString(AuthorityKeyIdentifier((
309 ("keyIdentifier", KeyIdentifier(caKeyId)),
314 bytes2long(GOST34112012256(urandom(16)).digest()[:20])
315 if args.serial is None else int(args.serial)
317 tbs = TBSCertificate((
318 ("version", Version("v3")),
319 ("serialNumber", CertificateSerialNumber(serial)),
320 ("signature", ai_sign),
321 ("issuer", subj if ca_ai is None else ca_subj),
322 ("validity", Validity((
323 ("notBefore", Time(("utcTime", UTCTime(not_before)))),
324 ("notAfter", Time(("utcTime", UTCTime(not_after)))),
327 ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
328 ("algorithm", AlgorithmIdentifier((
329 ("algorithm", ai["key_algorithm"]),
330 ("parameters", Any(key_params)),
332 ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
334 ("extensions", Extensions(exts)),
337 ("tbsCertificate", tbs),
338 ("signatureAlgorithm", ai_sign),
339 ("signatureValue", BitString(
340 sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1])
341 if ca_ai is None else
342 sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1])
345 out = stdout if args.out_cert is None else open(args.out_cert, "w")
346 print("-----BEGIN CERTIFICATE-----", file=out)
347 print(pem(cert), file=out)
348 print("-----END CERTIFICATE-----", file=out)