Introduction to ACME
The Automated Certificate Management Environment (ACME) protocol is an IETF standard (RFC 8555) designed to automate the process of certificate issuance and domain validation. Developed by the Internet Security Research Group (ISRG) for Let’s Encrypt, ACME enables server administrators to obtain and renew SSL/TLS certificates automatically without manual intervention, significantly simplifying the deployment of HTTPS websites.
Key Benefits:
- Complete automation of certificate issuance and renewal
- Standardized, vendor-neutral approach to certificate management
- Reduction in human error during certificate operations
- Elimination of manual approval delays
- Support for wildcard certificates
- Free implementation via Let’s Encrypt and other Certificate Authorities (CAs)
Core Concepts
ACME Workflow Overview
| Stage | Description |
|---|---|
| Account Creation | Client creates an account with the ACME server |
| Order Creation | Client requests certificate for specific domains |
| Domain Validation | Client proves control over the requested domains |
| Certificate Issuance | After validation, server issues the certificate |
| Certificate Renewal | Process repeats before certificate expiration |
Key Components
| Component | Description |
|---|---|
| ACME Client | Software that communicates with the ACME server (e.g., Certbot, acme.sh) |
| ACME Server | Service operated by a CA that issues certificates (e.g., Let’s Encrypt) |
| Account Key | RSA or ECDSA key pair used to identify and authenticate the client |
| Certificate Key | Key pair for which the certificate is issued |
| Challenges | Tests that verify domain control |
| Nonce | One-time values preventing replay attacks |
| JWS | JSON Web Signatures used to sign requests |
Challenge Types
| Challenge | Description | Usage |
|---|---|---|
| HTTP-01 | Places a file at /.well-known/acme-challenge/{token} | Standard validation for web servers |
| DNS-01 | Creates a TXT record at _acme-challenge.domain.com | Required for wildcard certificates |
| TLS-ALPN-01 | Uses TLS Application-Layer Protocol Negotiation | Alternative for servers behind firewalls |
ACME Protocol Flow
1. Account Registration
Client Server
| |
| POST /newAccount |
| JWS({"termsOfServiceAgreed": true, |
| "contact": ["mailto:admin@example.com"]}) |
|---------------------------------------------------->|
| |
| HTTP 201 Created |
| {"status": "valid", |
| "orders": "https://example.com/acme/orders/123"} |
|<----------------------------------------------------|
2. Order Creation
Client Server
| |
| POST /newOrder |
| JWS({"identifiers": [{"type": "dns", |
| "value": "example.com"}]}) |
|---------------------------------------------------->|
| |
| HTTP 201 Created |
| {"status": "pending", |
| "authorizations": ["https://example.com/acme/authz/123"], |
| "finalize": "https://example.com/acme/finalize/123"} |
|<----------------------------------------------------|
3. Domain Authorization
Client Server
| |
| POST /authz/123 |
| JWS({}) |
|---------------------------------------------------->|
| |
| HTTP 200 OK |
| {"status": "pending", |
| "challenges": [{"type": "http-01", |
| "url": "https://example.com/acme/chall/123", |
| "token": "abc123"}]} |
|<----------------------------------------------------|
4. Challenge Response
Client Server
| |
| (Client prepares challenge response) |
| |
| POST /chall/123 |
| JWS({}) |
|---------------------------------------------------->|
| |
| HTTP 200 OK |
| {"status": "processing"} |
|<----------------------------------------------------|
| |
| (Server verifies the challenge) |
| |
| HTTP 200 OK |
| {"status": "valid"} |
|<----------------------------------------------------|
5. Certificate Issuance
Client Server
| |
| POST /finalize/123 |
| JWS({"csr": "base64url(CSR)"}) |
|---------------------------------------------------->|
| |
| HTTP 200 OK |
| {"status": "processing"} |
|<----------------------------------------------------|
| |
| (Server processes the CSR) |
| |
| HTTP 200 OK |
| {"status": "valid", |
| "certificate": "https://example.com/acme/cert/123"} |
|<----------------------------------------------------|
| |
| GET /cert/123 |
|---------------------------------------------------->|
| |
| HTTP 200 OK |
| (PEM-encoded certificate chain) |
|<----------------------------------------------------|
Implementation Guide
Setting Up ACME Clients
Certbot (Official Let’s Encrypt Client)
Installation:
Ubuntu/Debian:
sudo apt update
sudo apt install certbot
CentOS/RHEL:
sudo yum install epel-release
sudo yum install certbot
Basic Usage:
Issue certificate (Apache):
sudo certbot --apache -d example.com -d www.example.com
Issue certificate (Nginx):
sudo certbot --nginx -d example.com -d www.example.com
Standalone mode:
sudo certbot certonly --standalone -d example.com
DNS challenge for wildcard:
sudo certbot certonly --manual --preferred-challenges=dns -d *.example.com -d example.com
Renew all certificates:
sudo certbot renew
acme.sh (Shell Script Implementation)
Installation:
curl https://get.acme.sh | sh
Basic Usage:
Issue certificate (Webroot):
acme.sh --issue -d example.com -w /var/www/html
Issue with DNS API (Cloudflare example):
export CF_Key="cloudflare-api-key"
export CF_Email="cloudflare-email"
acme.sh --issue -d example.com -d *.example.com --dns dns_cf
Install certificate:
acme.sh --install-cert -d example.com \
--key-file /path/to/keyfile \
--fullchain-file /path/to/fullchain \
--reloadcmd "service nginx reload"
Renew all certificates:
acme.sh --renew-all
Common Challenge Methods
HTTP-01 Challenge
- Server provides a token and key authorization
- Client places file at:
http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN> - Content of file:
<TOKEN>.<THUMBPRINT> - ACME server validates by HTTP GET request to that URL
Example implementation:
# Certbot handles this automatically, but manual example:
mkdir -p /var/www/html/.well-known/acme-challenge/
echo "abc123.base64url(sha256(accountKey))" > /var/www/html/.well-known/acme-challenge/abc123
DNS-01 Challenge
- Server provides a token and key authorization
- Client creates TXT record:
_acme-challenge.<YOUR_DOMAIN> - Record value:
base64url(sha256(keyAuthorization)) - ACME server validates by DNS lookup
Example implementation:
# Add the following TXT record
# Name: _acme-challenge.example.com
# Value: base64url(sha256(tokenValue.thumbprint))
TLS-ALPN-01 Challenge
- Server provides a token and key authorization
- Client configures TLS server to use ALPN extension “acme-tls/1”
- Client presents self-signed certificate with special extension
- ACME server validates by TLS handshake
Advanced Techniques
Wildcard Certificates
Wildcard certificates require the DNS-01 challenge:
Certbot example:
sudo certbot certonly --manual --preferred-challenges=dns \
-d *.example.com -d example.com
acme.sh with DNS API:
acme.sh --issue -d example.com -d *.example.com --dns dns_cf
Certificate Revocation
Certbot:
sudo certbot revoke --cert-path /etc/letsencrypt/live/example.com/cert.pem
acme.sh:
acme.sh --revoke -d example.com
acme.sh --remove -d example.com # Remove after revocation
Rate Limits (Let’s Encrypt Specific)
| Limit Type | Value | Time Window |
|---|---|---|
| Certificates per registered domain | 50 | Per week |
| Duplicate certificates | 5 | Per week |
| Failed validations | 5 | Per hour |
| Accounts per IP address | 10 | Per 3 hours |
| New orders | 300 | Per 3 hours |
Certificate Deployment
Apache:
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /path/to/fullchain.pem
SSLCertificateKeyFile /path/to/privkey.pem
# Other directives...
</VirtualHost>
Nginx:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
# Other directives...
}
Integration with Web Services
Integration with Popular Platforms
| Platform | Integration Method |
|---|---|
| Apache | mod_md or Certbot with –apache flag |
| Nginx | Certbot with –nginx flag |
| HAProxy | Use separate ACME client and configure cert paths |
| Docker | Traefik with Let’s Encrypt integration |
| Kubernetes | cert-manager with ClusterIssuer |
| Caddy | Built-in automatic HTTPS |
| Plesk | Let’s Encrypt extension |
| cPanel | AutoSSL feature |
Automating Certificate Management
Certbot with cron:
# Add to crontab:
0 0 * * * certbot renew --quiet
acme.sh with automated renewal:
# Installed by default during setup:
# 0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
Systemd timer for Certbot:
# Enable the timer:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
Common Challenges and Solutions
| Challenge | Solution |
|---|---|
| Rate limiting | Implement proper error handling and exponential backoff |
| DNS propagation delay | Add wait time or verification before completing DNS-01 challenges |
| Firewall blocking validation | Allow outbound traffic from CA validation servers or use DNS validation |
| Certificate renewal failures | Set up monitoring and alerts for expiring certificates |
| Mixed content warnings | Update all resources to use HTTPS URLs |
| Redirecting HTTP to HTTPS | Configure proper 301 redirects in web server |
| Certificate transparency logging | Configure monitoring for unexpected certificate issuance |
Best Practices
Security Recommendations
- Keep private keys secure and use appropriate file permissions
- Implement automated renewal at least 30 days before expiration
- Set up monitoring for certificate expiration
- Use the most specific certificate scope needed (avoid wildcards if unnecessary)
- Implement HTTP Strict Transport Security (HSTS)
- Use proper certificate chain ordering
- Implement OCSP stapling to improve performance
Performance Optimization
- Use appropriate key sizes (RSA 2048 or ECDSA P-256)
- Enable HTTP/2 for improved performance with HTTPS
- Implement OCSP stapling to reduce connection latency
- Use session resumption and TLS ticket keys
- Implement correct SSL/TLS caching headers
- Consider using ECDSA certificates for faster handshakes
ACME Client Selection Guide
| Client | Best For | Key Features |
|---|---|---|
| Certbot | General use, beginners | Official client, good documentation, plugins for web servers |
| acme.sh | Advanced users, scripting | Lightweight, rich DNS API support, low dependencies |
| Caddy | Simple web hosting | Built-in ACME client, automatic renewal |
| cert-manager | Kubernetes environments | Native Kubernetes integration |
| Traefik | Docker environments | Dynamic configuration, automatic renewal |
| Windows ACME Simple (WACS) | Windows servers | Windows-native GUI and CLI |
| Certify The Web | Windows servers with UI | User-friendly Windows interface |
| Lego | Go developers, embedding | Library and CLI tool written in Go |
Resources for Further Learning
Official Documentation
ACME Service Providers
| Provider | URL | Features |
|---|---|---|
| Let’s Encrypt | letsencrypt.org | Free, 90-day certificates, rate-limited |
| ZeroSSL | zerossl.com | Free tier, commercial options |
| BuyPass Go SSL | buypass.com | Free, 180-day certificates |
| SSL.com | ssl.com | Commercial, ACME support |
| Sectigo | sectigo.com | Commercial, ACME support |
Tools and Utilities
- SSL Labs Server Test
- Let’s Debug
- Certificate Decoder
- acme-dns (ACME DNS challenge proxy)
- certbot-dns-cloudflare (DNS plugins for Certbot)
Community Resources
Troubleshooting Guide
Common Error Messages
| Error | Possible Causes | Solutions |
|---|---|---|
| Connection refused | Firewall blocking ACME validation | Allow inbound connections on port 80/443 |
| Invalid response from server | Webserver misconfiguration | Check server configuration and logs |
| DNS problem: NXDOMAIN | DNS not propagated or misconfigured | Wait for DNS propagation, verify records |
| Too many certificates issued | Hit Let’s Encrypt rate limit | Wait for rate limit to reset, use staging environment for testing |
| Invalid Account Contact | Email address error | Update account with valid contact information |
| Timeout during connect | Network issues or server unreachable | Check network connectivity, firewall rules |
| Unauthorized -> Invalid response | Challenge verification failure | Check challenge configuration, server accessibility |
Verification Steps
HTTP-01 challenge verification:
curl -i http://example.com/.well-known/acme-challenge/TOKENDNS-01 challenge verification:
dig +short TXT _acme-challenge.example.comCertificate verification:
openssl x509 -in certificate.pem -text -nooutTLS connection test:
openssl s_client -connect example.com:443 -servername example.comCertbot dry run:
certbot renew --dry-run
