=encoding utf-8 =head1 NAME Net::ACME - Client for the ACME protocol (e.g., Let���s Encrypt) =head1 SYNOPSIS package MyACME::SomeService; sub _HOST { } #return the name of the ACME host #See below for full examples. =head1 DESCRIPTION This module implements client logic for the ACME protocol, the system for automated issuance of SSL certificates used by Let���s Encrypt. The methods of this class return objects that correspond to the respective ACME resource: =over 4 =item * C<register()>: C<Net::ACME::Registration> =item * C<start_domain_authz()>: C<Net::ACME::Authorization::Pending> =item * C<get_certificate()>: C<Net::ACME::Certificate> or C<Net::ACME::Certificate::Pending> =back =head1 WHY USE THIS MODULE? =over 4 =item * Closely based on cPanel���s widely used Let���s Encrypt plugin. =item * Thorough error-checking: any deviation from what the ACME protocol expects is reported immediately via an exception. =item * Well-defined object system, including typed, queryable exceptions. =item * Extensive test coverage. =item * Light memory footprint - no Moose/Moo/etc. =item * No careless overwriting of globals like C<$@>, C<$!>, and C<$?>. (Hopefully your code isn���t susceptible to this anyway, but it���s just a good precaution.) =item * All dependencies are either core or pure Perl. For RSA crypto we use L<Crypt::OpenSSL::RSA> if it���s available, or the system���s C<openssl> binary. Net::ACME will run almost anywhere! =back =head1 STATUS This module is now well-tested and should be safe for use in your application. =head1 CUSTOMIZATION B<OpenSSL>: This module will attempt to use L<Crypt::OpenSSL::RSA> if it is available; if not, it falls back to the C<openssl> binary. On most systems this should already be in the process���s search path, but if not, specify the binary���s location by setting C<$Net::ACME::Crypt::OPENSSL_BIN_PATH>. B<HTTPS options>: This module uses C<HTTP::Tiny> for its network operations. In some instances it is desirable to specify custom C<SSL_options> in that module���s constructor; to do this, populate C<@Net::ACME::HTTP_Tiny::SSL_OPTIONS>. =head1 URI vs. URL This module uses ���uri��� for ACME-related objects and ���url��� for HTTP-related ones. This apparent conflict is a result of maintaining consistency with both the ACME specification (���uri���) and L<HTTP::Tiny> (���url���). =head1 EXAMPLES See the C<examples> directory in the distribution for complete, interactive example scripts that also illustrate a bit of how ACME works. See below for cut-paste-y examples. =head1 EXAMPLE: REGISTRATION my $tos_url = Net::ACME::LetsEncrypt->get_terms_of_service(); my $acme = Net::ACME::LetsEncrypt->new( key => $reg_rsa_pem ); #Use this method any time you want to update contact information, #not just when you set up a new account. my $reg = $acme->register('mailto:who.am@i.tld', 'mailto:who@else.tld'); $acme->accept_tos( $reg->uri(), $tos_url ); =head1 EXAMPLE: DOMAIN AUTHORIZATION & CERTIFICATE PROCUREMENT for my $domain (@domains) { my $authz_p = $acme->start_domain_authz($domain); for my $cmb_ar ( $authz_p->combinations() ) { #$cmb_ar is a set of challenges that the ACME server will #accept as proof of domain control. As of November 2016, these #sets all contain exactly one challenge each: ���http-01���, etc. #Each member of @$cmb_ar is an instance of #Net::ACME::Challenge::Pending--maybe a subclass thereof such as #Net::ACME::Challenge::Pending::http_01. #At this point, you examine $cmb_ar and determine if this #combination is one that you���re interested in. You might try #something like: # # next if @$cmb_ar > 1; # next if $cmb_ar->[0]->type() ne 'http-01'; #Once you���ve examined $cmb_ar and set up the appropriate response(s), #it���s time to tell the ACME server to send its challenge query. $acme->do_challenge($_) for @$cmb_ar; while (1) { if ( $authz_p->is_time_to_poll() ) { my $poll = $authz_p->poll(); last if $poll->status() eq 'valid'; if ( $poll->status() eq 'invalid' ) { my @failed = grep { $_->error() } $poll->challenges(); warn $_->to_string() . $/ for @failed; die "Failed authorization for ���$domain���!"; } } sleep 1; } } } #Make a key and CSR. #Creation of CSRs is well-documented so won���t be discussed here. my $cert = $acme->get_certificate($csr_pem); #This shouldn���t actually be necessary for Let���s Encrypt, #but the ACME protocol describes it. while ( !$cert->pem() ) { sleep 1; next if !$cert->is_time_to_poll(); $cert = $cert->poll() || $cert; } =head1 TODO =over 4 =item * Find a way to sign with RSA in pure Perl. (NB: L<Crypt::RSA> has XS dependencies.) =item * Support EC keys. =item * Test and support more ACME v2 features (pending server support). =back =head1 THANKS =over 4 =item * cPanel, Inc. for permission to adapt their ACME framework for public consumption. =item * Stephen Ludin for developing and maintaining C<Protocol::ACME>, from which this module took its inspiration. =back =head1 SEE ALSO I am aware of the following additional CPAN modules that implement this protocol: =over 4 =item * L<Protocol::ACME> =item * L<Crypt::LE> =item * L<WWW::LetsEncrypt> =item * L<Mojo::ACME> =back =head1 REPOSITORY (FEEDBACK/BUGS) L<https://github.com/FGasper/p5-Net-ACME> =head1 AUTHOR Felipe Gasper (FELIPE) =head1 LICENSE This module is licensed under the same terms as Perl.