convert / dehydrated / certbot / letsencrypt config
If you find yourself in the situation that you have to reuse your Letsencrypt credentials/account generated by Dehydrated (a bash Letsencrypt interface) with the official Certbot client, like me, you’ll want to convert your config files.
In my case, I wanted to change my e-mail address, and the Dehydrated client offered no such command. With Certbot you can do this:
$ certbot register --update-registration --account f65c...
But you’ll need your credentials in a format that Certbot groks.
With a bit of trial and error, you can came a long way converting the files:
$ ls /.../dehydrated/accounts/ACCOUNT
account_key.pem registration_info.json
$ mkdir -p /etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/ACCOUNT
$ cd /etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory/ACCOUNT
$ cat >meta.json <<EOF
{"creation_host": "my.example.com", "creation_dt": "2016-12-20T14:12:31Z"}
EOF
If you have a sample Certbot regr.json
, you’ll figure out what to
place there based on the contents of the Dehydrated
registration_info.json
.
registration_info.json
:
{
"id": ACCOUNT_NUMBER,
"key": {
"kty": "RSA",
"n": "MODULUS_IN_BASE64",
"e": "EXPONENT_IN_BASE64"
},
"contact": [],
"agreement": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf",
"initialIp": "IP_ADDRESS",
"createdAt": "2016-12-20T14:12:31.054249908Z",
"Status": "valid"
}
regr.json
:
{
"body": {
"contact": [],
"agreement": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf",
"key": {
"kty": "RSA",
"n": "MODULUS_IN_BASE64",
"e": "EXPONENT_IN_BASE64"
}
},
"uri": "https://acme-v01.api.letsencrypt.org/acme/reg/ACCOUNT_NUMBER",
"new_authzr_uri": "https://acme-v01.api.letsencrypt.org/acme/new-authz",
"terms_of_service": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
}
Lastly, you’ll need the Certbot private_key.json
. It can be
converted from Dehydrated account_key.pem
, with the following
rsapem2json.py
python snippet:
#!/usr/bin/env python
# Usage: openssl rsa -in account_key.pem -text -noout | python rsapem2json.py
# Will convert the RSA PEM private key to the Letsencrypt/Certbot
# private_key.json file.
#
# Public Domain, Walter Doekes, OSSO B.V., 2016
#
# From:
# -----BEGIN RSA PRIVATE KEY-----
# MIIJJwsdAyjCseEAtNsljpkjhk9143w//jVdsfWsdf9sffLgdsf+sefdfsgE54km
# ...
#
# To:
# {"e": "AQAB",
# "n": "2YIitsUxJlYn_rVn_8Sges...",
# ...
#
from base64 import b64encode
from sys import stdin
maps = {
'modulus': 'n', 'privateExponent': 'd', 'prime1': 'p', 'prime2': 'q',
'coefficient': 'qi', 'exponent1': 'dp', 'exponent2': 'dq'}
extra = {'kty': 'RSA', 'e': '<publicExponent>'}
def block2b64(lines, key):
found = False
chars = []
for line in lines:
if line.startswith(key + ':'):
found = True
elif found and line.startswith(' '):
for i in line.split(':'):
i = i.strip()
if i:
chars.append(chr(int(i, 16)))
elif found:
break
assert chars, 'nothing found for {0}'.format(key)
return b64encode(''.join(chars))
data = stdin.read().split('\n')
conv = dict((v, block2b64(data, k)) for k, v in maps.items())
conv.update(extra)
# Add exponent
e = [i for i in data if i.startswith('publicExponent:')][0]
e = e.split('(', 1)[-1].split(')', 1)[0]
assert e.startswith('0x'), e
e = ('', '0')[len(e) % 2 == 1] + e[2:]
e = b64encode(''.join(chr(int(e[i:i+2], 16)) for i in range(0, len(e), 2)))
conv['e'] = e
# JSON-safe output.
print(repr(conv).replace("'", '"'))
Don’t forget to chmod 400
the private_key.json
.