I enconter this issue when creating my account with acme api
My domain is:
I'm creating an account and I haven't add a domain.
I ran this command:
import base64
import json
import requests
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
# Function to base64url encode data
def base64url_encode(data):
return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')
# Function to get a new nonce from Let's Encrypt
def get_nonce(new_nonce_url):
response = requests.head(new_nonce_url)
return response.headers['Replay-Nonce']
# Generate a new ECDSA key pair
private_key = ec.generate_private_key(ec.SECP256R1())
# Let's Encrypt API URLs
new_account_url = "https://acme-v02.api.letsencrypt.org/acme/new-acct"
new_nonce_url = "https://acme-v02.api.letsencrypt.org/acme/new-nonce"
# Get a new nonce from Let's Encrypt
nonce = get_nonce(new_nonce_url)
# Construct protected header
protected_header = {
"alg": "ES256",
"nonce": nonce,
"url": new_account_url,
}
# Serialize the public key to DER format
public_key_der = private_key.public_key().public_bytes(
encoding=Encoding.DER,
format=PublicFormat.SubjectPublicKeyInfo
)
# Extract x and y coordinates from the DER encoded public key
x_coordinate = public_key_der[27:59] # Adjust indices as per the key format
y_coordinate = public_key_der[59:] # Adjust indices as per the key format
# Base64url encode the coordinates
x_base64 = base64.urlsafe_b64encode(x_coordinate).rstrip(b'=').decode('utf-8')
y_base64 = base64.urlsafe_b64encode(y_coordinate).rstrip(b'=').decode('utf-8')
# Construct the JWK (JSON Web Key)
jwk = {
"kty": "EC",
"crv": "P-256", # Adjust as per the curve used (e.g., P-384 for SECP384R1)
"x": x_base64,
"y": y_base64
}
# Add the JWK to the protected header
protected_header["jwk"] = jwk
# Base64url encode the protected header
protected_header_base64 = base64url_encode(json.dumps(protected_header).encode('utf-8'))
# Construct payload
payload = {
"termsOfServiceAgreed": True,
"contact": ["mailto:my email"], # Replace with your email address
}
payload_base64 = base64url_encode(json.dumps(payload).encode('utf-8'))
# Combine protected header and payload
message = f"{protected_header_base64}.{payload_base64}".encode('utf-8')
# Sign the message
signature = private_key.sign(
message,
ec.ECDSA(hashes.SHA256())
)
# Encode the signature in DER format
r, s = utils.decode_dss_signature(signature)
signature_der = utils.encode_dss_signature(r, s)
signature_base64 = base64url_encode(signature_der)
# Construct the final JWS (JSON Web Signature) payload
jws_payload = {
"protected": protected_header_base64,
"payload": payload_base64,
"signature": signature_base64
}
# Send the registration request to Let's Encrypt
headers = {
"Content-Type": "application/jose+json"
}
response = requests.post(new_account_url, headers=headers, json=jws_payload)
# Print the response from Let's Encrypt
print(response.json())
It produced this output:
{'type': 'urn:ietf:params:acme:error:malformed', 'detail': 'JWS verification error', 'status': 400}
My web server is (include version):
I ran this locally with python 3.12.1
The operating system my web server runs on is (include version):
Microsoft Windows 10.0.19045.4529
My hosting provider, if applicable, is:
I'm running this locally
I can login to a root shell on my machine (yes or no, or I don't know):
Yes, but I'm not going to install my ssl cert on my computer, I don't have root access on my remote server.
I'm using a control panel to manage my site (no, or provide the name and version of the control panel):
NO
The version of my client is (e.g. output of certbot --version
or certbot-auto --version
if you're using Certbot):
python 3.12.1