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