]> Cypherpunks.ru repositories - pygost.git/commitdiff
Example X.509 self-signed certificate creation utility
authorSergey Matveev <stargrave@stargrave.org>
Wed, 29 Jan 2020 15:55:39 +0000 (18:55 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 3 Feb 2020 15:34:06 +0000 (18:34 +0300)
README
pygost/asn1schemas/cert-selfsigned-example.py [new file with mode: 0644]
pygost/asn1schemas/oids.py
pygost/asn1schemas/prvkey.py [new file with mode: 0644]
pygost/asn1schemas/x509.py

diff --git a/README b/README
index d703323e63e95ef5255b5222232920fd400028b6..71ee5bbcb385b887ff97b886850a12a2662323d7 100644 (file)
--- a/README
+++ b/README
@@ -50,6 +50,8 @@ Example 34.10-2012 keypair generation, signing and verifying:
     True
 
 Other examples can be found in docstrings and unittests.
+Example self-signed X.509 certificate creation can be found in
+pygost/asn1schemas/cert-selfsigned-example.py.
 
 PyGOST is free software: see the file COPYING for copying conditions.
 
diff --git a/pygost/asn1schemas/cert-selfsigned-example.py b/pygost/asn1schemas/cert-selfsigned-example.py
new file mode 100644 (file)
index 0000000..5cdca8f
--- /dev/null
@@ -0,0 +1,133 @@
+"""Create example self-signed X.509 certificate
+"""
+
+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 sys import stderr
+from textwrap import fill
+
+from pyderasn import Any
+from pyderasn import BitString
+from pyderasn import Integer
+from pyderasn import ObjectIdentifier
+from pyderasn import OctetString
+from pyderasn import PrintableString
+from pyderasn import UTCTime
+
+from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier
+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_signwithdigest_gost3410_2012_512
+from pygost.asn1schemas.prvkey import PrivateKey
+from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier
+from pygost.asn1schemas.prvkey import PrivateKeyInfo
+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 Certificate
+from pygost.asn1schemas.x509 import CertificateSerialNumber
+from pygost.asn1schemas.x509 import Extension
+from pygost.asn1schemas.x509 import Extensions
+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 SubjectKeyIdentifier
+from pygost.asn1schemas.x509 import SubjectPublicKeyInfo
+from pygost.asn1schemas.x509 import TBSCertificate
+from pygost.asn1schemas.x509 import Time
+from pygost.asn1schemas.x509 import Validity
+from pygost.asn1schemas.x509 import Version
+from pygost.gost3410 import CURVES
+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.gost34112012512 import GOST34112012512
+
+if len(argv) != 2:
+    print("Usage: cert-selfsigned-example.py COMMON-NAME", file=stderr)
+    sys_exit(1)
+
+def pem(obj):
+    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),
+))
+
+prv_raw = urandom(64)
+print("-----BEGIN PRIVATE KEY-----")
+print(pem(PrivateKeyInfo((
+    ("version", Integer(0)),
+    ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier((
+        ("algorithm", id_tc26_gost3410_2012_512),
+        ("parameters", Any(key_params)),
+    ))),
+    ("privateKey", PrivateKey(prv_raw)),
+))))
+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)
+id_at_commonName = ObjectIdentifier("2.5.4.3")
+subj = Name(("rdnSequence", RDNSequence([
+    RelativeDistinguishedName((
+        AttributeTypeAndValue((
+            ("type", AttributeType(id_at_commonName)),
+            ("value", AttributeValue(PrintableString(argv[1]))),
+        )),
+    ))
+])))
+not_before = datetime.utcnow()
+not_after = not_before + timedelta(days=365)
+ai_sign = AlgorithmIdentifier((
+    ("algorithm", id_tc26_signwithdigest_gost3410_2012_512),
+))
+tbs = TBSCertificate((
+    ("version", Version("v3")),
+    ("serialNumber", CertificateSerialNumber(12345)),
+    ("signature", ai_sign),
+    ("issuer", subj),
+    ("validity", Validity((
+        ("notBefore", Time(("utcTime", UTCTime(not_before)))),
+        ("notAfter", Time(("utcTime", UTCTime(not_after)))),
+    ))),
+    ("subject", subj),
+    ("subjectPublicKeyInfo", SubjectPublicKeyInfo((
+        ("algorithm", AlgorithmIdentifier((
+            ("algorithm", id_tc26_gost3410_2012_512),
+            ("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()
+            )),
+        )),
+    ))),
+))
+cert = Certificate((
+    ("tbsCertificate", tbs),
+    ("signatureAlgorithm", ai_sign),
+    ("signatureValue", BitString(sign(
+        curve,
+        prv,
+        GOST34112012512(tbs.encode()).digest(),
+        mode=2012,
+    ))),
+))
+print("-----BEGIN CERTIFICATE-----")
+print(pem(cert))
+print("-----END CERTIFICATE-----")
index 1e8e4f69d6e3ed1e1bfb411c6b79cbfc57502d9b..eff060d891eba5ad07b45e48d096bd6eb009c255 100644 (file)
@@ -10,7 +10,20 @@ id_encryptedData = id_pkcs7 + (6,)
 id_data = ObjectIdentifier("1.2.840.113549.1.7.1")
 id_tc26_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.1.1")
 id_tc26_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.1.2")
