README.md in acme-client-0.1.3 vs README.md in acme-client-0.2.0
- old
+ new
@@ -1,6 +1,7 @@
# Acme::Client
+[![Build Status](https://travis-ci.org/unixcharles/acme-client.svg?branch=master)](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.
@@ -8,16 +9,18 @@
## Usage
```ruby
# We're going to need a private key.
+require 'openssl'
private_key = OpenSSL::PKey::RSA.new(2048)
# We need an ACME server to talk to, see github.com/letsencrypt/boulder
endpoint = 'https://acme-staging.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:unixcharles@gmail.com')
@@ -27,54 +30,78 @@
# Let's try to optain a certificate for yourdomain.com
# We need to prove that we control the domain using one of the challenges method.
authorization = client.authorize(domain: 'yourdomain.com')
-# For now the only challenge method supprted by the client is simple_http.
-simple_http = authorization.simple_http
+# For now the only challenge method supprted by the client is http-01.
+challenge = authorization.http01
-# The SimpleHTTP method will require you to response to an HTTP request.
+# The http-01 method will require you to response to an HTTP request.
# You can retrieve the expected path for the file.
-simple_http.filename # => ".well-known/acme-challenge/:some_token"
+challenge.filename # => ".well-known/acme-challenge/:some_token"
# You can generate the body of the expected response.
-simple_http.file_content # => 'string of JWS signed json'
+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 'application/jose+json'.
-simple_http.content_type
+# You can send no Content-Type at all but if you send one it has to be 'text/plain'.
+challenge.content_type
+# Save the file. We'll create a public directory to serve it from, and we'll creating the challenge directory.
+FileUtils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )
+
+# Then writing 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
+
+
# Once you are ready to serve the confirmation request you can proceed.
-simple_http.request_verification # => true
-simple_http.verify_status # => 'pending'
+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.
sleep(1)
-simple_http.verify_status # => 'valid'
+challenge.verify_status # => 'valid'
# We're going to need a CSR, lets do this real quick with Ruby+OpenSSL.
csr = OpenSSL::X509::Request.new
# We need a private key for the certificate, not the same as the account key.
certificate_private_key = OpenSSL::PKey::RSA.new(2048)
# We just going to add the domain but normally you might want to provide more information.
csr.subject = OpenSSL::X509::Name.new([
- ['CN', common_name, OpenSSL::ASN1::UTF8STRING]
+ ['CN', 'yourdomain.com', OpenSSL::ASN1::UTF8STRING]
])
csr.public_key = certificate_private_key.public_key
csr.sign(certificate_private_key, OpenSSL::Digest::SHA256.new)
# We can now request a certificate
-client.new_certificate(csr) # => #<OpenSSL::X509::Certificate ....>
+certificate = client.new_certificate(csr) # => #<Acme::Certificate ....>
+
+# Save the certificate and key
+File.write("cert.pem", certificate.to_pem)
+File.write("key.pem", certificate_private_key.to_pem)
+File.write("chain.pem", certificate.chain_to_pem)
+File.write("fullchain.pem", certificate.fullchain_to_pem)
+
+# Start a webserver, using your shiny new certificate
+# ruby -r openssl -r webrick -r 'webrick/https' -e "s = WEBrick::HTTPServer.new(
+# :Port => 8443,
+# :DocumentRoot => Dir.pwd,
+# :SSLEnable => true,
+# :SSLPrivateKey => OpenSSL::PKey::RSA.new( File.read('key.pem') ),
+# :SSLCertificate => OpenSSL::X509::Certificate.new( File.read('cert.pem') )); trap('INT') { s.shutdown }; s.start"
```
# Not implemented
- Recovery methods are not implemented.
-- SimpleHTTP is the only challenge method implemented
+- http-01 is the only challenge method implemented
## Development
All the tests use VCR to mock the interaction with the server but if you
need to record new interation against the server simply clone boulder and