]> Cypherpunks.ru repositories - pygost.git/blob - pygost/asn1schemas/cert-selfsigned-example.py
Make Go's crypto/x509 compatible example certificates
[pygost.git] / pygost / asn1schemas / cert-selfsigned-example.py
1 """Create example self-signed X.509 certificate
2 """
3
4 from argparse import ArgumentParser
5 from base64 import standard_b64encode
6 from datetime import datetime
7 from datetime import timedelta
8 from os import urandom
9 from textwrap import fill
10
11 from pyderasn import Any
12 from pyderasn import BitString
13 from pyderasn import Boolean
14 from pyderasn import IA5String
15 from pyderasn import Integer
16 from pyderasn import OctetString
17 from pyderasn import PrintableString
18 from pyderasn import UTCTime
19
20 from pygost.asn1schemas.oids import id_at_commonName
21 from pygost.asn1schemas.oids import id_ce_basicConstraints
22 from pygost.asn1schemas.oids import id_ce_subjectAltName
23 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
24 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
25 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
26 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB
27 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC
28 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD
29 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
30 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
31 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB
32 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC
33 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
34 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
35 from pygost.asn1schemas.prvkey import PrivateKey
36 from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
37 from pygost.asn1schemas.prvkey import PrivateKeyInfo
38 from pygost.asn1schemas.x509 import AlgorithmIdentifier
39 from pygost.asn1schemas.x509 import AttributeType
40 from pygost.asn1schemas.x509 import AttributeTypeAndValue
41 from pygost.asn1schemas.x509 import AttributeValue
42 from pygost.asn1schemas.x509 import BasicConstraints
43 from pygost.asn1schemas.x509 import Certificate
44 from pygost.asn1schemas.x509 import CertificateSerialNumber
45 from pygost.asn1schemas.x509 import Extension
46 from pygost.asn1schemas.x509 import Extensions
47 from pygost.asn1schemas.x509 import GeneralName
48 from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
49 from pygost.asn1schemas.x509 import Name
50 from pygost.asn1schemas.x509 import RDNSequence
51 from pygost.asn1schemas.x509 import RelativeDistinguishedName
52 from pygost.asn1schemas.x509 import SubjectAltName
53 from pygost.asn1schemas.x509 import SubjectKeyIdentifier
54 from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
55 from pygost.asn1schemas.x509 import TBSCertificate
56 from pygost.asn1schemas.x509 import Time
57 from pygost.asn1schemas.x509 import Validity
58 from pygost.asn1schemas.x509 import Version
59 from pygost.gost3410 import CURVES
60 from pygost.gost3410 import prv_unmarshal
61 from pygost.gost3410 import pub_marshal
62 from pygost.gost3410 import public_key
63 from pygost.gost3410 import sign
64 from pygost.gost34112012256 import GOST34112012256
65 from pygost.gost34112012512 import GOST34112012512
66
67 parser = ArgumentParser(description="Self-signed X.509 certificate creator")
68 parser.add_argument(
69     "--ca",
70     action="store_true",
71     help="Enable BasicConstraints.cA",
72 )
73 parser.add_argument(
74     "--cn",
75     required=True,
76     help="Subject's CommonName",
77 )
78 parser.add_argument(
79     "--ai",
80     required=True,
81     help="Signing algorithm: {256[ABCD],512[ABC]}",
82 )
83 args = parser.parse_args()
84 ai = {
85     "256A": {
86         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
87         "key_algorithm": id_tc26_gost3410_2012_256,
88         "prv_len": 32,
89         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
90         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
91         "hasher": GOST34112012256,
92     },
93     "256B": {
94         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
95         "key_algorithm": id_tc26_gost3410_2012_256,
96         "prv_len": 32,
97         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
98         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
99         "hasher": GOST34112012256,
100     },
101     "256C": {
102         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
103         "key_algorithm": id_tc26_gost3410_2012_256,
104         "prv_len": 32,
105         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
106         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
107         "hasher": GOST34112012256,
108     },
109     "256D": {
110         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
111         "key_algorithm": id_tc26_gost3410_2012_256,
112         "prv_len": 32,
113         "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
114         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
115         "hasher": GOST34112012256,
116     },
117     "512A": {
118         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
119         "key_algorithm": id_tc26_gost3410_2012_512,
120         "prv_len": 64,
121         "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
122         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
123         "hasher": GOST34112012512,
124     },
125     "512B": {
126         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
127         "key_algorithm": id_tc26_gost3410_2012_512,
128         "prv_len": 64,
129         "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
130         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
131         "hasher": GOST34112012512,
132     },
133     "512C": {
134         "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
135         "key_algorithm": id_tc26_gost3410_2012_512,
136         "prv_len": 64,
137         "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
138         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
139         "hasher": GOST34112012512,
140     },
141 }[args.ai]
142
143
144 def pem(obj):
145     return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
146
147
148 key_params = GostR34102012PublicKeyParameters((
149     ("publicKeyParamSet", ai["publicKeyParamSet"]),
150 ))
151
152 prv_raw = urandom(ai["prv_len"])
153 print("-----BEGIN PRIVATE KEY-----")
154 print(pem(PrivateKeyInfo((
155     ("version", Integer(0)),
156     ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
157         ("algorithm", ai["key_algorithm"]),
158         ("parameters", Any(key_params)),
159     ))),
160     ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
161 ))))
162 print("-----END PRIVATE KEY-----")
163
164 prv = prv_unmarshal(prv_raw)
165 curve = ai["curve"]
166 pub_raw = pub_marshal(public_key(curve, prv))
167 subj = Name(("rdnSequence", RDNSequence([
168     RelativeDistinguishedName((
169         AttributeTypeAndValue((
170             ("type", AttributeType(id_at_commonName)),
171             ("value", AttributeValue(PrintableString(args.cn))),
172         )),
173     ))
174 ])))
175 not_before = datetime.utcnow()
176 not_after = not_before + timedelta(days=365)
177 ai_sign = AlgorithmIdentifier((
178     ("algorithm", ai["sign_algorithm"],),
179 ))
180 exts = [
181     Extension((
182         ("extnID", id_ce_subjectKeyIdentifier),
183         ("extnValue", OctetString(
184             SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
185         )),
186     )),
187     Extension((
188         ("extnID", id_ce_subjectAltName),
189         ("extnValue", OctetString(
190             SubjectAltName((
191                 GeneralName(("dNSName", IA5String(args.cn))),
192             )).encode()
193         )),
194     )),
195 ]
196 if args.ca:
197     exts.append(Extension((
198         ("extnID", id_ce_basicConstraints),
199         ("extnValue", OctetString(BasicConstraints((("cA", Boolean(True)),)).encode())),
200     )))
201 tbs = TBSCertificate((
202     ("version", Version("v3")),
203     ("serialNumber", CertificateSerialNumber(12345)),
204     ("signature", ai_sign),
205     ("issuer", subj),
206     ("validity", Validity((
207         ("notBefore", Time(("utcTime", UTCTime(not_before)))),
208         ("notAfter", Time(("utcTime", UTCTime(not_after)))),
209     ))),
210     ("subject", subj),
211     ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
212         ("algorithm", AlgorithmIdentifier((
213             ("algorithm", ai["key_algorithm"]),
214             ("parameters", Any(key_params)),
215         ))),
216         ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
217     ))),
218     ("extensions", Extensions(exts)),
219 ))
220 cert = Certificate((
221     ("tbsCertificate", tbs),
222     ("signatureAlgorithm", ai_sign),
223     ("signatureValue", BitString(sign(
224         curve,
225         prv,
226         ai["hasher"](tbs.encode()).digest()[::-1],
227     ))),
228 ))
229 print("-----BEGIN CERTIFICATE-----")
230 print(pem(cert))
231 print("-----END CERTIFICATE-----")