Python Acme V2 - 重用订单/挑战

标签 python ssl lets-encrypt acme

我正在使用 python-acme 编写一个小脚本,它接受一个域并执行以下两项操作之一:

  • 使用 DNS01 质询,如果质询未通过,则返回需要添加到域的 DNS 条目。
  • 使用DNS01挑战,如果挑战通过,返回证书信息。

但是我遇到了问题,而且文档非常糟糕 :P 我正在尝试找到一种“重用”订单/挑战的方法。基本上,到目前为止,每当我运行脚本时,DNS 条目的验证 token 都会发生变化。

    print("Validating Challenge...")
    response, validation = challenge.response_and_validation(client_acme.net.key)
    print("response %s" % response.to_partial_json())
    print("validation %s" % validation)
    print("-> Validation Domain: %s" % challenge.chall.validation_domain_name(domain))
    print("-> Validation Value: %s" % challenge.chall.validation(client_acme.net.key))
    # TODO - We are here, gotta actually get info on the DNS challange and attempt to validate.
    print("Validation Completed!")

我尝试过的事情:

  • 使用相同的 RSA key 注册帐户。注册时遇到验证错误。
  • 使用相同的 CSR 运行 new_order。仍然返回不同的 key 。

完整代码(在它的原型(prototype)荣耀中)

import json
import josepy as jose
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from acme import client
from acme import messages
from acme import challenges
from acme import crypto_util
import OpenSSL

from boto.s3.connection import S3Connection
from boto.s3.key import Key

"""
Generates an SSL certificate through Let's Encrypt provided a domain. If the 
domain requires a DNS challenge to be passed, that information is passed back
to the user. Otherwise, the SSL certificate is generated and returned back to
the user.

Documentation for generating SSL via this method:
http://www.gilesthomas.com/2018/11/python-code-to-generate-lets-encrypt-certificates/

ACME Documentation:
https://kite.com/python/docs/acme.client.ClientV2.new_order
"""

print('Loading function')

DEBUG = True

DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'
KEY_SIZE = 2048
CERT_PKEY_BITS = 2048
USER_AGENT = 'python-acme-example'
EMAIL_ADDRESS = 'REDACTED'

S3_BUCKET = 'REDACTED'
S3_KEY = 'REDACTED'
S3_SECRET = 'REDACTED'


# TODO - Load These From Event
PASSWORD = "swordfish" 
SALT = "yourAppName"  


def new_csr_comp(domain_name, pkey_pem=None):
    """Create certificate signing request."""
    if pkey_pem is None:
        # Create private key.
        pkey = OpenSSL.crypto.PKey()
        pkey.generate_key(OpenSSL.crypto.TYPE_RSA, CERT_PKEY_BITS)
        pkey_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM,
                                                  pkey)
    csr_pem = crypto_util.make_csr(pkey_pem, [domain_name])
    return pkey_pem, csr_pem

def save_key(pk, filename):
    pem = pk.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()
    )
    return pem

