summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Matveev <stargrave@stargrave.org>2020-01-29 18:55:39 +0300
committerSergey Matveev <stargrave@stargrave.org>2020-02-03 18:34:06 +0300
commitec39453b4f1c8d7b40383fa2af43d8a7cfeb4b22 (patch)
tree7303e822effc5c0bc44fd8ffcd753ca0017f96d4
parentc6b782134cc714f660c0777e802446d5c6553b7c (diff)
downloadpygost-ec39453b4f1c8d7b40383fa2af43d8a7cfeb4b22.tar.xz
Example X.509 self-signed certificate creation utility
-rw-r--r--README2
-rw-r--r--pygost/asn1schemas/cert-selfsigned-example.py133
-rw-r--r--pygost/asn1schemas/oids.py13
-rw-r--r--pygost/asn1schemas/prvkey.py73
-rw-r--r--pygost/asn1schemas/x509.py21
5 files changed, 242 insertions, 0 deletions
diff --git a/README b/README
index d703323..71ee5bb 100644
--- 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
index 0000000..5cdca8f
--- /dev/null
+++ b/pygost/asn1schemas/cert-selfsigned-example.py
@@ -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-----")
diff --git a/pygost/asn1schemas/oids.py b/pygost/asn1schemas/oids.py
index 1e8e4f6..eff060d 100644
--- a/pygost/asn1schemas/oids.py
+++ b/pygost/asn1schemas/oids.py
@@ -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
index 0000000..67e6fb1
--- /dev/null
+++ b/pygost/asn1schemas/prvkey.py
@@ -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()),
+ )
diff --git a/pygost/asn1schemas/x509.py b/pygost/asn1schemas/x509.py
index fae6627..bbeca66 100644
--- a/pygost/asn1schemas/x509.py
+++ b/pygost/asn1schemas/x509.py
@@ -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()),