.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Net::ACME2 3" .TH Net::ACME2 3 "2020-04-13" "perl v5.30.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Net::ACME2 \- Client logic for the ACME (Let's Encrypt) protocol .PP .IX Xref "Lets Encrypt Let's Encrypt letsencrypt" .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& package SomeCA::ACME; \& \& use parent qw( Net::ACME2 ); \& \& use constant { \& DIRECTORY_PATH => \*(Aq/acme\-directory\*(Aq, \& }; \& \& # %opts are the parameters given to new(). \& sub HOST { \& my ($class, %opts) = @_; \& \& # You can make this depend on the %opts if you want. \& return \*(Aqacme.someca.net\*(Aq; \& } \& \& 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 => \*(Aqdns\*(Aq, value => \*(Aq*.example.com\*(Aq }, \& ], \& ); \& \& 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 \*(Aqvalid\*(Aq 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 \*(Aqvalid\*(Aq) { \& sleep 1; \& $acme\->poll_order($order); \& } \& \& # ... and now fetch the certificate chain: \& \& my $pem_chain = $acme\->get_certificate_chain($order); .Ve .PP See \fI/examples\fR in the distribution for more fleshed-out examples. .PP To use Let’s Encrypt , see Net::ACME2::LetsEncrypt. .SH "DESCRIPTION" .IX Header "DESCRIPTION" This library implements client logic for the \&\s-1ACME\s0 (Automated Certificate Management Environment) protocol, as standardized in \s-1RFC 8555\s0 and popularized by Let’s Encrypt . .SH "STATUS" .IX Header "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. .SH "FEATURES" .IX Header "FEATURES" .IP "\(bu" 4 Support for both \s-1ECDSA\s0 and \s-1RSA\s0 encrytion. .IP "\(bu" 4 Support for http\-01, dns\-01, and tls\-alpn\-01 challenges. .IP "\(bu" 4 Comprehensive error handling with typed, X::Tiny\-based exceptions. .IP "\(bu" 4 Retry \s-1POST\s0 on \f(CW\*(C`badNonce\*(C'\fR errors. .IP "\(bu" 4 This is a pure-Perl solution. Most of its dependencies are either core modules or pure Perl themselves. \s-1XS\s0 is necessary to communicate with the \s-1ACME\s0 server via \s-1TLS\s0; however, most Perl installations already include the necessary logic (i.e., Net::SSLeay) for \s-1TLS.\s0 .Sp In short, Net::ACME2 will run anywhere that Perl can speak \s-1TLS,\s0 which is \&\fIalmost\fR everywhere that Perl runs. .SH "ERROR HANDLING" .IX Header "ERROR HANDLING" All thrown exceptions are instances of Net::ACME2::X::Generic. Specific error classes aren’t yet defined. .SH "CRYPTOGRAPHY & SPEED" .IX Header "CRYPTOGRAPHY & SPEED" 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. .PP To address these problems, Net::ACME2 will, after parsing a key, look for and prefer the following XS-based libraries for cryptography instead: .IP "\(bu" 4 Crypt::OpenSSL::RSA (based on OpenSSL ) .IP "\(bu" 4 CryptX (based on LibTomCrypt ) .PP If the above are unavailable to you, then you may be able to speed up your Math::BigInt installation; see that module’s documentation for more details. .SH "METHODS" .IX Header "METHODS" .ie n .SS "\fI\s-1CLASS\s0\fP\->new( %OPTS )" .el .SS "\fI\s-1CLASS\s0\fP\->new( \f(CW%OPTS\fP )" .IX Subsection "CLASS->new( %OPTS )" Instantiates an \s-1ACME2\s0 object, which you’ll use for all interactions with the \s-1ACME\s0 server. \f(CW%OPTS\fR is: .IP "\(bu" 4 \&\f(CW\*(C`key\*(C'\fR \- Required. The private key to associate with the \s-1ACME2\s0 user. Anything that \f(CW\*(C`Crypt::Perl::PK::parse_key()\*(C'\fR can parse is acceptable. .IP "\(bu" 4 \&\f(CW\*(C`key_id\*(C'\fR \- Optional. As returned by \f(CW\*(C`key_id()\*(C'\fR. Saves a round-trip to the \s-1ACME2\s0 server, so you should give this if you have it. .IP "\(bu" 4 \&\f(CW\*(C`directory\*(C'\fR \- Optional. A hash reference to use as the directory contents. Saves a round-trip to the \s-1ACME2\s0 server, but there’s no built-in logic to determine when the cache goes invalid. Caveat emptor. .ie n .SS "$id = \fI\s-1OBJ\s0\fP\->\fBkey_id()\fP" .el .SS "\f(CW$id\fP = \fI\s-1OBJ\s0\fP\->\fBkey_id()\fP" .IX Subsection "$id = OBJ->key_id()" Returns the object’s cached key \s-1ID,\s0 either as given at instantiation or as fetched in \f(CW\*(C`create_account()\*(C'\fR. .SS "\fI\s-1OBJ\s0\fP\->http_timeout( [$NEW] )" .IX Subsection "OBJ->http_timeout( [$NEW] )" A passthrough interface to the underlying HTTP::Tiny object’s \&\f(CW\*(C`timeout()\*(C'\fR method. .ie n .SS "$url = \fI\s-1CLASS\s0\fP\->\fBget_terms_of_service()\fP" .el .SS "\f(CW$url\fP = \fI\s-1CLASS\s0\fP\->\fBget_terms_of_service()\fP" .IX Subsection "$url = CLASS->get_terms_of_service()" Returns the \s-1URL\s0 for the terms of service. Callable as either a class method or an instance method. .ie n .SS "$created_yn = \fI\s-1OBJ\s0\fP\->create_account( %OPTS )" .el .SS "\f(CW$created_yn\fP = \fI\s-1OBJ\s0\fP\->create_account( \f(CW%OPTS\fP )" .IX Subsection "$created_yn = OBJ->create_account( %OPTS )" Creates an account using the \s-1ACME2\s0 object’s key and the passed \&\f(CW%OPTS\fR, which are as described in the \s-1ACME2\s0 spec (cf. \f(CW\*(C`newAccount\*(C'\fR). Boolean values may be given as simple Perl booleans. .PP Returns 1 if the account is newly created or 0 if the account already existed. .PP \&\s-1NB:\s0 \f(CW\*(C`create_new_account()\*(C'\fR is an alias for this method. .ie n .SS "$order = \fI\s-1OBJ\s0\fP\->create_order( %OPTS )" .el .SS "\f(CW$order\fP = \fI\s-1OBJ\s0\fP\->create_order( \f(CW%OPTS\fP )" .IX Subsection "$order = OBJ->create_order( %OPTS )" Returns a Net::ACME2::Order object. \f(CW%OPTS\fR is as described in the \&\s-1ACME\s0 spec (cf. \f(CW\*(C`newOrder\*(C'\fR). Boolean values may be given as simple Perl booleans. .PP \&\s-1NB:\s0 \f(CW\*(C`create_new_order()\*(C'\fR is an alias for this method. .ie n .SS "$authz = \fI\s-1OBJ\s0\fP\->get_authorization( $URL )" .el .SS "\f(CW$authz\fP = \fI\s-1OBJ\s0\fP\->get_authorization( \f(CW$URL\fP )" .IX Subsection "$authz = OBJ->get_authorization( $URL )" Fetches the authorization’s information based on the given \f(CW$URL\fR and returns a Net::ACME2::Authorization object. .PP The \s-1URL\s0 is as given by Net::ACME2::Order’s \f(CW\*(C`authorizations()\*(C'\fR method. .ie n .SS "$str = \fI\s-1OBJ\s0\fP\->make_key_authorization( $CHALLENGE )" .el .SS "\f(CW$str\fP = \fI\s-1OBJ\s0\fP\->make_key_authorization( \f(CW$CHALLENGE\fP )" .IX Subsection "$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 \f(CW$CHALLENGE\fR. See \fI/examples\fR in the distribution for example usage. .PP If you’re using \s-1HTTP\s0 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 \s-1HTTP\s0 challenges. .ie n .SS "\fI\s-1OBJ\s0\fP\->accept_challenge( $CHALLENGE )" .el .SS "\fI\s-1OBJ\s0\fP\->accept_challenge( \f(CW$CHALLENGE\fP )" .IX Subsection "OBJ->accept_challenge( $CHALLENGE )" Signal to the \s-1ACME\s0 server that the \s-1CHALLENGE\s0 is ready. .ie n .SS "$status = \fI\s-1OBJ\s0\fP\->poll_authorization( $AUTHORIZATION )" .el .SS "\f(CW$status\fP = \fI\s-1OBJ\s0\fP\->poll_authorization( \f(CW$AUTHORIZATION\fP )" .IX Subsection "$status = OBJ->poll_authorization( $AUTHORIZATION )" Accepts a Net::ACME2::Authorization instance and polls the \&\s-1ACME\s0 server for that authorization’s status. The \f(CW$AUTHORIZATION\fR object is then updated with the results of the poll. .PP As a courtesy, this returns the \f(CW$AUTHORIZATION\fR’s new \f(CW\*(C`status()\*(C'\fR. .ie n .SS "$status = \fI\s-1OBJ\s0\fP\->finalize_order( $ORDER, $CSR )" .el .SS "\f(CW$status\fP = \fI\s-1OBJ\s0\fP\->finalize_order( \f(CW$ORDER\fP, \f(CW$CSR\fP )" .IX Subsection "$status = OBJ->finalize_order( $ORDER, $CSR )" Finalizes an order and updates the \f(CW$ORDER\fR object with the returned status. \f(CW$CSR\fR may be in either \s-1DER\s0 or \s-1PEM\s0 format. .PP As a courtesy, this returns the \f(CW$ORDER\fR’s \f(CW\*(C`status()\*(C'\fR. If this does not equal \f(CW\*(C`valid\*(C'\fR, then you should probably \f(CW\*(C`poll_order()\*(C'\fR until it does. .ie n .SS "$status = \fI\s-1OBJ\s0\fP\->poll_order( $ORDER )" .el .SS "\f(CW$status\fP = \fI\s-1OBJ\s0\fP\->poll_order( \f(CW$ORDER\fP )" .IX Subsection "$status = OBJ->poll_order( $ORDER )" Like \f(CW\*(C`poll_authorization()\*(C'\fR but handles a Net::ACME2::Order object instead. .ie n .SS "$cert = \fI\s-1OBJ\s0\fP\->get_certificate_chain( $ORDER )" .el .SS "\f(CW$cert\fP = \fI\s-1OBJ\s0\fP\->get_certificate_chain( \f(CW$ORDER\fP )" .IX Subsection "$cert = OBJ->get_certificate_chain( $ORDER )" Fetches the \f(CW$ORDER\fR’s certificate chain and returns it in the format implied by the \&\f(CW\*(C`application/pem\-certificate\-chain\*(C'\fR \s-1MIME\s0 type. See the \s-1ACME\s0 protocol specification for details about this format. .SH "TODO" .IX Header "TODO" .IP "\(bu" 4 Add pre-authorization support if there is ever a production use for it. .IP "\(bu" 4 Expose the Retry-After header via the module \s-1API.\s0 .IP "\(bu" 4 There is currently no way to fetch an order or challenge’s properties via \s-1URL.\s0 Prior to ACME’s adoption of “POST\-as\-GET” this was doable via a plain \s-1GET\s0 to the \s-1URL,\s0 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.) .IP "\(bu" 4 Add (more) tests. .SH "SEE ALSO" .IX Header "SEE ALSO" Crypt::LE is another \s-1ACME\s0 client library. .PP Crypt::Perl provides this library’s default cryptography backend. See this distribution’s \fI/examples\fR directory for sample usage to generate keys and CSRs. .PP Net::ACME implements client logic for the variant of this protocol that Let’s Encrypt first deployed.