def lambda_handler(event, context):
    print("Formatting Domain...")
    domain = event['domain']
    domain = domain.lower()
    print("Formatted! Domain is: %s" % domain)

    print("Generating User Key...")
    conn = S3Connection(S3_KEY, S3_SECRET)
    bucket = conn.get_bucket(S3_BUCKET)
    key_name = "%s.key" % domain
    existing_account = False
    print("-> Looking For Existing Key.")
    rsa_key = bucket.get_key(key_name)
    if rsa_key is None or DEBUG:
        print("-> Key Not Found. Creating New One.")
        rsa_key = rsa.generate_private_key(
                public_exponent=65537,
                key_size=KEY_SIZE,
                backend=default_backend()
            )
        pem = save_key(rsa_key, key_name)
        print(key_name)
        k = Key(bucket)
        k.key = key_name
        k.set_contents_from_string(pem)
    else:
        print("-> Key File Found.")
        existing_account = True
        rsa_key = rsa_key.get_contents_as_string()
        rsa_key = load_pem_private_key(rsa_key, password=None, backend=default_backend())
        print(rsa_key)
        print("-> Converted File To Usable Format.")
    acc_key = jose.JWKRSA(
            key=rsa_key
        )
    print("Generated!")

    print("Registering With Let's Encrypt...")
    print("-> Connecting to Let's Encrypt on {}".format(DIRECTORY_URL))
    net = client.ClientNetwork(acc_key, user_agent=USER_AGENT)
    directory = messages.Directory.from_json(net.get(DIRECTORY_URL).json())
    client_acme = client.ClientV2(directory, net=net)
    print("-> Registering")
    email = (EMAIL_ADDRESS)
    regr = None 
    # TODO - Use Existing Account
    # if existing_account:
    #     regr = messages.NewRegistration(key=acc_key.public_key(), only_return_existing=True)
    # else:
    account_created = messages.NewRegistration.from_data(email=email, terms_of_service_agreed=True)
    regr = client_acme.new_account(account_created)
    print("Registered!")

    print("Creating CSR...")
    temp_pkey_pem, temp_csr_pem = new_csr_comp(domain)
    key_name = "%s.pkey_pem" % domain
    pkey_pem = bucket.get_key(key_name)
    if pkey_pem is None:
        print("-> Creating New PKEY")
        k = Key(bucket)
        k.key = key_name
        k.set_contents_from_string(temp_pkey_pem)
        pkey_pem = temp_pkey_pem
    else:
        print("-> Using Existing PKEY")
        pkey_pem = pkey_pem.get_contents_as_string()
    key_name = "%s.csr_pem" % domain
    csr_pem = bucket.get_key(key_name)
    if csr_pem is None:
        print("-> Creating New CSR")
        k = Key(bucket)
        k.key = key_name
        k.set_contents_from_string(temp_csr_pem)
        csr_pem = temp_csr_pem
    else:
        print("-> Using Existing CSR")
        csr_pem = csr_pem.get_contents_as_string()
    print("Created!")

    print("Requesting Challenges...")
    orderr = client_acme.new_order(csr_pem)
    print("Requested!")

    print("Selecting DNS Challenge...")
    challenge = None
    authz_list = orderr.authorizations
    for authz in authz_list:
        for i in authz.body.challenges:
            if isinstance(i.chall, challenges.DNS01):
                challenge = i
            else:
                print("-> Other challenge found: %s" % i.chall)
    if challenge is None:
        raise Exception("Could not find a DNS challenge!")
    print("Selected!")

    print("Validating Challenge...")
    response, validation = challenge.response_and_validation(client_acme.net.key)
    print("response %s" % response.to_partial_json())
    print("validation %s" % validation)
    print("-> Validation Domain: %s" % challenge.chall.validation_domain_name(domain))
    print("-> Validation Value: %s" % challenge.chall.validation(client_acme.net.key))
    # TODO - We are here, gotta actually get info on the DNS challange and attempt to validate.
    print("Validation Completed!")

    print("Starting Challenge...")
    client_acme.answer_challenge(challenge, response)
    finalized_orderr = client_acme.poll_and_finalize(orderr)
    fullchain_pem = finalized_orderr.fullchain_pem
    print("-> PEM: %s" % fullchain_pem)
    print("Challenge Completed!")

    # TODO - We need to return the DNS challenge if it hasn't been completed yet.

    return "done"
    #raise Exception('Something went wrong')

最佳答案

科比

print("Starting Challenge...")
client_acme.answer_challenge(challenge, response)
finalized_orderr = client_acme.poll_and_finalize(orderr)
fullchain_pem = finalized_orderr.fullchain_pem
print("-> PEM: %s" % fullchain_pem)
print("Challenge Completed!")`enter code here`

每次运行此代码块时,它都会向 LetsEncrypt Server 发送一个挑战。如果挑战成功,它将返回您的证书并且一切正常,但如果挑战无效,LetsEncrypt 服务器将更改您的 key 授权和 TXT 值。

希望,它的帮助。

关于Python Acme V2 - 重用订单/挑战,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58506975/

相关文章:

python - 检查模型输入时出错 : the list of Numpy arrays that you are passing to your model is not the size the model expected

http - HTTPS 与 HTTP 请求有何不同?

python - curl 到 python 请求错误 : "no api key supplied"

python - np.array ndmin 参数 : specify placement of added dimensions

java - 如何将新的 JVM 附加到生成的 Python 进程?

java - 为什么 IE 仅拒绝 127.0.0.1 的自签名本地主机证书,而 Chrome 接受它?

java - 带有客户端证书的 https 请求在 android 中返回访问被拒绝,但在 post man 中效果很好,retrofit 和 okhttp3 返回相同的错误

ssl - 无法在 cpanel 中安装证书

Node.js Nginx LetsEncrypt 坏网关