+id_tc26_gost3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.2.2")
+id_tc26_gost3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.2.3")
+id_tc26_gost3410_2012_256_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.1.1")
+id_tc26_gost3410_2012_256_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.1.2")
+id_tc26_gost3410_2012_256_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.1.3")
+id_tc26_gost3410_2012_256_paramSetD = ObjectIdentifier("1.2.643.7.1.2.1.1.4")
+id_tc26_gost3410_2012_512_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.2.1")
+id_tc26_gost3410_2012_512_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.2.2")
+id_tc26_gost3410_2012_512_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.2.3")
+id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
+id_tc26_signwithdigest_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")
 id_Gost28147_89 = ObjectIdentifier("1.2.643.2.2.21")
 
 id_pbes2 = ObjectIdentifier("1.2.840.113549.1.5.13")
 id_pbkdf2 = ObjectIdentifier("1.2.840.113549.1.5.12")
+
+id_ce_subjectKeyIdentifier = ObjectIdentifier("2.5.29.14")
diff --git a/pygost/asn1schemas/prvkey.py b/pygost/asn1schemas/prvkey.py
new file mode 100644 (file)
index 0000000..67e6fb1
--- /dev/null
@@ -0,0 +1,73 @@
+# coding: utf-8
+# PyGOST -- Pure Python GOST cryptographic functions library
+# Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from pyderasn import Any
+from pyderasn import BitString
+from pyderasn import Choice
+from pyderasn import Integer
+from pyderasn import Null
+from pyderasn import ObjectIdentifier
+from pyderasn import OctetString
+from pyderasn import Sequence
+from pyderasn import tag_ctxc
+
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256
+from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512
+from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters
+
+
+class ECParameters(Choice):
+    schema = (
+        ("namedCurve", ObjectIdentifier()),
+        ("implicitCurve", Null()),
+        # ("specifiedCurve", SpecifiedECDomain()),
+    )
+
+
+ecPrivkeyVer1 = Integer(1)
+
+
+class ECPrivateKey(Sequence):
+    schema = (
+        ("version", Integer(ecPrivkeyVer1)),
+        ("privateKey", OctetString()),
+        ("parameters", ECParameters(expl=tag_ctxc(0), optional=True)),
+        ("publicKey", BitString(expl=tag_ctxc(1), optional=True)),
+    )
+
+
+class PrivateKeyAlgorithmIdentifier(Sequence):
+    schema = (
+        ("algorithm", ObjectIdentifier(defines=(
+            (("parameters",), {
+                id_tc26_gost3410_2012_256: GostR34102012PublicKeyParameters(),
+                id_tc26_gost3410_2012_512: GostR34102012PublicKeyParameters(),
+            }),
+        ))),
+        ("parameters", Any(optional=True)),
+    )
+
+
+class PrivateKey(OctetString):
+    pass
+
+
+class PrivateKeyInfo(Sequence):
+    schema = (
+        ("version", Integer(0)),
+        ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier()),
+        ("privateKey", PrivateKey()),
+    )
index fae66270b82206e3577225e2d83f57f1417dcd6c..bbeca669c5f5f3412a1ddfa0135c6fb62a1a912a 100644 (file)
@@ -112,6 +112,19 @@ class Validity(Sequence):
     )
 
 
+id_tc26_gost_28147_param_Z = ObjectIdentifier("1.2.643.7.1.2.5.1.1")
+
+
+class GostR34102012PublicKeyParameters(Sequence):
+    schema = (
+        ("publicKeyParamSet", ObjectIdentifier()),
+        ("digestParamSet", ObjectIdentifier()),
+        ("encryptionParamSet", ObjectIdentifier(
+            default=id_tc26_gost_28147_param_Z,
+        )),
+    )
+
+
 class SubjectPublicKeyInfo(Sequence):
     schema = (
         ("algorithm", AlgorithmIdentifier()),
@@ -123,6 +136,14 @@ class UniqueIdentifier(BitString):
     pass
 
 
+class KeyIdentifier(OctetString):
+    pass
+
+
+class SubjectKeyIdentifier(KeyIdentifier):
+    pass
+
+
 class Extension(Sequence):
     schema = (
         ("extnID", ObjectIdentifier()),