summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorVo Minh Thu <thu@hypered.io>2015-12-18 07:46:48 +0100
committerVo Minh Thu <thu@hypered.io>2015-12-18 07:46:48 +0100
commitc26b53ba48e5b01c817cf2219cfecb62ee16559b (patch)
tree09ed1d6519e43d4966856f39ef383d70f0880e76 /README.md
Initial commit.
Diffstat (limited to 'README.md')
-rw-r--r--README.md173
1 files changed, 173 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c101c43
--- /dev/null
+++ b/README.md
@@ -0,0 +1,173 @@
1# Let's Encrypt ACME protocol
2
3This is a simple Haskell script to obtain a certificate from [Let's
4Encrypt](https://letsencrypt.org/) using their ACME protocol.
5
6
7- The main source of information to write this was
8 https://github.com/diafygi/letsencrypt-nosudo
9
10- The ACME spec: https://letsencrypt.github.io/acme-spec/
11
12Most values are still hard-coded for my initial attempt (i.e. my email address
13and a domain of mine).
14
15
16## Discover the URL for letsencrypt ACME endpoints
17
18API endpoints are listed at https://acme-v01.api.letsencrypt.org/directory and
19are 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
33
34You need an account with Let's Encrypt to ask and receive certificates for your
35domains. The account is controlled by a public/private key pair:
36
37```
38openssl genrsa 4096 > user.key
39openssl rsa -in user.key -pubout > user.pub
40```
41
42
43## Generate nonces
44
45
46Each request to the API have a nonce to prevent replays. The nonce is currently
47hard-coded in the script. New nonces can be obtained from letsencrypt with
48
49```
50> generate-nonce.sh
51```
52
53
54## Create user account
55
56Generate `registration.body` by using the `acme.hs` script then POST it to
57letsencrypt (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
82Let's Encrypt needs a proof that you control the claimed domain. You can
83request 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```
119
120The script assumes you'll answer the challenge by hosting a file at a location
121chosen by Let's Encrypt. Extract the token for the `http-01` challenge and run
122the script again. Now you have to host the file at the location reported by the
123script.
124
125Once this is done, you can ask Let's Encrypt to check the file.
126
127```
128> curl -s -X POST --data-binary "@challenge-response.body" \
129 https://acme-v01.api.letsencrypt.org/acme/challenge/vXZ1UnZ-y1q7sntnf6NdOfbPAwetJFBqOtvp7FHCjaU/1844048 | json_pp
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```
138
139The same URL can then be polled until the status becomes valid.
140
141
142## Send CSR / Receive certificate
143
144The CSR is created with:
145
146```
147> openssl genrsa 4096 > domain.key
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```
151
152And 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
161
162Including explicit DH key exchange parameters to prevent Logjam attack
163(https://weakdh.org/).
164
165```
166> openssl x509 -inform der -in aaa.reesd.com.cert.der \
167 -out aaa.reesd.com.cert.pem
168> openssl dhparam -out aaa.reesd.com-dhparams.pem 2048
169> cat aaa.reesd.com.cert.pem \
170 lets-encrypt-x1-cross-signed.pem \
171 aaa.reesd.com.key \
172 aaa.reesd.com-dhparams.pem > aaa.reesd.com-combined.pem
173```