]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/asn1schemas/cert-selfsigned-example.py
Ability to issue child certificate
[pygost.git] / pygost / asn1schemas / cert-selfsigned-example.py
old mode 100644 (file)
new mode 100755 (executable)
index 8c589c2..94b4b34
@@ -1,7 +1,9 @@
+#!/usr/bin/env python3
 """Create example self-signed X.509 certificate
 """
 
 from argparse import ArgumentParser
+from base64 import standard_b64decode
 from base64 import standard_b64encode
 from datetime import datetime
 from datetime import timedelta
@@ -11,6 +13,7 @@ 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
@@ -18,6 +21,7 @@ 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
@@ -42,10 +46,12 @@ 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
@@ -76,8 +82,12 @@ parser.add_argument(
     required=True,
     help="Signing algorithm: {256[ABCD],512[ABC]}",
 )
+parser.add_argument(
+    "--issue-with",
+    help="Path to PEM with CA to issue the child",
+)
 args = parser.parse_args()
-ai = {
+AIs = {
     "256A": {
         "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA,
         "key_algorithm": id_tc26_gost3410_2012_256,
@@ -134,7 +144,34 @@ ai = {
         "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512,
         "hasher": GOST34112012512,
     },
-}[args.ai]
+}
+ai = AIs[args.ai]
+
+ca_prv = None
+ca_subj = None
+ca_ai = None
+if args.issue_with is not None:
+    with open(args.issue_with, "rb") as fd:
+        lines = fd.read().decode("ascii").split("-----")
+    idx = lines.index("BEGIN PRIVATE KEY")
+    if idx == -1:
+        raise ValueError("PEM has no PRIVATE KEY")
+    prv_raw = standard_b64decode(lines[idx + 1])
+    idx = lines.index("BEGIN CERTIFICATE")
+    if idx == -1:
+        raise ValueError("PEM has no CERTIFICATE")
+    cert_raw = standard_b64decode(lines[idx + 1])
+    pki = PrivateKeyInfo().decod(prv_raw)
+    ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"]))))
+    tbs = Certificate().decod(cert_raw)["tbsCertificate"]
+    ca_subj = tbs["subject"]
+    curve_oid = GostR34102012PublicKeyParameters().decod(bytes(
+        tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"]
+    ))["publicKeyParamSet"]
+    ca_ai = next(iter([
+        params for params in AIs.values()
+        if params["publicKeyParamSet"] == curve_oid
+    ]))
 
 
 def pem(obj):
@@ -153,7 +190,7 @@ print(pem(PrivateKeyInfo((
         ("algorithm", ai["key_algorithm"]),
         ("parameters", Any(key_params)),
     ))),
-    ("privateKey", PrivateKey(prv_raw)),
+    ("privateKey", PrivateKey(OctetString(prv_raw).encode())),
 ))))
 print("-----END PRIVATE KEY-----")
 
@@ -171,15 +208,24 @@ subj = Name(("rdnSequence", RDNSequence([
 not_before = datetime.utcnow()
 not_after = not_before + timedelta(days=365)
 ai_sign = AlgorithmIdentifier((
-    ("algorithm", ai["sign_algorithm"],),
+    ("algorithm", (ai if ca_ai is None else ca_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((
@@ -190,7 +236,7 @@ tbs = TBSCertificate((
     ("version", Version("v3")),
     ("serialNumber", CertificateSerialNumber(12345)),
     ("signature", ai_sign),
-    ("issuer", subj),
+    ("issuer", subj if ca_ai is None else ca_subj),
     ("validity", Validity((
         ("notBefore", Time(("utcTime", UTCTime(not_before)))),
         ("notAfter", Time(("utcTime", UTCTime(not_after)))),
@@ -208,11 +254,11 @@ tbs = TBSCertificate((
 cert = Certificate((
     ("tbsCertificate", tbs),
     ("signatureAlgorithm", ai_sign),
-    ("signatureValue", BitString(sign(
-        curve,
-        prv,
-        ai["hasher"](tbs.encode()).digest()[::-1],
-    ))),
+    ("signatureValue", BitString(
+        sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1])
+        if ca_ai is None else
+        sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1])
+    )),
 ))
 print("-----BEGIN CERTIFICATE-----")
 print(pem(cert))