代码之家  ›  专栏  ›  技术社区  ›  Joenarr Bronarsson

Python加密:创建一个由现有CA签名的证书,然后导出

  •  2
  • Joenarr Bronarsson  · 技术社区  · 5 年前

    我正在创建一个CA,如下所示:

    openssl genrsa -out ca.key 4096
    openssl req -new -x509 -days 3650 -key ca.key -out ca.cert
    

    然后我调用这个函数 cert_authority private_key

    def create_cert(cert_authority, private_key):
        one_day = datetime.timedelta(1, 0, 0)
        # Use our private key to generate a public key
        private_key = serialization.load_pem_private_key(
            private_key.encode("ascii"), password=None, backend=default_backend()
        )
        public_key = private_key.public_key()
    
        ca = x509.load_pem_x509_certificate(
            cert_authority.encode("ascii"), default_backend()
        )
    
        builder = x509.CertificateBuilder()
        builder = builder.subject_name(
            x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io")])
        )
        builder = builder.issuer_name(ca.issuer)
        builder = builder.not_valid_before(datetime.datetime.today() - one_day)
        builder = builder.not_valid_after(datetime.datetime.today() + (one_day * 30))
        builder = builder.serial_number(x509.random_serial_number())
        builder = builder.public_key(public_key)
    
        cert = builder.sign(
            private_key=private_key, algorithm=hashes.SHA256(), backend=default_backend()
        )
    
        print(cert.public_bytes(serialization.Encoding.PEM))
    

    http://srdevspot.blogspot.com/2011/08/openssl-error0906d064pem.html

    $ openssl verify -CAfile ca.crt -untrusted phone.crt
    unable to load certificates
    

    希望我错过了一些简单的东西,因为我对这一切都是新的!

    最后,我会注意到,如果加密不是最好的,我愿意使用另一个加密库。

    现在用保罗非常有用的回答:

    def create_cert(cert_authority, private_key):
        one_day = datetime.timedelta(1, 0, 0)
        # Use our private key to generate a public key
        root_key = serialization.load_pem_private_key(
            private_key.encode("ascii"), password=None, backend=default_backend()
        )
    
        root_cert = x509.load_pem_x509_certificate(
            cert_authority.encode("ascii"), default_backend()
        )
    
        # Now we want to generate a cert from that root
        cert_key = rsa.generate_private_key(
            public_exponent=65537, key_size=2048, backend=default_backend()
        )
        new_subject = x509.Name(
            [
                x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
                x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Texas"),
                x509.NameAttribute(NameOID.LOCALITY_NAME, u"Austin"),
                x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"New Org Name!"),
            ]
        )
        cert = (
            x509.CertificateBuilder()
            .subject_name(new_subject)
            .issuer_name(root_cert.issuer)
            .public_key(cert_key.public_key())
            .serial_number(x509.random_serial_number())
            .not_valid_before(datetime.datetime.utcnow())
            .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=30))
            .add_extension(
                x509.SubjectAlternativeName([x509.DNSName(u"somedomain.com")]),
                critical=False,
            )
            .sign(root_key, hashes.SHA256(), default_backend())
        )
    
        # Dump to scratch
        with open("scratch/phone_cert.pem", "wb") as f:
            f.write(cert.public_bytes(encoding=serialization.Encoding.PEM))
    
        # Return PEM
        cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM)
    
        cert_key_pem = cert_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        )
    
        return cert_pem, cert_key_pem
    

    这是保存文件并将创建的证书和私钥作为PEM字符串返回的正确方法吗?

    我还发现,当我尝试用 openssl verify -verbose -CAfile ca.crt -untrusted phone_cert.pem 命令永远不会回来——可能是一个单独的问题,但会感谢任何想法。

    0 回复  |  直到 5 年前
        1
  •  11
  •   Paul Kehrer    5 年前

    我在这里看到两个问题。首先,您正在创建另一个自签名证书,因此您生成的证书不是由CA签名的,它本身就是一个CA 使用CA的私钥(例如。 private_key 但您需要创建一个 新的 与新证书关联的私钥,并将该证书的公钥嵌入证书中。

    certificate_private_key = <generate an ec or rsa key here>
    certificate_public_key = certificate_private_key.public_key()
    

    那就做吧

    builder = builder.public_key(certificate_public_key)
    

    您的输出也有问题,因为您试图从print语句中复制和粘贴内容。输出 cert.public_bytes(serialization.Encoding.PEM) 将是具有分隔符和正确PEM行长度的有效X509证书,因此请直接将其写入文件:

    with open("cert.crt", "wb") as f:
        f.write(cert.public_bytes(serialization.Encoding.PEM))
    

    openssl x509 -noout -text -in cert.crt

    下面是一个完整的例子 cryptography 创建自签名根CA并使用该CA对证书进行签名。

    import datetime
    
    from cryptography import x509
    from cryptography.x509.oid import NameOID
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric import rsa
    
    
    root_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Texas"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, u"Austin"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Company"),
        x509.NameAttribute(NameOID.COMMON_NAME, u"My CA"),
    ])
    root_cert = x509.CertificateBuilder().subject_name(
        subject
    ).issuer_name(
        issuer
    ).public_key(
        root_key.public_key()
    ).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        datetime.datetime.utcnow()
    ).not_valid_after(
        datetime.datetime.utcnow() + datetime.timedelta(days=3650)
    ).sign(root_key, hashes.SHA256(), default_backend())
    
    # Now we want to generate a cert from that root
    cert_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
        backend=default_backend()
    )
    new_subject = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Texas"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, u"Austin"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"New Org Name!"),
    ])
    cert = x509.CertificateBuilder().subject_name(
        new_subject
    ).issuer_name(
        root_cert.issuer
    ).public_key(
        cert_key.public_key()
    ).serial_number(
        x509.random_serial_number()
    ).not_valid_before(
        datetime.datetime.utcnow()
    ).not_valid_after(
    datetime.datetime.utcnow() + datetime.timedelta(days=30)
    ).add_extension(
        x509.SubjectAlternativeName([x509.DNSName(u"somedomain.com")]),
        critical=False,
    ).sign(root_key, hashes.SHA256(), default_backend())
    
        2
  •  4
  •   Taylor Graham    4 年前

    因为我是新来的,还不能发表评论,所以我必须发布一个答案

    我在很大程度上依赖于paul的答案来实现我自己的实现,这是非常有用的信息。但是我必须在CA证书上再添加一个扩展才能获得 openssl verify -verbose -CAfile ca.crt client.crt

    添加 .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) 根证书生成器完成了这个技巧。

    ca_crt = x509.CertificateBuilder() \
        .subject_name(subject) \
        .issuer_name(issuer) \
        .public_key(ca_key.public_key()) \
        .serial_number(x509.random_serial_number()) \
        .not_valid_before(datetime.datetime.today() - one_day) \
        .not_valid_after(datetime.datetime.today() + (one_day * 365)) \
        .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True) \
        .sign(ca_key, hashes.SHA256(), default_backend())
    

    其他一切都像保罗一样。