]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/asn1schemas/cert-selfsigned-example.py
cert-selfsigned-example is executable
[pygost.git] / pygost / asn1schemas / cert-selfsigned-example.py
old mode 100644 (file)
new mode 100755 (executable)
index e8a3327..20e873b
@@ -1,26 +1,37 @@
+#!/usr/bin/env python3
 """Create example self-signed X.509 certificate
 """
 
+from argparse import ArgumentParser
 from base64 import standard_b64encode
 from datetime import datetime
 from datetime import timedelta
 from os import urandom
-from sys import argv
-from sys import exit as sys_exit
 from textwrap import fill
 
 from pyderasn import Any
 from pyderasn import BitString
+from pyderasn import Boolean
+from pyderasn import IA5String
 from pyderasn import Integer
 from pyderasn import OctetString
 from pyderasn import PrintableString
 from pyderasn import UTCTime
 
 from pygost.asn1schemas.oids import id_at_commonName
+from pygost.asn1schemas.oids import id_ce_basicConstraints
+from pygost.asn1schemas.oids import id_ce_subjectAltName
 from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
 from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA
-from pygost.asn1schemas.oids import id_tc26_gost3411_2012_512
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC
+from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256
 from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512
 from pygost.asn1schemas.prvkey import PrivateKey
 from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
@@ -29,14 +40,17 @@ from pygost.asn1schemas.x509 import AlgorithmIdentifier
 from pygost.asn1schemas.x509 import AttributeType
 from pygost.asn1schemas.x509 import AttributeTypeAndValue
 from pygost.asn1schemas.x509 import AttributeValue
+from pygost.asn1schemas.x509 import BasicConstraints
 from pygost.asn1schemas.x509 import Certificate
 from pygost.asn1schemas.x509 import CertificateSerialNumber
 from pygost.asn1schemas.x509 import Extension
 from pygost.asn1schemas.x509 import Extensions
+from pygost.asn1schemas.x509 import GeneralName
 from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
 from pygost.asn1schemas.x509 import Name
 from pygost.asn1schemas.x509 import RDNSequence
 from pygost.asn1schemas.x509 import RelativeDistinguishedName
+from pygost.asn1schemas.x509 import SubjectAltName
 from pygost.asn1schemas.x509 import SubjectKeyIdentifier
 from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
 from pygost.asn1schemas.x509 import TBSCertificate
@@ -48,49 +62,143 @@ from pygost.gost3410 import prv_unmarshal
 from pygost.gost3410 import pub_marshal
 from pygost.gost3410 import public_key
 from pygost.gost3410 import sign
+from pygost.gost34112012256 import GOST34112012256
 from pygost.gost34112012512 import GOST34112012512
 
-if len(argv) != 2:
-    sys_exit("Usage: cert-selfsigned-example.py COMMON-NAME")
+parser = ArgumentParser(description="Self-signed X.509 certificate creator")
+parser.add_argument(
+    "--ca",
+    action="store_true",
+    help="Enable BasicConstraints.cA",
+)
+parser.add_argument(
+    "--cn",
+    required=True,
+    help="Subject's CommonName",
+)
+parser.add_argument(
+    "--ai",
+    required=True,
+    help="Signing algorithm: {256[ABCD],512[ABC]}",
+)
+args = parser.parse_args()
+ai = {
+    "256A": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
+        "key_algorithm": id_tc26_gost3410_2012_256,
+        "prv_len": 32,
+        "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
+        "hasher": GOST34112012256,
+    },
+    "256B": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB,
+        "key_algorithm": id_tc26_gost3410_2012_256,
+        "prv_len": 32,
+        "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
+        "hasher": GOST34112012256,
+    },
+    "256C": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC,
+        "key_algorithm": id_tc26_gost3410_2012_256,
+        "prv_len": 32,
+        "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
+        "hasher": GOST34112012256,
+    },
+    "256D": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD,
+        "key_algorithm": id_tc26_gost3410_2012_256,
+        "prv_len": 32,
+        "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256,
+        "hasher": GOST34112012256,
+    },
+    "512A": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA,
+        "key_algorithm": id_tc26_gost3410_2012_512,
+        "prv_len": 64,
+        "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
+        "hasher": GOST34112012512,
+    },
+    "512B": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB,
+        "key_algorithm": id_tc26_gost3410_2012_512,
+        "prv_len": 64,
+        "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
+        "hasher": GOST34112012512,
+    },
+    "512C": {
+        "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC,
+        "key_algorithm": id_tc26_gost3410_2012_512,
+        "prv_len": 64,
+        "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"],
+        "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
+        "hasher": GOST34112012512,
+    },
+}[args.ai]
 
 
 def pem(obj):
