README | 2 ++ pygost/asn1schemas/cert-selfsigned-example.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++ pygost/asn1schemas/oids.py | 13 +++++++++++++ pygost/asn1schemas/prvkey.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ pygost/asn1schemas/x509.py | 21 +++++++++++++++++++++ diff --git a/README b/README index 9b98a999d661b84fcfe31ce7e699ab4cdce88f4b4205b5bce6d2c07fd9f89b9b..190bed813ada291036576b7f89b8850068d91a4e1be783a03d8058e5911a6481 100644 --- a/README +++ b/README @@ -50,6 +50,8 @@ >>> verify(curve, pub, dgst, signature, mode=2012) 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 0000000000000000000000000000000000000000..47735613fa2f4399a061ce4e4077890a90ea341f8f2b00a40a7c218a26145863 --- /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 7f03cf0bd951fc79873d19a1b3e0add91756dfdb57d61254e70e3e835b4d6d8e..0a63f50493add20cc71c2e4dfcbce8a39e098e4716dae455bed88b6e6a57e541 100644 --- a/pygost/asn1schemas/oids.py +++ b/pygost/asn1schemas/oids.py @@ -10,7 +10,20 @@ 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 0000000000000000000000000000000000000000..70456cab0ca33ca2a75f9f66b9a6b155dbbef873e001f171e67578d481a6acad --- /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 +# +# 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 . + +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 a682aaa3ea1653dca1847e0e4adb15f7abf0e4d63b05df9aaed3c6f81afacf2c..05f87dbfd92af1cf4a095eefa0c77c059b8ee01729472eb5bce0672101b37e39 100644 --- a/pygost/asn1schemas/x509.py +++ b/pygost/asn1schemas/x509.py @@ -112,6 +112,19 @@ ("notAfter", Time()), ) +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()), @@ -120,6 +133,14 @@ ) class UniqueIdentifier(BitString): + pass + + +class KeyIdentifier(OctetString): + pass + + +class SubjectKeyIdentifier(KeyIdentifier): pass