[PYTHON] Encrypt ACME on Alibaba Cloud: Build an ACME request and sign the JWS payload

In this multi-part article, you'll learn how to use the Let's Encrypt ACME version 2 API with ** Python ** for ** SSL certificates **.

ACME request example

Let's look at an example of creating a new account. This pseudo-code example shows an HTTP POST, HTTP header, and HTTP body. "based64_encode" is not really part of the HTTP body, but it does indicate that the code needs to base64 encode the data before sending it to the ACME server.

POST /acme/new-account HTTP/1.1
Host: acme-staging-v02.api.letsencrypt.org
Content-Type: application/jose+json

{
    "protected": base64_encode({
        "alg": "ES256",
        "jwk": {...},
        "nonce": "6S8IqOGY7eL2lsGoTZYifg",
        "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-acct"
    }),
    "payload": base64_encode({
        "termsOfServiceAgreed": true,
        "contact": [
            "mailto:[email protected]",
            "mailto:[email protected]"
        ]
    }),
    "signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}

ACME API HTTP request body

ACME requests are encapsulated in JSON Web Signature (JWS) objects.

The JWS object consists of three parts. JWS Protected Header, API command parameters (payload), signature. Each part is explained below. Each part is individually base64 encoded and combined into a single JSON object.

{
    "protected": base64_encode(jws_protected_header),
    "payload": base64_encode(payload),
    "signature": based64_encode(signature)
}

JWS Protected Header

Inside the JWS object is the JWS Protected Header. The JWS Protected Header contains the following fields:

field Description
alg algorithm. A MAC-based algorithm used to sign requests. The supported algorithms are ES256 and RS256. Reference See RFC 7518. In these examples, RS256(SH-RSASSA with 256-PKCS1-v1_5)Use the. I will explain briefly. RS256 means signing with the RSA private key and validating with the corresponding RSA public key.
jwk JSON Web Key. jwk is used for all requests that are not signed using an existing account. For example:"New account".. jwk is a JSON object described later in this article.
kid Key ID. kid will be used for all requests signed using an existing account. For example, "Get account information".
nonce Nonce. A unique value used to prevent replay attacks. This value is returned by the NewNonce API and the response header "Replay" after each successful ACME API call.-It will be returned with "Nonce".
url URL. This header parameter encodes the URL that the client is pointing to the request for. Please refer to each ACME API for the required values.

The "jwk" and "kid" fields are mutually exclusive. The server must reject the request containing both.

The ACME API uses one or the other and specifies it.

JWS Web Key (JWK)

JWK parameters vary depending on the type of cryptographic signature. The examples in this series use RSA key pairs.

field Description
e Public index. This is the public index of RSA key pairs.
kty Key Type. The method used to sign the JWS. If you use an RSA key pair, it will be RSA. See RFC 7638 for details.
n Modulus. Modulus from an RSA key pair. For a 2048-bit key, the value of field "n" will be 256 octets long when decoded.
{
    "e": base64_encode(public_exponent),
    "kty": "RSA",
    "n": base64_encode(modulus),
}

payload

The payload contains API call parameters. These are different for each API. The contents of the payload json object are base64 encoded. The following example shows the payload of the New Account API. There are two parameters. "termsOfServiceAgreed" and "contact". The payload is part of the JSON Web Signature (JWS) contained in the HTTP body.

"payload": base64_encode({
    "termsOfServiceAgreed": true,
    "contact": [
        "mailto:[email protected]",
        "mailto:[email protected]"
    ]
})

signature

The signature is a SHA-256 message digest with an RSA private key. The ACME server uses the corresponding public key to verify the signature.

def sign(data, keyfile):
    """ Create the ACME API Signature """

    # Load the RSA Private Key from the file (RSA PKCS #1)
    pkey = load_private_key(keyfile)

    # Create the signature
    sig = crypto.sign(pkey, data, "sha256")

    return sig

Overview

So far, we have shown how the ACME API system works.

In the final part of the next, we'll look at how to perform DNS validation and how to create and modify DNS server resource records to support ACME DNS validation.

Recommended Posts

Encrypt ACME on Alibaba Cloud: Build an ACME request and sign the JWS payload
Encrypt ACME on Alibaba Cloud: Create account key, certificate key, certificate signing request
Encrypt ACME on Alibaba Cloud: Create ACME endpoints, directories, ACME accounts
Encrypt ACME on Alibaba Cloud: Concepts Related to SSL Certificates
Build a game leaderboard on Alibaba cloud using Python and Redis
[CentOS 7.3] Build an FTP server on the ESXi host
Run the flask app on Cloud9 and Apache Httpd
Build an Ubuntu python development environment on Google Cloud Platform