-    return fill(standard_b64encode(obj.encode()).decode('ascii'), 64)
+    return fill(standard_b64encode(obj.encode()).decode("ascii"), 64)
 
 
 key_params = GostR34102012PublicKeyParameters((
-    ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetA),
-    ("digestParamSet", id_tc26_gost3411_2012_512),
+    ("publicKeyParamSet", ai["publicKeyParamSet"]),
 ))
 
-prv_raw = urandom(64)
+prv_raw = urandom(ai["prv_len"])
 print("-----BEGIN PRIVATE KEY-----")
 print(pem(PrivateKeyInfo((
     ("version", Integer(0)),
     ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
-        ("algorithm", id_tc26_gost3410_2012_512),
+        ("algorithm", ai["key_algorithm"]),
         ("parameters", Any(key_params)),
     ))),
-    ("privateKey", PrivateKey(prv_raw)),
+    ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
 ))))
 print("-----END PRIVATE KEY-----")
 
 prv = prv_unmarshal(prv_raw)
-curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"]
-pub_raw = pub_marshal(public_key(curve, prv), mode=2012)
+curve = ai["curve"]
+pub_raw = pub_marshal(public_key(curve, prv))
 subj = Name(("rdnSequence", RDNSequence([
     RelativeDistinguishedName((
         AttributeTypeAndValue((
             ("type", AttributeType(id_at_commonName)),
-            ("value", AttributeValue(PrintableString(argv[1]))),
+            ("value", AttributeValue(PrintableString(args.cn))),
         )),
     ))
 ])))
 not_before = datetime.utcnow()
 not_after = not_before + timedelta(days=365)
 ai_sign = AlgorithmIdentifier((
-    ("algorithm", id_tc26_signwithdigest_gost3410_2012_512),
+    ("algorithm", ai["sign_algorithm"],),
 ))
+exts = [
+    Extension((
+        ("extnID", id_ce_subjectKeyIdentifier),
+        ("extnValue", OctetString(
+            SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode()
+        )),
+    )),
+    Extension((
+        ("extnID", id_ce_subjectAltName),
+        ("extnValue", OctetString(
+            SubjectAltName((
+                GeneralName(("dNSName", IA5String(args.cn))),
+            )).encode()
+        )),
+    )),
+]
+if args.ca:
+    exts.append(Extension((
+        ("extnID", id_ce_basicConstraints),
+        ("extnValue", OctetString(BasicConstraints((("cA", Boolean(True)),)).encode())),
+    )))
 tbs = TBSCertificate((
     ("version", Version("v3")),
     ("serialNumber", CertificateSerialNumber(12345)),
@@ -103,19 +211,12 @@ tbs = TBSCertificate((
     ("subject", subj),
     ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
         ("algorithm", AlgorithmIdentifier((
-            ("algorithm", id_tc26_gost3410_2012_512),
+            ("algorithm", ai["key_algorithm"]),
             ("parameters", Any(key_params)),
         ))),
         ("subjectPublicKey", BitString(OctetString(pub_raw).encode())),
     ))),
-    ("extensions", Extensions((
-        Extension((
-            ("extnID", id_ce_subjectKeyIdentifier),
-            ("extnValue", OctetString(
-                SubjectKeyIdentifier(GOST34112012512(pub_raw).digest()[:20]).encode()
-            )),
-        )),
-    ))),
+    ("extensions", Extensions(exts)),
 ))
 cert = Certificate((
     ("tbsCertificate", tbs),
@@ -123,8 +224,7 @@ cert = Certificate((
     ("signatureValue", BitString(sign(
         curve,
         prv,
-        GOST34112012512(tbs.encode()).digest()[::-1],
-        mode=2012,
+        ai["hasher"](tbs.encode()).digest()[::-1],
     ))),
 ))
 print("-----BEGIN CERTIFICATE-----")