diff options
author | Andrew Cady <d@jerkface.net> | 2016-01-21 13:33:57 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2016-01-21 13:33:57 -0500 |
commit | 71cff90ba42b49ea81d8c92adbbf8bd0f1343499 (patch) | |
tree | 6edc8e4e30835f1abff14c291c218da0f557d2c4 /README.md | |
parent | d313b531e9f83878face52c33955f477db164e1f (diff) |
update documentation to reflect new code
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 173 |
1 files changed, 37 insertions, 136 deletions
@@ -1,5 +1,21 @@ | |||
1 | # Let's Encrypt ACME protocol | 1 | # Let's Encrypt ACME protocol |
2 | 2 | ||
3 | ``` | ||
4 | Usage: acme-encrypt-exe --key FILE --domain DOMAIN --dir DIR [--email ADDRESS] | ||
5 | [--terms URL] [--staging] | ||
6 | This is a work in progress. | ||
7 | |||
8 | Available options: | ||
9 | -h,--help Show this help text | ||
10 | --key FILE filename of your private RSA key | ||
11 | --domain DOMAIN the domain name to certify | ||
12 | --dir DIR output directory for ACME challenges | ||
13 | --email ADDRESS an email address with which to register an account | ||
14 | --terms URL the terms param of the registration request | ||
15 | --staging use staging servers instead of live servers | ||
16 | (certificates will not be real!) | ||
17 | ``` | ||
18 | |||
3 | This is a simple Haskell script to obtain a certificate from [Let's | 19 | This is a simple Haskell script to obtain a certificate from [Let's |
4 | Encrypt](https://letsencrypt.org/) using their ACME protocol. | 20 | Encrypt](https://letsencrypt.org/) using their ACME protocol. |
5 | 21 | ||
@@ -9,165 +25,50 @@ Encrypt](https://letsencrypt.org/) using their ACME protocol. | |||
9 | 25 | ||
10 | - The ACME spec: https://letsencrypt.github.io/acme-spec/ | 26 | - The ACME spec: https://letsencrypt.github.io/acme-spec/ |
11 | 27 | ||
12 | Most values are still hard-coded for my initial attempt (i.e. my email address | ||
13 | and a domain of mine). | ||
14 | |||
15 | |||
16 | ## Discover the URL for letsencrypt ACME endpoints | ||
17 | |||
18 | API endpoints are listed at https://acme-v01.api.letsencrypt.org/directory and | ||
19 | are currently hard-coded in the script. | ||
20 | |||
21 | ``` | ||
22 | > curl -s https://acme-v01.api.letsencrypt.org/directory | json_pp | ||
23 | { | ||
24 | "new-cert" : "https://acme-v01.api.letsencrypt.org/acme/new-cert", | ||
25 | "new-authz" : "https://acme-v01.api.letsencrypt.org/acme/new-authz", | ||
26 | "revoke-cert" : "https://acme-v01.api.letsencrypt.org/acme/revoke-cert", | ||
27 | "new-reg" : "https://acme-v01.api.letsencrypt.org/acme/new-reg" | ||
28 | } | ||
29 | ``` | ||
30 | |||
31 | |||
32 | ## Generate user account keys | 28 | ## Generate user account keys |
33 | 29 | ||
34 | You need an account with Let's Encrypt to ask and receive certificates for your | 30 | The needed keys will be automatically generated with HsOpenSSL. You can also |
35 | domains. The account is controlled by a public/private key pair: | 31 | pre-generate them manually, in which case they won't be overwritten: |
36 | |||
37 | ``` | ||
38 | openssl genrsa 4096 > user.key | ||
39 | openssl rsa -in user.key -pubout > user.pub | ||
40 | ``` | ||
41 | |||
42 | |||
43 | ## Generate nonces | ||
44 | 32 | ||
45 | 33 | ||
46 | Each request to the API have a nonce to prevent replays. The nonce is currently | ||
47 | hard-coded in the script. New nonces can be obtained from letsencrypt with | ||
48 | |||
49 | ``` | ||
50 | > generate-nonce.sh | ||
51 | ``` | 34 | ``` |
52 | 35 | openssl genrsa 4096 > user.key | |
53 | 36 | mkdir -p ${DOMAIN_NAME} | |
54 | ## Create user account | 37 | openssl genrsa 4096 > ${DOMAIN_NAME}/rsa.key |
55 | |||
56 | Generate `registration.body` by using the `acme.hs` script then POST it to | ||
57 | letsencrypt (note it assumes you agree to their subscriber agreement): | ||
58 | |||
59 | ``` | ||
60 | > curl -s -X POST --data-binary "@registration.body" \ | ||
61 | https://acme-v01.api.letsencrypt.org/acme/new-reg | json_pp | ||
62 | { | ||
63 | "agreement" : "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf", | ||
64 | "contact" : [ | ||
65 | "mailto:noteed@gmail.com" | ||
66 | ], | ||
67 | "key" : { | ||
68 | "e" : "...", | ||
69 | "kty" : "RSA", | ||
70 | "n" : "..." | ||
71 | }, | ||
72 | "id" : 36009, | ||
73 | "createdAt" : "2015-12-04T14:22:08.321951547Z", | ||
74 | "initialIp" : "80.236.245.73" | ||
75 | } | ||
76 | ``` | ||
77 | |||
78 | |||
79 | ## Request a challenge | ||
80 | |||
81 | |||
82 | Let's Encrypt needs a proof that you control the claimed domain. You can | ||
83 | request a challenge with `challenge-request.body`. | ||
84 | |||
85 | ``` | ||
86 | > curl -s -X POST --data-binary "@challenge-request.body" \ | ||
87 | https://acme-v01.api.letsencrypt.org/acme/new-authz | json_pp | ||
88 | { | ||
89 | "expires" : "2015-12-21T18:44:52.331487674Z", | ||
90 | "challenges" : [ | ||
91 | { | ||
92 | "status" : "pending", | ||
93 | "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844047", | ||
94 | "type" : "tls-sni-01", | ||
95 | "token" : "oielAbB7MdyCl29mqjzlqGdrCQSB8SyJaxHbAgQBA7Q" | ||
96 | }, | ||
97 | { | ||
98 | "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048", | ||
99 | "status" : "pending", | ||
100 | "type" : "http-01", | ||
101 | "token" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg" | ||
102 | } | ||
103 | ], | ||
104 | "identifier" : { | ||
105 | "type" : "dns", | ||
106 | "value" : "aaa.reesd.com" | ||
107 | }, | ||
108 | "combinations" : [ | ||
109 | [ | ||
110 | 0 | ||
111 | ], | ||
112 | [ | ||
113 | 1 | ||
114 | ] | ||
115 | ], | ||
116 | "status" : "pending" | ||
117 | } | ||
118 | ``` | 38 | ``` |
119 | 39 | ||
120 | The script assumes you'll answer the challenge by hosting a file at a location | 40 | ## Send CSR |
121 | chosen by Let's Encrypt. Extract the token for the `http-01` challenge and run | ||
122 | the script again. Now you have to host the file at the location reported by the | ||
123 | script. | ||
124 | 41 | ||
125 | Once this is done, you can ask Let's Encrypt to check the file. | 42 | The CSR will be automatically created. You can also create it yourself with: |
126 | 43 | ||
127 | ``` | 44 | ``` |
128 | > curl -s -X POST --data-binary "@challenge-response.body" \ | 45 | > openssl req -new -sha256 -key ${DOMAIN}/rsa.key \ |
129 | https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048 | json_pp | 46 | -subj "/CN=aaa.reesd.com" -outform DER > ${DOMAIN}/csr.der |
130 | { | ||
131 | "token" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg", | ||
132 | "keyAuthorization" : "DjyJpI3HVWAmsAwMT5ZFpW8dj19cel6ml6qaBUeGpCg.EJe0KReqzCUq6leNOerMC9naZSHxP9TJzGxCcsGkNrw", | ||
133 | "type" : "http-01", | ||
134 | "uri" : "https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048", | ||
135 | "status" : "pending" | ||
136 | } | ||
137 | ``` | 47 | ``` |
138 | 48 | ||
139 | The same URL can then be polled until the status becomes valid. | 49 | ## Receive certificate |
140 | 50 | ||
51 | The signed certificate will be saved by this program in | ||
52 | ``./${DOMAIN}/cert.der``. You can copy that file to the place your TLS server is | ||
53 | configured to read it. | ||
141 | 54 | ||
142 | ## Send CSR / Receive certificate | 55 | You can also view the certificate like so: |
143 | |||
144 | The CSR is created with: | ||
145 | 56 | ||
146 | ``` | 57 | ``` |
147 | > openssl genrsa 4096 > domain.key | 58 | > openssl x509 -inform der -in ${DOMAIN}/cert.der -noout -text | less |
148 | > openssl req -new -sha256 -key domain.key -subj "/CN=aaa.reesd.com" > aaa.reesd.com.csr | ||
149 | > openssl req -in aaa.reesd.com.csr -outform DER > aaa.reesd.com.csr.der | ||
150 | ``` | 59 | ``` |
151 | 60 | ||
152 | And the signed certificate can be obtained from Let's Encrypt: | ||
153 | |||
154 | ``` | ||
155 | > curl -s -X POST --data-binary "@csr-request.body" \ | ||
156 | https://acme-v01.api.letsencrypt.org/acme/new-cert > aaa.reesd.com.cert.der | ||
157 | ``` | ||
158 | |||
159 | |||
160 | ## Create a certificate for HAProxy | 61 | ## Create a certificate for HAProxy |
161 | 62 | ||
162 | Including explicit DH key exchange parameters to prevent Logjam attack | 63 | Including explicit DH key exchange parameters to prevent Logjam attack |
163 | (https://weakdh.org/). | 64 | (https://weakdh.org/). |
164 | 65 | ||
165 | ``` | 66 | ``` |
166 | > openssl x509 -inform der -in aaa.reesd.com.cert.der \ | 67 | > openssl x509 -inform der -in ${DOMAIN}/cert.der \ |
167 | -out aaa.reesd.com.cert.pem | 68 | -out ${DOMAIN}/cert.pem |
168 | > openssl dhparam -out aaa.reesd.com-dhparams.pem 2048 | 69 | > openssl dhparam -out ${DOMAIN}/dhparams.pem 2048 |
169 | > cat aaa.reesd.com.cert.pem \ | 70 | > cat ${DOMAIN}/cert.pem \ |
170 | lets-encrypt-x1-cross-signed.pem \ | 71 | lets-encrypt-x1-cross-signed.pem \ |
171 | aaa.reesd.com.key \ | 72 | ${DOMAIN}/rsa.key \ |
172 | aaa.reesd.com-dhparams.pem > aaa.reesd.com-combined.pem | 73 | ${DOMAIN}/dhparams.pem > aaa.reesd.com-combined.pem |
173 | ``` | 74 | ``` |