Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fgasper/p5-net-acme2
CPAN’s Net::ACME2
https://github.com/fgasper/p5-net-acme2
Last synced: 18 days ago
JSON representation
CPAN’s Net::ACME2
- Host: GitHub
- URL: https://github.com/fgasper/p5-net-acme2
- Owner: FGasper
- License: other
- Created: 2018-01-13T18:26:45.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2020-11-22T12:42:30.000Z (about 4 years ago)
- Last Synced: 2024-10-22T07:42:54.046Z (2 months ago)
- Language: Perl
- Size: 179 KB
- Stars: 3
- Watchers: 4
- Forks: 7
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: Changes
- License: LICENSE
Awesome Lists containing this project
README
# NAME
Net::ACME2 - Client logic for the ACME (Let's Encrypt) protocol
# SYNOPSIS
package SomeCA::ACME;
use parent qw( Net::ACME2 );
use constant {
DIRECTORY_PATH => '/acme-directory',
};# %opts are the parameters given to new().
sub HOST {
my ($class, %opts) = @_;# You can make this depend on the %opts if you want.
return 'acme.someca.net';
}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_account(
termsOfServiceAgreed => 1,
);
}#Save $acme->key_id() somewhere so you can use it again.
my $order = $acme->create_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);
}# ... and now fetch the certificate chain:
my $pem_chain = $acme->get_certificate_chain($order);
See `/examples` in the distribution for more fleshed-out examples.
To use [Let’s Encrypt](http://letsencrypt.org), see
[Net::ACME2::LetsEncrypt](https://metacpan.org/pod/Net::ACME2::LetsEncrypt).# DESCRIPTION
This library implements client logic for the
ACME (Automated Certificate Management Environment) protocol, as
standardized in [RFC 8555](https://www.rfc-editor.org/rfc/rfc8555.txt)
and popularized by [Let’s Encrypt](http://letsencrypt.org).# STATUS
This is a production-grade implementation. While breaking changes at this
point are unlikely, please always check the changelog before upgrading to
a new version of this module.# FEATURES
- Support for both ECDSA and RSA encrytion.
- Support for http-01, dns-01, and [tls-alpn-01](https://datatracker.ietf.org/doc/draft-ietf-acme-tls-alpn/) challenges.
- Comprehensive error handling with typed, [X::Tiny](https://metacpan.org/pod/X::Tiny)-based exceptions.
- Supports blocking and (experimentally) non-blocking I/O.
- [Retry POST on `badNonce` errors.](https://tools.ietf.org/html/rfc8555#section-6.5)
- 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](https://metacpan.org/pod/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::Generic](https://metacpan.org/pod/Net::ACME2::X::Generic).
Specific error classes aren’t yet defined.# CRYPTOGRAPHY & SPEED
[Crypt::Perl](https://metacpan.org/pod/Crypt::Perl) provides all cryptographic operations that this library
needs using pure Perl. While this satisfies this module’s intent to be
as pure-Perl as possible, there are a couple of significant drawbacks
to this approach: firstly, it’s slower than XS-based code, and secondly,
it loses the security benefits of the vetting that more widely-used
cryptography libraries receive.To address these problems, Net::ACME2 will, after parsing a key, look
for and prefer the following XS-based libraries for cryptography instead:- [Crypt::OpenSSL::RSA](https://metacpan.org/pod/Crypt::OpenSSL::RSA) (based on [OpenSSL](http://openssl.org))
- [CryptX](https://metacpan.org/pod/CryptX) (based on [LibTomCrypt](http://www.libtom.net/LibTomCrypt/))If the above are unavailable to you, then you may be able to speed up
your [Math::BigInt](https://metacpan.org/pod/Math::BigInt) installation; see that module’s documentation
for more details.# EXPERIMENTAL: NON-BLOCKING (ASYNCHRONOUS) I/O
By default, Net::ACME2 uses blocking I/O.
To facilitate asynchronous/non-blocking I/O, you may give an `async_ua`
to `new()`. This value must be an object that implements `request()`.
That method should mimic [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny)’s method of the same name
**except** that, instead of returning a hash reference, it should return
a promise. (à la [Promise::XS](https://metacpan.org/pod/Promise::XS), [Promise::ES6](https://metacpan.org/pod/Promise::ES6), [Mojo::Promise](https://metacpan.org/pod/Mojo::Promise), etc.)
That promise’s resolution should be a single value that mimics
`HTTP::Tiny::request()`’s return structure.When a Net::ACME2 instance is created with `async_ua`, several of the
methods described below return promises. These promises resolve to the values
that otherwise would be returned directly in synchronous mode. Any exception
that would be thrown in synchronous mode is given as the promise’s rejection
value. This document’s convention to indicate a function that, in
asynchronous mode, returns a promise is:promise($whatever) = ...
This distribution ships with [Net::ACME2::Curl](https://metacpan.org/pod/Net::ACME2::Curl), a wrapper around
[Net::Curl::Promiser](https://metacpan.org/pod/Net::Curl::Promiser), which in turns wraps [Net::Curl::Multi](https://metacpan.org/pod/Net::Curl::Multi). This
provides out-of-the-box support for Perl’s most widely-used event interfaces;
see Net::Curl::Promiser’s documentation for more details.# 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.
- `async_ua` - Optional. Provides a custom UA object to facilitate
non-blocking I/O. This object **MUST** implement the interface described above.## $id = _OBJ_->key\_id()
Returns the object’s cached key ID, either as given at instantiation
or as fetched in `create_account()`.## _OBJ_->http\_timeout( \[$NEW\] )
A passthrough interface to the underlying [HTTP::Tiny](https://metacpan.org/pod/HTTP::Tiny) object’s
`timeout()` method.Throws an exception if `async_ua` was given to `new()`.
## promise($url) = _CLASS_->get\_terms\_of\_service()
Returns the URL for the terms of service. Callable as either
a class method or an instance method.## promise($created\_yn) = _OBJ_->create\_account( %OPTS )
Creates an 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.NB: `create_new_account()` is an alias for this method.
## promise($order) = _OBJ_->create\_order( %OPTS )
Returns a [Net::ACME2::Order](https://metacpan.org/pod/Net::ACME2::Order) object. %OPTS is as described in the
ACME spec (cf. `newOrder`). Boolean values may be given as simple
Perl booleans.NB: `create_new_order()` is an alias for this method.
## promise($authz) = _OBJ_->get\_authorization( $URL )
Fetches the authorization’s information based on the given $URL
and returns a [Net::ACME2::Authorization](https://metacpan.org/pod/Net::ACME2::Authorization) object.The URL is as given by [Net::ACME2::Order](https://metacpan.org/pod/Net::ACME2::Order)’s `authorizations()` method.
## $str = _OBJ_->make\_key\_authorization( $CHALLENGE )
Accepts an instance of [Net::ACME2::Challenge](https://metacpan.org/pod/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](https://metacpan.org/pod/Net::ACME2::Challenge::http_01) for a potentially simpler way to
handle HTTP challenges.## promise() = _OBJ_->accept\_challenge( $CHALLENGE )
Signal to the ACME server that the CHALLENGE is ready.
## promise($status) = _OBJ_->poll\_authorization( $AUTHORIZATION )
Accepts a [Net::ACME2::Authorization](https://metacpan.org/pod/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 $AUTHORIZATION’s new `status()`.
## promise($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.## promise($status) = _OBJ_->poll\_order( $ORDER )
Like `poll_authorization()` but handles a
[Net::ACME2::Order](https://metacpan.org/pod/Net::ACME2::Order) object instead.## promise($cert) = _OBJ_->get\_certificate\_chain( $ORDER )
Fetches the $ORDER’s certificate chain and returns
it in the format implied by the
`application/pem-certificate-chain` MIME type. See the ACME
protocol specification for details about this format.# TODO
- Add pre-authorization support if there is ever a production
use for it.
- Expose the Retry-After header via the module API.
- There is currently no way to fetch an order or challenge’s
properties via URL. Prior to ACME’s adoption of “POST-as-GET” this was
doable via a plain GET to the URL, but that’s no longer possible.
If there’s a need, I’ll consider adding such logic to Net::ACME2.
(It’s trivial to add; I’d just like to keep things as
simple as possible.)
- Add (more) tests.# SEE ALSO
[Crypt::LE](https://metacpan.org/pod/Crypt::LE) is another ACME client library.
[Crypt::Perl](https://metacpan.org/pod/Crypt::Perl) provides this library’s default cryptography backend.
See this distribution’s `/examples` directory for sample usage
to generate keys and CSRs.[Net::ACME](https://metacpan.org/pod/Net::ACME) implements client logic for the variant of this
protocol that Let’s Encrypt first deployed.