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 resulting key",
111 help="Path to PEM with the resulting certificate",
113 args = parser.parse_args()
116 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
117 "key_algorithm": id_tc26_gost3410_2012_256,
119 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
120 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
121 "hasher": GOST34112012256,
124 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
125 "key_algorithm": id_tc26_gost3410_2012_256,
127 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
128 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
129 "hasher": GOST34112012256,
132 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
133 "key_algorithm": id_tc26_gost3410_2012_256,
135 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
136 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
137 "hasher": GOST34112012256,
140 "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
141 "key_algorithm": id_tc26_gost3410_2012_256,
143 "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
144 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
145 "hasher": GOST34112012256,
148 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
149 "key_algorithm": id_tc26_gost3410_2012_512,
151 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
152 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
153 "hasher": GOST34112012512,
156 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
157 "key_algorithm": id_tc26_gost3410_2012_512,
159 "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
160 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
161 "hasher": GOST34112012512,
164 "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
165 "key_algorithm": id_tc26_gost3410_2012_512,
167 "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
168 "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
169 "hasher": GOST34112012512,
178 if args.issue_with is not None:
179 with open(args.issue_with, "rb") as fd:
180 lines = fd.read().decode("ascii").split("-----")
181 idx = lines.index("BEGIN PRIVATE KEY")
183 raise ValueError("PEM has no PRIVATE KEY")
184 prv_raw = standard_b64decode(lines[idx + 1])
185 idx = lines.index("BEGIN CERTIFICATE")
187 raise ValueError("PEM has no CERTIFICATE")
188 cert_raw = standard_b64decode(lines[idx + 1])
189 pki = PrivateKeyInfo().decod(prv_raw)
190 ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
191 ca_cert = Certificate().decod(cert_raw)
192 tbs = ca_cert["tbsCertificate"]
193 ca_subj = tbs["subject"]
194 curve_oid = GostR34102012PublicKeyParameters().decod(bytes(
195 tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"]
196 ))["publicKeyParamSet"]
198 params for params in AIs.values()
199 if params["publicKeyParamSet"] == curve_oid
204 return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
207 key_params = GostR34102012PublicKeyParameters((
208 ("publicKeyParamSet", ai["publicKeyParamSet"]),
211 prv_raw = urandom(ai["prv_len"])
212 out = stdout if args.out_key is None else open(args.out_key, "w")
213 print("-----BEGIN PRIVATE KEY-----", file=out)
214 print(pem(PrivateKeyInfo((
215 ("version", Integer(0)),
216 ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
217 ("algorithm", ai["key_algorithm"]),
218 ("parameters", Any(key_params)),
220 ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
222 print("-----END PRIVATE KEY-----", file=out)
224 prv = prv_unmarshal(prv_raw)
226 pub_raw = pub_marshal(public_key(curve, prv))
227 rdn = [RelativeDistinguishedName((
228 AttributeTypeAndValue((
229 ("type", AttributeType(id_at_commonName)),
230 ("value", AttributeValue(PrintableString(args.cn))),
234 rdn.append(RelativeDistinguishedName((
235 AttributeTypeAndValue((
236 ("type", AttributeType(id_at_countryName)),
237 ("value", AttributeValue(PrintableString(args.country))),
240 subj = Name(("rdnSequence", RDNSequence(rdn)))
241 not_before = datetime.utcnow()
242 not_after = not_before + timedelta(days=365 * (10 if args.ca else 1))
243 ai_sign = AlgorithmIdentifier((
244 ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]),
248 ("extnID", id_ce_subjectKeyIdentifier),
249 ("extnValue", OctetString(
250 SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
254 ("extnID", id_ce_keyUsage),
255 ("critical", Boolean(True)),
256 ("extnValue", OctetString(KeyUsage(
257 ("keyCertSign" if args.ca else "digitalSignature",),
262 exts.append(Extension((
263 ("extnID", id_ce_basicConstraints),
264 ("critical", Boolean(True)),
265 ("extnValue", OctetString(BasicConstraints((
266 ("cA", Boolean(True)),
270 exts.append(Extension((
271 ("extnID", id_ce_subjectAltName),
272 ("extnValue", OctetString(
274 GeneralName(("dNSName", IA5String(args.cn))),
278 if ca_ai is not None:
280 bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"])))
281 for ext in ca_cert["tbsCertificate"]["extensions"]
282 if ext["extnID"] == id_ce_subjectKeyIdentifier
284 exts.append(Extension((
285 ("extnID", id_ce_authorityKeyIdentifier),
286 ("extnValue", OctetString(AuthorityKeyIdentifier((
287 ("keyIdentifier", KeyIdentifier(caKeyId)),
292 bytes2long(GOST34112012256(urandom(16)).digest()[:20])
293 if args.serial is None else int(args.serial)
295 tbs = TBSCertificate((
296 ("version", Version("v3")),
297 ("serialNumber", CertificateSerialNumber(serial)),
298 ("signature", ai_sign),
299 ("issuer", subj if ca_ai is None else ca_subj),
300 ("validity", Validity((
301 ("notBefore", Time(("utcTime", UTCTime(not_before)))),
302 ("notAfter", Time(("utcTime", UTCTime(not_after)))),
305 ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
306 ("algorithm", AlgorithmIdentifier((
307 ("algorithm", ai["key_algorithm"]),
308 ("parameters", Any(key_params)),
310 ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
312 ("extensions", Extensions(exts)),
315 ("tbsCertificate", tbs),
316 ("signatureAlgorithm", ai_sign),
317 ("signatureValue", BitString(
318 sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1])
319 if ca_ai is None else
320 sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1])
323 out = stdout if args.out_cert is None else open(args.out_cert, "w")
324 print("-----BEGIN CERTIFICATE-----", file=out)
325 print(pem(cert), file=out)
326 print("-----END CERTIFICATE-----", file=out)