SSL Certificate Library

Hex.pmHex.pmHex Docs

A comprehensive SSL certificate management library with Let's Encrypt ACME v2 support for Erlang/OTP applications.

Index

Features

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

Dependencies

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 Coverage68%

Coverage Analysis

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 lint

Testing

# Compile and run all tests
rebar3 as test eunit

# Run specific test module
rebar3 as test eunit --module=my_module_tests

Code 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 generate

Documentation and Publishing

# Generate HTML documentation
rebar3 ex_doc

# Authenticate with Hex (one-time setup)
rebar3 hex user auth

# Publish to Hex
rebar3 hex publish

Development 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