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 stdout
12 from textwrap import fill
14 from pyderasn import Any
15 from pyderasn import BitString
16 from pyderasn import Boolean
17 from pyderasn import IA5String
18 from pyderasn import Integer
19 from pyderasn import OctetString
20 from pyderasn import PrintableString
21 from pyderasn import UTCTime
23 from pygost.asn1schemas.oids import id_at_commonName
24 from pygost.asn1schemas.oids import id_at_countryName
25 from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier
26 from pygost.asn1schemas.oids import id_ce_basicConstraints
27 from pygost.asn1schemas.oids import id_ce_keyUsage
28 from pygost.asn1schemas.oids import id_ce_subjectAltName
29 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
30 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
31 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
32 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB
33 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC
34 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD
35 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
36 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
37 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB
38 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC
39 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
40 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
41 from pygost.asn1schemas.prvkey import PrivateKey
42 from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
43 from pygost.asn1schemas.prvkey import PrivateKeyInfo
44 from pygost.asn1schemas.x509 import AlgorithmIdentifier
45 from pygost.asn1schemas.x509 import AttributeType
46 from pygost.asn1schemas.x509 import AttributeTypeAndValue
47 from pygost.asn1schemas.x509 import AttributeValue
48 from pygost.asn1schemas.x509 import AuthorityKeyIdentifier
49 from pygost.asn1schemas.x509 import BasicConstraints
50 from pygost.asn1schemas.x509 import Certificate
51 from pygost.asn1schemas.x509 import CertificateSerialNumber
52 from pygost.asn1schemas.x509 import Extension
53 from pygost.asn1schemas.x509 import Extensions
54 from pygost.asn1schemas.x509 import GeneralName
55 from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
56 from pygost.asn1schemas.x509 import KeyIdentifier
57 from pygost.asn1schemas.x509 import KeyUsage
58 from pygost.asn1schemas.x509 import Name
59 from pygost.asn1schemas.x509 import RDNSequence
60 from pygost.asn1schemas.x509 import RelativeDistinguishedName
61 from pygost.asn1schemas.x509 import SubjectAltName
62 from pygost.asn1schemas.x509 import SubjectKeyIdentifier
63 from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
64 from pygost.asn1schemas.x509 import TBSCertificate
65 from pygost.asn1schemas.x509 import Time
66 from pygost.asn1schemas.x509 import Validity
67 from pygost.asn1schemas.x509 import Version
68 from pygost.gost3410 import CURVES
69 from pygost.gost3410 import prv_unmarshal
70 from pygost.gost3410 import pub_marshal
71 from pygost.gost3410 import public_key
72 from pygost.gost3410 import sign
73 from pygost.gost34112012256 import GOST34112012256
74 from pygost.gost34112012512 import GOST34112012512
75 from pygost.utils import bytes2long
77 parser = ArgumentParser(description="Self-signed X.509 certificate creator")
81 help="Enable BasicConstraints.cA",
86 help="Subject's CommonName",
90 help="Subject's Country",
99 help="Signing algorithm: {256[ABCD],512[ABC]}",
103 help="Path to PEM with CA to issue the child",
107 help="Path to PEM with the key to reuse",
111 help="Path to PEM with the resulting key",
116 help="Only generate the key",
120 help="Path to PEM with the resulting certificate",
122 args = parser.parse_args()
125 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
126 "key_algorithm": id_tc26_gost3410_2012_256,
128 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
129 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
130 "hasher": GOST34112012256,
133 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
134 "key_algorithm": id_tc26_gost3410_2012_256,
136 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
137 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
138 "hasher": GOST34112012256,
141 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
142 "key_algorithm": id_tc26_gost3410_2012_256,
144 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
145 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
146 "hasher": GOST34112012256,
149 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
150 "key_algorithm": id_tc26_gost3410_2012_256,
152 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
153 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
154 "hasher": GOST34112012256,
157 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
158 "key_algorithm": id_tc26_gost3410_2012_512,
160 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
161 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
162 "hasher": GOST34112012512,
165 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
166 "key_algorithm": id_tc26_gost3410_2012_512,
168 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
169 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
170 "hasher": GOST34112012512,
173 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
174 "key_algorithm": id_tc26_gost3410_2012_512,
176 "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
177 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
178 "hasher": GOST34112012512,
187 if args.issue_with is not None:
188 with open(args.issue_with, "rb") as fd:
189 lines = fd.read().decode("ascii").split("-----")
190 idx = lines.index("BEGIN PRIVATE KEY")
192 raise ValueError("PEM has no PRIVATE KEY")
193 prv_raw = standard_b64decode(lines[idx + 1])
194 idx = lines.index("BEGIN CERTIFICATE")
196 raise ValueError("PEM has no CERTIFICATE")
197 cert_raw = standard_b64decode(lines[idx + 1])
198 pki = PrivateKeyInfo().decod(prv_raw)
199 ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
200 ca_cert = Certificate().decod(cert_raw)
201 tbs = ca_cert["tbsCertificate"]
202 ca_subj = tbs["subject"]
203 curve_oid = GostR34102012PublicKeyParameters().decod(bytes(
204 tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"]
205 ))["publicKeyParamSet"]
207 params for params in AIs.values()
208 if params["publicKeyParamSet"] == curve_oid
211 key_params = GostR34102012PublicKeyParameters((
212 ("publicKeyParamSet", ai["publicKeyParamSet"]),
217 return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
220 if args.reuse_key is not None:
221 with open(args.reuse_key, "rb") as fd:
222 lines = fd.read().decode("ascii").split("-----")
223 idx = lines.index("BEGIN PRIVATE KEY")
225 raise ValueError("PEM has no PRIVATE KEY")
226 prv_raw = standard_b64decode(lines[idx + 1])
227 pki = PrivateKeyInfo().decod(prv_raw)
228 prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
230 prv_raw = urandom(ai["prv_len"])
231 out = stdout if args.out_key is None else open(args.out_key, "w")
232 print("-----BEGIN PRIVATE KEY-----", file=out)
233 print(pem(PrivateKeyInfo((
234 ("version", Integer(0)),
235 ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
236 ("algorithm", ai["key_algorithm"]),
237 ("parameters", Any(key_params)),
239 ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
241 print("-----END PRIVATE KEY-----", file=out)
244 prv = prv_unmarshal(prv_raw)
247 pub_raw = pub_marshal(public_key(curve, prv))
248 rdn = [RelativeDistinguishedName((
249 AttributeTypeAndValue((
250 ("type", AttributeType(id_at_commonName)),
251 ("value", AttributeValue(PrintableString(args.cn))),
255 rdn.append(RelativeDistinguishedName((
256 AttributeTypeAndValue((
257 ("type", AttributeType(id_at_countryName)),
258 ("value", AttributeValue(PrintableString(args.country))),
261 subj = Name(("rdnSequence", RDNSequence(rdn)))
262 not_before = datetime.utcnow()
263 not_after = not_before + timedelta(days=365 * (10 if args.ca else 1))
264 ai_sign = AlgorithmIdentifier((
265 ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]),
269 ("extnID", id_ce_subjectKeyIdentifier),
270 ("extnValue", OctetString(
271 SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
275 ("extnID", id_ce_keyUsage),
276 ("critical", Boolean(True)),
277 ("extnValue", OctetString(KeyUsage(
278 ("keyCertSign" if args.ca else "digitalSignature",),
283 exts.append(Extension((
284 ("extnID", id_ce_basicConstraints),
285 ("critical", Boolean(True)),
286 ("extnValue", OctetString(BasicConstraints((
287 ("cA", Boolean(True)),
291 exts.append(Extension((
292 ("extnID", id_ce_subjectAltName),
293 ("extnValue", OctetString(
295 GeneralName(("dNSName", IA5String(args.cn))),
299 if ca_ai is not None:
301 bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"])))
302 for ext in ca_cert["tbsCertificate"]["extensions"]
303 if ext["extnID"] == id_ce_subjectKeyIdentifier
305 exts.append(Extension((
306 ("extnID", id_ce_authorityKeyIdentifier),
307 ("extnValue", OctetString(AuthorityKeyIdentifier((
308 ("keyIdentifier", KeyIdentifier(caKeyId)),
313 bytes2long(GOST34112012256(urandom(16)).digest()[:20])
314 if args.serial is None else int(args.serial)
316 tbs = TBSCertificate((
317 ("version", Version("v3")),
318 ("serialNumber", CertificateSerialNumber(serial)),
319 ("signature", ai_sign),
320 ("issuer", subj if ca_ai is None else ca_subj),
321 ("validity", Validity((
322 ("notBefore", Time(("utcTime", UTCTime(not_before)))),
323 ("notAfter", Time(("utcTime", UTCTime(not_after)))),
326 ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
327 ("algorithm", AlgorithmIdentifier((
328 ("algorithm", ai["key_algorithm"]),
329 ("parameters", Any(key_params)),
331 ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
333 ("extensions", Extensions(exts)),
336 ("tbsCertificate", tbs),
337 ("signatureAlgorithm", ai_sign),
338 ("signatureValue", BitString(
339 sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1])
340 if ca_ai is None else
341 sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1])
344 out = stdout if args.out_cert is None else open(args.out_cert, "w")
345 print("-----BEGIN CERTIFICATE-----", file=out)
346 print(pem(cert), file=out)
347 print("-----END CERTIFICATE-----", file=out)