前言

我常用的网站要手机验证了。

代码

这里使用buypass的证书,最多五个子域名,这个脚本使用DNS验证。

import json
import datetime

from acme import messages
from acme import challenges
from acme import client
from acme import messages

import josepy as jose

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key

def new_csr(domain):
    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
    )
    with open(domain[0] + ".key", "wb") as f:
        f.write(key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        ))
    csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, domain[0]),
    ])).add_extension(
        x509.SubjectAlternativeName([x509.DNSName(i) for i in domain]),
        critical=False,
    ).sign(key, hashes.SHA256())
    return csr.public_bytes(serialization.Encoding.PEM)

def main():
    domain = ["your_domain_list"]
    # 生成csr
    csr = new_csr(domain)
    # 加载用户秘钥
    # RSA 2048bits-4096bits
    with open("./private.key", "r") as f:
        key = load_pem_private_key(f.read().encode("utf-8"), password=None)
    # 获取jwk
    acc_key = jose.JWKRSA(key=key)
    net = client.ClientNetwork(acc_key, user_agent="acm/0.0.1")
    # 本地测试用
    # net.verify_ssl = False
    # 取得acme目录
    directory = messages.Directory.from_json(net.get("https://api.buypass.com/acme/directory").json())
    # directory = messages.Directory.from_json(net.get("https://acme-v02.api.letsencrypt.org/directory").json())

    client_acme = client.ClientV2(directory, net=net)

    # 加载账户信息
    # with open("./account.json", "r") as f:
    #     user = messages.RegistrationResource.json_loads(f.read())
    # client_acme.query_registration(user)

    # 首次注册
    regr = client_acme.new_account(
        messages.NewRegistration.from_data(
            email=("[email protected]"),
            terms_of_service_agreed=True,
        )
    )

    # 保存账户信息
    with open("./account.json", "w") as f:
        f.write(json.dumps(regr.to_json()))

    # 新建订单
    order = client_acme.new_order(csr)
    challenges_list = []
    for authz in order.authorizations:
        for i in authz.body.challenges:
            # 选取DNS验证
            if isinstance(i.chall, challenges.DNS01):
                response, validation = i.response_and_validation(client_acme.net.key)
                challenges_list.append([i, response])
                print("[DNS]", "_acme-challenge." + authz.body.identifier.value)
                print("[DNS]", validation)

    while input("ok? (y) ") != "y":
        pass

    # 通知准备验证
    for i in challenges_list:
        client_acme.answer_challenge(i[0], i[1])
    print("[INFO]", "wait...")

    # 自动轮询2分钟并取回证书
    finalized_orderr = client_acme.poll_and_finalize(order, deadline=datetime.datetime.now() + datetime.timedelta(minutes=2))
    print("[OUT]", finalized_orderr.fullchain_pem)
    with open(domain[0] + ".crt", "w") as f:
        f.write(finalized_orderr.fullchain_pem)   
    pass

if __name__ == "__main__":
    main()

JSON Web Key

使用JSON的结构来承载公钥。(RFC7517)

import json
import base64
import binascii
from cryptography.hazmat.primitives.serialization import load_pem_private_key

def b64(text):
    return base64.urlsafe_b64encode(text).decode('utf8').replace("=", "")

def get_jwk(path):
    with open(path, "r") as f:
        pri = load_pem_private_key(f.read().encode("utf-8"), password=None)
    pub = pri.public_key().public_numbers()
    e = "{:x}".format(pub.e)
    e = "0{0}".format(e) if int(e) % 2 else e
    e = b64(binascii.unhexlify(e))
    n = b64(binascii.unhexlify(hex(pub.n)[2:]))
    return {"n": n, "e": e, "kty": "RSA"}

if __name__ == "__main__":
    print(get_jwk("./private.key"))

使用了RSA的模数与指数来代表一份公钥,如果是椭圆则是基点坐标。

标签: Python

添加新评论