Python3使用acme.client.ClientV2.answer_challenge 使用DNS验证
时间: 2024-05-09 18:16:37 浏览: 251
在 Python3 中使用 ACME 协议实现 DNS 验证的步骤如下:
1. 安装 `acme` 和 `requests` 模块:
```bash
pip install acme requests
```
2. 导入相关模块:
```python
import os
import json
import requests
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from acme import client
from acme import messages
from acme.client import ClientV2
from acme.errors import AcmeError
from acme.jose import jwk
from acme.jose import jose_base64url_decode
```
3. 设置 ACME 服务器的 URL 和 API 密钥:
```python
ACME_SERVER = 'https://acme-v02.api.letsencrypt.org/directory'
API_KEY = '<your_api_key>'
```
4. 生成 ACME 账户的 RSA 密钥:
```python
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
)
```
5. 创建 ACME 客户端实例:
```python
directory_url = ACME_SERVER
client = ClientV2(directory_url, key=private_key)
```
6. 注册 ACME 账户:
```python
email = '<your_email_address>'
registration = client.new_account(
messages.NewRegistration.from_data(email=email),
accept_terms_of_service=True,
)
```
7. 创建需要验证的域名列表:
```python
domain_list = ['example.com', 'www.example.com']
```
8. 对每个域名创建 ACME 订单:
```python
order_list = []
for domain in domain_list:
identifier = messages.Identifier(
typ=messages.IDENTIFIER_FQDN, value=domain)
order = client.new_order(identifier)
order_list.append(order)
```
9. 针对每个域名,获取 ACME 服务器返回的 DNS 验证信息:
```python
for order in order_list:
authorizations = client.fetch_authorizations(order)
for authz in authorizations:
if authz.body.identifier.typ == messages.IDENTIFIER_FQDN:
domain = authz.body.identifier.value
dns_challenge = None
for i in authz.body.challenges:
if isinstance(i.chall, messages.DNS01):
dns_challenge = i
break
if dns_challenge is None:
raise ValueError("No DNS challenge found for domain")
token = jose_base64url_decode(dns_challenge.chall.token)
key = jwk.JWK.load(private_key.public_key())
thumbprint = key.thumbprint()
dns_value = thumbprint + '.' + token.decode()
dns_record_name = '_acme-challenge.' + domain
dns_record_value = dns_value.decode()
print(f"Please add the following DNS record to your domain:")
print(f"- Name:\t{dns_record_name}")
print(f"- Type:\tTXT")
print(f"- Value:\t{dns_record_value}")
```
10. 在 DNS 解析器中添加 TXT 记录,等待 DNS 记录生效:
```bash
_acme-challenge.example.com. 3600 IN TXT "thumbprint.token"
_acme-challenge.www.example.com. 3600 IN TXT "thumbprint.token"
```
11. 等待 DNS 记录生效后,调用 `answer_challenge` 方法完成 DNS 验证:
```python
for order in order_list:
authorizations = client.fetch_authorizations(order)
for authz in authorizations:
if authz.body.identifier.typ == messages.IDENTIFIER_FQDN:
domain = authz.body.identifier.value
dns_challenge = None
for i in authz.body.challenges:
if isinstance(i.chall, messages.DNS01):
dns_challenge = i
break
if dns_challenge is None:
raise ValueError("No DNS challenge found for domain")
response, validation = client.answer_challenge(dns_challenge, "dns-01")
print(f"Domain {domain} validated!")
```
12. 如果验证成功,ACME 服务器将会返回证书签名请求(CSR)和证书链:
```python
for order in order_list:
certificate_request, final_order = client.finalize_order(
order, csr_pem=None)
certificate_url = final_order.body.certificate
certificate_pem = client.fetch_certificate(certificate_url).body
```
13. 将证书和私钥保存到文件中:
```python
with open('cert.pem', 'wb') as f:
f.write(certificate_pem)
with open('key.pem', 'wb') as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
))
```
阅读全文