SSL Certificate Library
A comprehensive SSL certificate management library with Let's Encrypt ACME v2 support for Erlang/OTP applications.
Index
Features
- Full ACME v2 protocol support
- Let's Encrypt certificate automation
- DNS and HTTP-01 challenge support
- Certificate lifecycle management
- Cryptographic operations for certificate handling
- Comprehensive validation and state management
- Modern HTTP/2 client using gun
Installation
Add this library to your rebar.config dependencies:
{deps, [
{ssl_cert, {git, "https://github.com/permaweb/ssl_cert.git", {branch, "main"}}}
]}.Usage
Device Configuration
Configure the SSL certificate device with the required options:
%% Configuration for SSL certificate requests
Opts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>],
<<"email">> => <<"admin@example.com">>,
<<"environment">> => <<"staging">> % Use "production" for live certificates
}
}.Certificate Request Workflow
Step 1: Request Certificate
%% Initiate certificate request - returns DNS challenges
{ok, Response} = dev_ssl_cert:request(undefined, undefined, Opts),
#{<<"body">> := #{
<<"challenges">> := Challenges,
<<"message">> := <<"Create DNS TXT records for the following challenges, then call finalize">>
}} = Response.Step 2: Set DNS TXT Records
Based on the returned challenges, create DNS TXT records:
_acme-challenge.example.com. TXT "challenge_token_here"
_acme-challenge.www.example.com. TXT "challenge_token_here"Step 3: Finalize Certificate
%% After DNS records are set, finalize the certificate
{ok, FinalResponse} = dev_ssl_cert:finalize(undefined, undefined, Opts),
#{<<"body">> := #{
<<"certificate_pem">> := CertPem,
<<"key_pem">> := KeyPem,
<<"domains">> := Domains
}} = FinalResponse.Certificate Management
Renew Certificate
%% Renew existing certificate
RenewOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>],
<<"email">> => <<"admin@example.com">>,
<<"environment">> => <<"production">>
}
},
{ok, RenewResponse} = dev_ssl_cert:renew(undefined, undefined, RenewOpts).Delete Certificate
%% Delete stored certificate
DeleteOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>]
}
},
{ok, DeleteResponse} = dev_ssl_cert:delete(undefined, undefined, DeleteOpts).Environment Configuration
Staging Environment (for testing)
StagingOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"test.example.com">>],
<<"email">> => <<"test@example.com">>,
<<"environment">> => <<"staging">>
}
}.Production Environment
ProductionOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>],
<<"email">> => <<"admin@example.com">>,
<<"environment">> => <<"production">>
}
}.Direct Module Usage
For advanced use cases, you can call the underlying modules directly:
%% Validate request parameters
{ok, ValidatedParams} = ssl_cert_validation:validate_request_params(
[<<"example.com">>], <<"admin@example.com">>, <<"staging">>),
%% Process certificate request
{ok, ProcessResponse} = ssl_cert_ops:process_certificate_request(ValidatedParams, Wallet),
%% Validate DNS challenges
{ok, ValidationResponse} = ssl_cert_challenge:validate_dns_challenges_state(RequestState, PrivateKey),
%% Generate CSR
{ok, {CsrDer, PrivateKey}} = acme_csr:generate_csr([<<"example.com">>], #{}).Modules
acme_client- Main ACME client APIssl_cert_ops- High-level certificate operationsacme_protocol- Core ACME protocol implementationacme_crypto- Cryptographic operations and JWSacme_csr- Certificate Signing Request generationssl_cert_challenge- Challenge handling and validationssl_cert_validation- Certificate validation utilitiesssl_cert_state- State management utilitiesssl_utils- Utility functions and HTTP client
Dependencies
gun- Modern HTTP/2 client for ACME communicationcrypto- Cryptographic operationspublic_key- Public key operationsssl- SSL/TLS supportinets- Additional HTTP utilities
Code Coverage
Current test coverage across all modules:
| Module | Coverage |
|---|---|
| Core Modules | |
acme_client | 25% |
acme_crypto | 65% |
acme_csr | 81% |
acme_http | 49% |
acme_protocol | 26% |
acme_url | 100% |
ssl_cert_challenge | 18% |
ssl_cert_ops | 24% |
ssl_cert_state | 65% |
ssl_cert_validation | 95% |
ssl_utils | 29% |
| Test Modules | |
acme_client_tests | 91% |
acme_crypto_tests | 100% |
acme_csr_tests | 91% |
acme_http_tests | 100% |
acme_protocol_tests | 91% |
acme_url_tests | 100% |
ssl_cert_challenge_tests | 100% |
ssl_cert_integration_tests | 100% |
ssl_cert_ops_tests | 100% |
ssl_cert_state_tests | 100% |
ssl_cert_test_suite | 10% |
ssl_cert_validation_tests | 100% |
ssl_utils_tests | 100% |
| Total Coverage | 68% |
Coverage Analysis
- High Coverage (80%+):
acme_csr,acme_url,ssl_cert_validation - Medium Coverage (50-79%):
acme_crypto,ssl_cert_state - Low Coverage (<50%):
acme_client,acme_http,acme_protocol,ssl_cert_challenge,ssl_cert_ops,ssl_utils
Development Commands
Code Quality and Formatting
# Format all Erlang files
rebar3 fmt
# Check if files need formatting (don't modify)
rebar3 fmt --check
# Run linter to check code quality
rebar3 lintTesting
# Compile and run all tests
rebar3 as test eunit
# Run specific test module
rebar3 as test eunit --module=my_module_testsCode Coverage
# Run tests with coverage analysis
rebar3 cover
# Generate coverage reports
rebar3 covertool generate
# Full test and coverage workflow
rebar3 as test eunit && rebar3 cover && rebar3 covertool generateDocumentation and Publishing
# Generate HTML documentation
rebar3 ex_doc
# Authenticate with Hex (one-time setup)
rebar3 hex user auth
# Publish to Hex
rebar3 hex publishDevelopment Workflow
# Complete quality check before commit
rebar3 clean
rebar3 fmt --check
rebar3 lint
rebar3 as test compile
rebar3 as test eunit
rebar3 cover
rebar3 covertool generate