thehyve/acme-client

Name: acme-client

Owner: The Hyve

Description: A Ruby client for the letsencrypt's ACME protocol.

Forked from: unixcharles/acme-client

Created: 2016-05-13 16:43:06.0

Updated: 2016-05-13 16:43:07.0

Pushed: 2016-05-13 21:16:36.0

Homepage: null

Size: 380

Language: Ruby

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Acme::Client

Build Status

acme-client is a client implementation of the ACME protocol in Ruby.

You can find the ACME reference implementations of the server in Go and the client in Python.

ACME is part of the Letsencrypt 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:

'acme-client'
Usage
Register client

In order to authenticate our client, we have to create an account for it.

're going to need a private key.
ire 'openssl'
ate_key = OpenSSL::PKey::RSA.new(4096)

 need an ACME server to talk to, see github.com/letsencrypt/boulder
RNING: This endpoint is the production endpoint, which is rate limited and will produce valid certificates.
u should probably use the staging endpoint for all your experimentation:
dpoint = 'https://acme-staging.api.letsencrypt.org/'
oint = 'https://acme-v01.api.letsencrypt.org/'

itialize the client
ire 'acme-client'
nt = Acme::Client.new(private_key: private_key, endpoint: endpoint, connection_options: { request: { open_timeout: 5, timeout: 5 } })

 the private key is not known to the server, we need to register it for the first time.
stration = client.register(contact: 'mailto:contact@example.com')

u 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)
stration.agree_terms
Authorize for domain

Before you are able to obtain certificates for your domain, you have to prove that you are in control of it.

orization = client.authorize(domain: 'example.org')

is example is using the http-01 challenge type. Other challenges are dns-01 or tls-sni-01.
lenge = authorization.http01

e http-01 method will require you to respond to a HTTP request.

u can retrieve the challenge token
lenge.token # => "some_token"

u can retrieve the expected path for the file.
lenge.filename # => ".well-known/acme-challenge/:some_token"

u can generate the body of the expected response.
lenge.file_content # => 'string token and JWK thumbprint'

u are not required to send a Content-Type. This method will return the right Content-Type should you decide to include one.
lenge.content_type

ve the file. We'll create a public directory to serve it from, and inside it we'll create the challenge file.
Utils.mkdir_p( File.join( 'public', File.dirname( challenge.filename ) ) )

'll write the content of the file
.write( File.join( 'public', challenge.filename), challenge.file_content )

tionally save the challenge for use at another time (eg: by a background job processor)
.write('challenge', challenge.to_h.to_json)

e challenge file can be served with a Ruby webserver.
u 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

ad a saved challenge. This is only required if you need to reuse a saved challenge as outlined above.
lenge = client.challenge_from_hash(JSON.parse(File.read('challenge')))

ce you are ready to serve the confirmation request you can proceed.
lenge.request_verification # => true
lenge.verify_status # => 'pending'

it a bit for the server to make the request, or just blink. It should be fast.
p(1)

lenge.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.

're going to need a certificate signing request. If not explicitly
ecified, the first name listed becomes the common name.
= Acme::Client::CertificateRequest.new(names: %w[example.org www.example.org])

 can now request a certificate. You can pass anything that returns
valid DER encoded CSR when calling to_der on it. For example an
enSSL::X509::Request should work too.
ificate = client.new_certificate(csr) # => #<Acme::Client::Certificate ....>

ve the certificate and the private key to files
.write("privkey.pem", certificate.request.private_key.to_pem)
.write("cert.pem", certificate.to_pem)
.write("chain.pem", certificate.chain_to_pem)
.write("fullchain.pem", certificate.fullchain_to_pem)

art a webserver, using your shiny new certificate
by -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('privkey.pem') ),
:SSLCertificate => OpenSSL::X509::Certificate.new( File.read('cert.pem') )); trap('INT') { s.shutdown }; s.start"

Not implemented

Requirements

Ruby >= 2.1

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 run it normally with ./start.py.

Pull request?

Yes.

License

MIT License


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.