X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pygost%2Fasn1schemas%2Fcert-selfsigned-example.py;h=bd562b1c6caa6dbca8a8e698fd280f1cc4c39c3f;hb=7c81d18aac97d24c40d079ce969998bc16f65a17;hp=94b4b34cf4ba318dc5bf75a0ac427b12d58dab65;hpb=a57c47d89bb73d7967a6cfbf26c6db2d41e1ca8d;p=pygost.git diff --git a/pygost/asn1schemas/cert-selfsigned-example.py b/pygost/asn1schemas/cert-selfsigned-example.py index 94b4b34..bd562b1 100755 --- a/pygost/asn1schemas/cert-selfsigned-example.py +++ b/pygost/asn1schemas/cert-selfsigned-example.py @@ -8,6 +8,8 @@ from base64 import standard_b64encode from datetime import datetime from datetime import timedelta from os import urandom +from sys import exit as sys_exit +from sys import stdout from textwrap import fill from pyderasn import Any @@ -20,7 +22,10 @@ from pyderasn import PrintableString from pyderasn import UTCTime from pygost.asn1schemas.oids import id_at_commonName +from pygost.asn1schemas.oids import id_at_countryName +from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier from pygost.asn1schemas.oids import id_ce_basicConstraints +from pygost.asn1schemas.oids import id_ce_keyUsage 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 @@ -41,6 +46,7 @@ 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 AuthorityKeyIdentifier from pygost.asn1schemas.x509 import BasicConstraints from pygost.asn1schemas.x509 import Certificate from pygost.asn1schemas.x509 import CertificateSerialNumber @@ -48,6 +54,8 @@ 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 KeyIdentifier +from pygost.asn1schemas.x509 import KeyUsage from pygost.asn1schemas.x509 import Name from pygost.asn1schemas.x509 import RDNSequence from pygost.asn1schemas.x509 import RelativeDistinguishedName @@ -65,6 +73,7 @@ from pygost.gost3410 import public_key from pygost.gost3410 import sign from pygost.gost34112012256 import GOST34112012256 from pygost.gost34112012512 import GOST34112012512 +from pygost.utils import bytes2long parser = ArgumentParser(description="Self-signed X.509 certificate creator") parser.add_argument( @@ -77,6 +86,14 @@ parser.add_argument( required=True, help="Subject's CommonName", ) +parser.add_argument( + "--country", + help="Subject's Country", +) +parser.add_argument( + "--serial", + help="Serial number", +) parser.add_argument( "--ai", required=True, @@ -86,6 +103,23 @@ parser.add_argument( "--issue-with", help="Path to PEM with CA to issue the child", ) +parser.add_argument( + "--reuse-key", + help="Path to PEM with the key to reuse", +) +parser.add_argument( + "--out-key", + help="Path to PEM with the resulting key", +) +parser.add_argument( + "--only-key", + action="store_true", + help="Only generate the key", +) +parser.add_argument( + "--out-cert", + help="Path to PEM with the resulting certificate", +) args = parser.parse_args() AIs = { "256A": { @@ -148,6 +182,7 @@ AIs = { ai = AIs[args.ai] ca_prv = None +ca_cert = None ca_subj = None ca_ai = None if args.issue_with is not None: @@ -163,7 +198,8 @@ if args.issue_with is not None: 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_cert = Certificate().decod(cert_raw) + tbs = ca_cert["tbsCertificate"] ca_subj = tbs["subject"] curve_oid = GostR34102012PublicKeyParameters().decod(bytes( tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"] @@ -173,68 +209,114 @@ if args.issue_with is not None: if params["publicKeyParamSet"] == curve_oid ])) +key_params = GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", ai["publicKeyParamSet"]), +)) + def pem(obj): return fill(standard_b64encode(obj.encode()).decode("ascii"), 64) -key_params = GostR34102012PublicKeyParameters(( - ("publicKeyParamSet", ai["publicKeyParamSet"]), -)) - -prv_raw = urandom(ai["prv_len"]) -print("-----BEGIN PRIVATE KEY-----") -print(pem(PrivateKeyInfo(( - ("version", Integer(0)), - ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier(( - ("algorithm", ai["key_algorithm"]), - ("parameters", Any(key_params)), - ))), - ("privateKey", PrivateKey(OctetString(prv_raw).encode())), -)))) -print("-----END PRIVATE KEY-----") +if args.reuse_key is not None: + with open(args.reuse_key, "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]) + pki = PrivateKeyInfo().decod(prv_raw) + prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"])))) +else: + prv_raw = urandom(ai["prv_len"]) + out = stdout if args.out_key is None else open(args.out_key, "w") + print("-----BEGIN PRIVATE KEY-----", file=out) + print(pem(PrivateKeyInfo(( + ("version", Integer(0)), + ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier(( + ("algorithm", ai["key_algorithm"]), + ("parameters", Any(key_params)), + ))), + ("privateKey", PrivateKey(OctetString(prv_raw).encode())), + ))), file=out) + print("-----END PRIVATE KEY-----", file=out) + if args.only_key: + sys_exit() + prv = prv_unmarshal(prv_raw) -prv = prv_unmarshal(prv_raw) curve = ai["curve"] pub_raw = pub_marshal(public_key(curve, prv)) -subj = Name(("rdnSequence", RDNSequence([ - RelativeDistinguishedName(( +rdn = [RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_commonName)), + ("value", AttributeValue(PrintableString(args.cn))), + )), +))] +if args.country: + rdn.append(RelativeDistinguishedName(( AttributeTypeAndValue(( - ("type", AttributeType(id_at_commonName)), - ("value", AttributeValue(PrintableString(args.cn))), + ("type", AttributeType(id_at_countryName)), + ("value", AttributeValue(PrintableString(args.country))), )), - )) -]))) + ))) +subj = Name(("rdnSequence", RDNSequence(rdn))) not_before = datetime.utcnow() -not_after = not_before + timedelta(days=365) +not_after = not_before + timedelta(days=365 * (10 if args.ca else 1)) ai_sign = AlgorithmIdentifier(( ("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_keyUsage), + ("critical", Boolean(True)), + ("extnValue", OctetString(KeyUsage( + ("keyCertSign" if args.ca else "digitalSignature",), + ).encode())), + )), +] +if args.ca: + exts.append(Extension(( + ("extnID", id_ce_basicConstraints), + ("critical", Boolean(True)), + ("extnValue", OctetString(BasicConstraints(( + ("cA", Boolean(True)), + )).encode())), + ))) +else: + exts.append(Extension(( ("extnID", id_ce_subjectAltName), ("extnValue", OctetString( SubjectAltName(( GeneralName(("dNSName", IA5String(args.cn))), )).encode() )), - )), -] -if args.ca: + ))) +if ca_ai is not None: + caKeyId = [ + bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"]))) + for ext in ca_cert["tbsCertificate"]["extensions"] + if ext["extnID"] == id_ce_subjectKeyIdentifier + ][0] exts.append(Extension(( - ("extnID", id_ce_basicConstraints), - ("extnValue", OctetString(BasicConstraints((("cA", Boolean(True)),)).encode())), + ("extnID", id_ce_authorityKeyIdentifier), + ("extnValue", OctetString(AuthorityKeyIdentifier(( + ("keyIdentifier", KeyIdentifier(caKeyId)), + )).encode())), ))) + +serial = ( + bytes2long(GOST34112012256(urandom(16)).digest()[:20]) + if args.serial is None else int(args.serial) +) tbs = TBSCertificate(( ("version", Version("v3")), - ("serialNumber", CertificateSerialNumber(12345)), + ("serialNumber", CertificateSerialNumber(serial)), ("signature", ai_sign), ("issuer", subj if ca_ai is None else ca_subj), ("validity", Validity(( @@ -260,6 +342,7 @@ cert = Certificate(( sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1]) )), )) -print("-----BEGIN CERTIFICATE-----") -print(pem(cert)) -print("-----END CERTIFICATE-----") +out = stdout if args.out_cert is None else open(args.out_cert, "w") +print("-----BEGIN CERTIFICATE-----", file=out) +print(pem(cert), file=out) +print("-----END CERTIFICATE-----", file=out)