NAME

Net::ACME2 - IETF-standard ACME (Let’s Encrypt) client

SYNOPSIS

package SomeCA::ACME;

use parent qw( Net::ACME2 );

use constant {
    HOST => 'acme.someca.net',
    DIRECTORY_PATH => '/acme-directory',
};

package main;

my $acme = SomeCA::ACME->new(
    key => $account_key_pem_or_der,
    key_id => undef,
);

#for a new account
{
    my $terms_url = $acme->get_terms_of_service();

    $acme->create_new_account(
        termsOfServiceAgreed => 1,
    );
}

#Save $acme->key_id() somewhere so you can use it again.

my $order = $acme->create_new_order(
    identifiers => [
        { type => 'dns', value => '*.example.com' },
    ],
);

my $authz = $acme->get_authorization( ($order->authorizations())[0] );

my @challenges = $authz->challenges();

# ... Pick a challenge, and satisfy it.

$acme->accept_challenge($challenge);

sleep 1 while 'valid' ne $acme->poll_authorization($authz);

# ... Make a key and CSR for *.example.com

$acme->finalize_order($order, $csr_pem_or_der);

while ($order->status() ne 'valid') {
    sleep 1;
    $acme->poll_order($order);
}

my $certificate_url = $order->certificate();

# ... Download your certificate! :)

See /examples in the distribution for more fleshed-out examples.

To use Let’s Encrypt, see Net::ACME2::LetsEncrypt.

DESCRIPTION

This library implements client logic for the IETF-standard ACME (Automated Certificate Management Environment) protocol. As of this writing, that protocol remains in development; the latest draft will be available from https://datatracker.ietf.org/doc/draft-ietf-acme-acme/.

Net::ACME2 derives from Net::ACME, which implements the (significantly different) earlier draft of that protocol as initially deployed by Let’s Encrypt.

STATUS

This is a beta-grade implementation. While the underlying protocol is in use for production, it’s still not finalized; consequently, this distribution remains subject to change. It is expected that any further breaking changes will be small, but you still MUST check the changelog before upgrading!

FEATURES

  • Support for both ECDSA and RSA encrytion.

  • Support for http-01, dns-01, and tls-alpn-01 challenges.

  • Comprehensive error handling with typed, X::Tiny-based exceptions.

  • This is a pure-Perl solution. Most of its dependencies are either core modules or pure Perl themselves. XS is necessary to communicate with the ACME server via TLS; however, most Perl installations already include the necessary logic (i.e., Net::SSLeay) for TLS.

    In short, Net::ACME2 will run anywhere that Perl can speak TLS, which is almost everywhere that Perl runs.

ERROR HANDLING

All thrown exceptions are instances of Net::ACME2::X::Base. Specific error classes aren’t yet defined.

SPEED

If you notice speed problems, check to see if your Math::BigInt installation can be made faster.

METHODS

CLASS->new( %OPTS )

Instantiates an ACME2 object, which you’ll use for all interactions with the ACME server. %OPTS is:

  • key - Required. The private key to associate with the ACME2 user. Anything that Crypt::Perl::PK::parse_key() can parse is acceptable.

  • key_id - Optional. As returned by key_id(). Saves a round-trip to the ACME2 server, so you should give this if you have it.

  • directory - Optional. A hash reference to use as the directory contents. Saves a round-trip to the ACME2 server, but there’s no built-in logic to determine when the cache goes invalid. Caveat emptor.

$id = OBJ->key_id()

Returns the object’s cached key ID, either as given at instantiation or as fetched in create_new_account().

$url = OBJ->get_terms_of_service()

Returns the URL for the terms of service.

NOTE: For Let’s Encrypt you can unofficially resolve against https://acme-v01.api.letsencrypt.org/terms to see the terms of service.

$created_yn = OBJ->create_new_account( %OPTS )

Creates a new account using the ACME2 object’s key and the passed %OPTS, which are as described in the ACME2 spec (cf. newAccount). Boolean values may be given as simple Perl booleans.

Returns 1 if the account is newly created or 0 if the account already existed.

$order = OBJ->create_new_order( %OPTS )

Returns a Net::ACME2::Order object. %OPTS is as described in the ACME spec (cf. newOrder). Boolean values may be given as simple Perl booleans.

$authz = OBJ->get_authorization( $URL )

Fetches the authorization’s information based on the given $URL and returns a Net::ACME2::Authorization object.

The URL is as given by Net::ACME2::Order’s authorizations() method.

$str = OBJ->make_key_authorization( $CHALLENGE )

Accepts an instance of Net::ACME2::Challenge (probably a subclass thereof) and returns a key authorization string suitable for handling the given $CHALLENGE. See /examples in the distribution for example usage.

If you’re using HTTP authorization and are on the same server as the domains’ document roots, then look at the handler logic in Net::ACME2::Challenge::http_01 for a potentially simpler way to handle HTTP challenges.

OBJ->accept_challenge( CHALLENGE )

Signal to the ACME server that the CHALLENGE is ready.

$status = OBJ->poll_authorization( $AUTHORIZATION )

Accepts a Net::ACME2::Authorization instance and polls the ACME server for that authorization’s status. The AUTHORIZATION object is then updated with the results of the poll.

As a courtesy, this returns the object’s new status().

$status = OBJ->finalize_order( $ORDER, $CSR )

Finalizes an order and updates the $ORDER object with the returned status. $CSR may be in either DER or PEM format.

As a courtesy, this returns the $ORDER’s status(). If this does not equal valid, then you should probably poll_order() until it does.

$status = OBJ->poll_order( $ORDER )

Like poll_authorization() but handles a Net::ACME2::Order object instead.

TODO

  • Add pre-authorization support if there is ever a production use for it.

  • Expose the Retry-After header via the module API.

  • Add (more) tests.

SEE ALSO

Crypt::Perl provides pure-Perl cryptography for this library. See the present library distribution’s /examples directory for sample usage to generate keys and CSRs.