README.md in acme-client-0.3.0 vs README.md in acme-client-0.3.1
- old
+ new
@@ -1,81 +1,119 @@
# Acme::Client
[](https://travis-ci.org/unixcharles/acme-client)
`acme-client` is a client implementation of the [ACME](https://letsencrypt.github.io/acme-spec) protocol in Ruby.
-You can find the server reference implementation for ACME server [here](https://github.com/letsencrypt/boulder) and also the a reference [client](https://github.com/letsencrypt/letsencrypt) in python.
+You can find the ACME reference implementations of the [server](https://github.com/letsencrypt/boulder) in Go and the [client](https://github.com/letsencrypt/letsencrypt) in Python.
-ACME is part of the [Letsencrypt](https://letsencrypt.org/) project, that are working hard at encrypting all the things.
+ACME is part of the [Letsencrypt](https://letsencrypt.org/) project, which goal is to provide free SSL/TLS certificates with automation of the acquiring and renewal process.
+## Installation
+
+Via Rubygems:
+
+ $ gem install acme-client
+
+Or add it to a Gemfile:
+
+```ruby
+gem 'acme-client'
+```
+
## Usage
+### Register client
+
+In order to authenticate our client, we have to create an account for it.
+
```ruby
# We're going to need a private key.
require 'openssl'
-private_key = OpenSSL::PKey::RSA.new(2048)
+private_key = OpenSSL::PKey::RSA.new(4096)
# We need an ACME server to talk to, see github.com/letsencrypt/boulder
+# WARNING: This endpoint is the production endpoint, which is rate limited and will produce valid certificates.
+# You should probably use the staging endpoint for all your experimentation:
+# endpoint = 'https://acme-staging.api.letsencrypt.org/'
endpoint = 'https://acme-v01.api.letsencrypt.org/'
# Initialize the client
require 'acme/client'
client = Acme::Client.new(private_key: private_key, endpoint: endpoint)
# If the private key is not known to the server, we need to register it for the first time.
registration = client.register(contact: 'mailto:contact@example.com')
-# You'll may need to agree to the term (that's up the to the server to require it or not but boulder does by default)
+# You may need to agree to the terms of service (that's up the to the server to require it or not but boulder does by default)
registration.agree_terms
+```
-# Let's try to optain a certificate for example.org
+### Authorize for domain
-# We need to prove that we control the domain using one of the challenges method.
+Before you are able to obtain certificates for your domain, you have to prove that you are in control of it.
+
+```ruby
authorization = client.authorize(domain: 'example.org')
-# For now the only challenge method supprted by the client is http-01.
+# This example is using the http-01 challenge type. Other challenges are dns-01 or tls-sni-01.
challenge = authorization.http01
-# The http-01 method will require you to response to an HTTP request.
+# The http-01 method will require you to respond to a HTTP request.
+# You can retrieve the challenge token
+challenge.token # => "some_token"
+
# You can retrieve the expected path for the file.
challenge.filename # => ".well-known/acme-challenge/:some_token"
# You can generate the body of the expected response.
challenge.file_content # => 'string token and JWK thumbprint'
-# You can send no Content-Type at all but if you send one it has to be 'text/plain'.
+# You are not required to send a Content-Type. This method will return the right Content-Type should you decide to include one.
challenge.content_type
-# Save the file. We'll create a public directory to serve it from, and we'll creating the challenge directory.
+# Save the file. We'll create a public directory to serve it from, and inside it we'll create the challenge file.
FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
-# Then writing the file
+# We'll write the content of the file
File.write( File.join( 'public', challenge.filename), challenge.file_content )
-# The challenge file can be server with a Ruby webserver such as run a webserver in another console. You may need to forward ports on your router
-#ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0
+# Optionally save the challenge for use at another time (eg: by a background job processor)
+File.write('challenge', challenge.to_h.to_json)
+# The challenge file can be served with a Ruby webserver.
+# You can run a webserver in another console for that purpose. You may need to forward ports on your router.
+#
+# $ ruby -run -e httpd public -p 8080 --bind-address 0.0.0.0
+# Load a saved challenge. This is only required if you need to reuse a saved challenge as outlined above.
+challenge = client.challenge_from_hash(JSON.parse(File.read('challenge')))
+
# Once you are ready to serve the confirmation request you can proceed.
challenge.request_verification # => true
challenge.verify_status # => 'pending'
-# Wait a bit for the server to make the request, or really just blink, it should be fast.
+# Wait a bit for the server to make the request, or just blink. It should be fast.
sleep(1)
challenge.verify_status # => 'valid'
+```
+### Obtain a certificate
+
+Now that your account is authorized for the domain, you should be able to obtain a certificate for it.
+
+```ruby
# We're going to need a certificate signing request. If not explicitly
# specified, the first name listed becomes the common name.
csr = Acme::Client::CertificateRequest.new(names: %w[example.org www.example.org])
-# We can now request a certificate, you can pass anything that returns
-# a valid DER encoded CSR when calling to_der on it, for example a
-# OpenSSL::X509::Request too.
+# We can now request a certificate. You can pass anything that returns
+# a valid DER encoded CSR when calling to_der on it. For example an
+# OpenSSL::X509::Request should work too.
certificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>
-# Save the certificate and key
+# Save the certificate and the private key to files
File.write("privkey.pem", certificate.request.private_key.to_pem)
File.write("cert.pem", certificate.to_pem)
File.write("chain.pem", certificate.chain_to_pem)
File.write("fullchain.pem", certificate.fullchain_to_pem)