|
#!/usr/bin/python
|
|
import os,sys,argparse,subprocess,shutil,time
|
|
import json,base64,binascii
|
|
pki_directory = '/etc/keystore'
|
|
|
|
def letsencrypt_key_convert(file):
|
|
with open(file) as fp:
|
|
pkey=json.load(fp)
|
|
def enc(data):
|
|
missing_padding = 4 - len(data) % 4
|
|
if missing_padding:
|
|
data += b'='* missing_padding
|
|
return b'0x'+binascii.hexlify(base64.b64decode(data,b'-_')).upper()
|
|
for k,v in pkey.items():
|
|
if k == 'kty': continue
|
|
pkey[k] = enc(v.encode()).decode()
|
|
|
|
res = "asn1=SEQUENCE:private_key\n[private_key]\nversion=INTEGER:0\n"
|
|
res += "n=INTEGER:{}\n".format(pkey[u'n'])
|
|
res += "e=INTEGER:{}\n".format(pkey[u'e'])
|
|
res += "d=INTEGER:{}\n".format(pkey[u'd'])
|
|
res += "p=INTEGER:{}\n".format(pkey[u'p'])
|
|
res += "q=INTEGER:{}\n".format(pkey[u'q'])
|
|
res += "dp=INTEGER:{}\n".format(pkey[u'dp'])
|
|
res += "dq=INTEGER:{}\n".format(pkey[u'dq'])
|
|
res += "qi=INTEGER:{}\n".format(pkey[u'qi'])
|
|
return res
|
|
|
|
|
|
def pki_directory_migrate():
|
|
os.mkdir(pki_directory)
|
|
os.mkdir('%s/live' % (pki_directory))
|
|
os.mkdir('%s/backups' % (pki_directory))
|
|
os.mkdir('%s/accounts' % (pki_directory))
|
|
if os.path.isdir('/etc/letsencrypt/letsencrypt/accounts'):
|
|
for x in os.listdir('/etc/letsencrypt/letsencrypt/accounts'):
|
|
for xx in os.listdir('/etc/letsencrypt/letsencrypt/accounts/%s/directory/' % (x)):
|
|
if os.path.isfile('/etc/letsencrypt/letsencrypt/accounts/%s/directory/%s/private_key.json' % (x, xx)):
|
|
with open('%s/accounts/%s.tmp' % (pki_directory, x), 'w') as f:
|
|
f.write(letsencrypt_key_convert('/etc/letsencrypt/letsencrypt/accounts/%s/directory/%s/private_key.json' % (x, xx)))
|
|
subprocess.check_call(['openssl', 'asn1parse', '-noout', '-out', '%s/accounts/%s.der' % (pki_directory, x), '-genconf', '%s/accounts/%s.tmp' % (pki_directory, x)])
|
|
subprocess.check_call(['openssl', 'rsa', '-in', '%s/accounts/%s.der' % (pki_directory, x), '-out', '%s/accounts/%s.key' % (pki_directory, x), '-inform', 'der', '-outform', 'pem'])
|
|
os.remove('%s/accounts/%s.tmp' % (pki_directory, x))
|
|
os.remove('%s/accounts/%s.der' % (pki_directory, x))
|
|
os.chmod('%s/accounts/%s.key' % (pki_directory, x), 0o400)
|
|
|
|
def split_certificate(chain, cert, issuer):
|
|
with open(chain, 'r') as chainfile:
|
|
with open(cert, 'w') as certfile:
|
|
with open(issuer, 'w') as issuerfile:
|
|
first=True
|
|
for l in chainfile.readlines():
|
|
if '\n' == l:
|
|
first = False
|
|
continue
|
|
if first:
|
|
certfile.write(l)
|
|
else:
|
|
issuerfile.write(l)
|
|
|
|
if '__main__' == __name__:
|
|
parser = argparse.ArgumentParser();
|
|
parser.add_argument('-d', '--domain', help='set domain', dest='domain', metavar='DOMAIN', type=str, nargs=1, required=True)
|
|
parser.add_argument('-e', '--other-domains', help='set other domains', dest='other_domain', metavar='OTHER_DOMAIN', type=str, nargs=1, required=False)
|
|
parser.add_argument('-c', help='create pki_directory structure', dest='create', action='store_true')
|
|
parser.add_argument('-l', help='letsencrypt signature', dest='letsencrypt', action='store_true')
|
|
parser.add_argument('--rsa', help='generate rsa key as well as ecdsa', dest='rsa', action='store_true')
|
|
args = parser.parse_args();
|
|
|
|
if args.create:
|
|
pki_directory_migrate()
|
|
sys.exit(0)
|
|
if not os.path.isdir(pki_directory):
|
|
print('Error: The directory structure does not exists', file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# create a temp directory
|
|
tmpdir = '%s/tmp' % (pki_directory)
|
|
try:
|
|
shutil.rmtree(tmpdir)
|
|
except:
|
|
pass
|
|
os.mkdir(tmpdir)
|
|
|
|
# create the keys
|
|
if args.rsa:
|
|
subprocess.check_call(['openssl', 'genrsa', '-out', '%s/key.rsa.pem' % (tmpdir), '2048'])
|
|
subprocess.check_call(['openssl', 'ecparam', '-out', '%s/key.ecc.pem' % (tmpdir), '-name', 'prime256v1', '-genkey'])
|
|
|
|
# create csr
|
|
o = ['DNS:%s' % (args.domain[0])]
|
|
if args.other_domain:
|
|
for d in args.other_domain[0].split(','):
|
|
o.append('DNS:%s' % (d))
|
|
if args.rsa:
|
|
subprocess.check_call(['bash', '-c', 'openssl req -new -key "%s/key.rsa.pem" -out "%s/csr.rsa.pem" -subj "/O=confais.org/CN=%s" -reqexts cert -config <(cat /etc/ssl/openssl.cnf <(printf "[ cert ]\nsubjectAltName=%s"))' % (tmpdir, tmpdir, args.domain[0], ','.join(o))])
|
|
subprocess.check_call(['bash', '-c', 'openssl req -new -key "%s/key.ecc.pem" -out "%s/csr.ecc.pem" -subj "/O=confais.org/CN=%s" -reqexts cert -config <(cat /etc/ssl/openssl.cnf <(printf "[ cert ]\nsubjectAltName=%s"))' % (tmpdir, tmpdir, args.domain[0], ','.join(o))])
|
|
|
|
# sign the certificate
|
|
if args.letsencrypt:
|
|
if args.rsa:
|
|
with open('%s/chain.rsa.pem' % (tmpdir), 'w') as chainfile:
|
|
p = subprocess.Popen(['acme-tiny', '--account-key', '%s/accounts/acme-v01.api.letsencrypt.org.key' % (pki_directory), '--csr', '%s/csr.rsa.pem' % (tmpdir), '--acme-dir', '/srv/http/.well-known/acme-challenge/'], stdout=chainfile)
|
|
p.wait()
|
|
with open('%s/chain.ecc.pem' % (tmpdir), 'w') as chainfile:
|
|
p = subprocess.Popen(['acme-tiny', '--account-key', '%s/accounts/acme-v01.api.letsencrypt.org.key' % (pki_directory), '--csr', '%s/csr.ecc.pem' % (tmpdir), '--acme-dir', '/srv/http/.well-known/acme-challenge/'], stdout=chainfile)
|
|
p.wait()
|
|
else:
|
|
# no letsencrypt signature
|
|
input("Copy chain.ecc.pem or/and chain.rsa.pem to continue...")
|
|
|
|
time.sleep(15)
|
|
|
|
# split the chain into cert and issuer
|
|
if args.rsa:
|
|
split_certificate('%s/chain.rsa.pem' % (tmpdir), '%s/cert.rsa.pem' % (tmpdir), '%s/issuer.rsa.pem' % (tmpdir))
|
|
split_certificate('%s/chain.ecc.pem' % (tmpdir), '%s/cert.ecc.pem' % (tmpdir), '%s/issuer.ecc.pem' % (tmpdir))
|
|
|
|
# certificate transparency
|
|
# if args.letsencrypt:
|
|
# if args.rsa:
|
|
# subprocess.check_call(['bash', '-c', 'ct-submit ct.googleapis.com/pilot < %s/chain.rsa.pem > %s/ct.rsa.sct' % (tmpdir, tmpdir)])
|
|
# subprocess.check_call(['bash', '-c', 'ct-submit ct.googleapis.com/pilot < %s/chain.ecc.pem > %s/ct.ecc.sct' % (tmpdir, tmpdir)])
|
|
if args.letsencrypt:
|
|
if args.rsa:
|
|
subprocess.check_call(['bash', '-c', 'ct-submit ct.cloudflare.com/logs/nimbus2021 < %s/chain.rsa.pem > %s/ct.rsa.sct' % (tmpdir, tmpdir)])
|
|
subprocess.check_call(['bash', '-c', 'ct-submit ct.cloudflare.com/logs/nimbus2021 < %s/chain.ecc.pem > %s/ct.ecc.sct' % (tmpdir, tmpdir)])
|
|
|
|
|
|
|
|
# dh parameters
|
|
#subprocess.check_call(['openssl', 'dhparam', '-dsaparam', '-out', '%s/dh.pem' % (tmpdir), '2048'])
|
|
with open("%s/dh.pem" % (tmpdir), "w") as f:
|
|
f.write("-----BEGIN DH PARAMETERS-----\n")
|
|
f.write("MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz\n")
|
|
f.write("+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a\n")
|
|
f.write("87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7\n")
|
|
f.write("YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi\n")
|
|
f.write("7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD\n")
|
|
f.write("ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3\n")
|
|
f.write("7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32\n")
|
|
f.write("nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e\n")
|
|
f.write("8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx\n")
|
|
f.write("iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K\n")
|
|
f.write("zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=\n")
|
|
f.write("-----END DH PARAMETERS-----\n")
|
|
|
|
# permissions
|
|
if args.rsa:
|
|
os.chmod('%s/key.rsa.pem' % (tmpdir), 0o400)
|
|
os.chmod('%s/csr.rsa.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/chain.rsa.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/cert.rsa.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/issuer.rsa.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/key.ecc.pem' % (tmpdir), 0o400)
|
|
os.chmod('%s/csr.ecc.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/chain.ecc.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/cert.ecc.pem' % (tmpdir), 0o444)
|
|
os.chmod('%s/issuer.ecc.pem' % (tmpdir), 0o444)
|
|
if(os.path.isfile('%s/ct.rsa.sct' % (tmpdir))):
|
|
os.chmod('%s/ct.rsa.sct' % (tmpdir), 0o444)
|
|
if(os.path.isfile('%s/ct.ecc.sct' % (tmpdir))):
|
|
os.chmod('%s/ct.ecc.sct' % (tmpdir), 0o444)
|
|
os.chmod('%s/dh.pem' % (tmpdir), 0o444)
|
|
|
|
# move files
|
|
try:
|
|
shutil.rmtree('%s/backups/%s' % (pki_directory, args.domain[0]))
|
|
except:
|
|
pass
|
|
if os.path.isdir('%s/live/%s' % (pki_directory, args.domain[0])):
|
|
shutil.move('%s/live/%s' % (pki_directory, args.domain[0]), '%s/backups/%s' % (pki_directory, args.domain[0]))
|
|
shutil.move('%s/tmp/' % (pki_directory), '%s/live/%s' % (pki_directory, args.domain[0]))
|
|
|
|
|
|
# display infos
|
|
if args.rsa:
|
|
ret_rsa = subprocess.check_output(['ldns-dane', '-n', '-c', '%s/live/%s/cert.rsa.pem' % (pki_directory, args.domain[0]), 'create', args.domain[0], '443', '3', '1', '2' ])
|
|
ret_ecc = subprocess.check_output(['ldns-dane', '-n', '-c', '%s/live/%s/cert.ecc.pem' % (pki_directory, args.domain[0]), 'create', args.domain[0], '443', '3', '1', '2' ])
|
|
print('')
|
|
print('========')
|
|
|
|
print('TLSA:')
|
|
if args.rsa:
|
|
print(ret_rsa.decode(), end='')
|
|
print(ret_ecc.decode())
|
|
|
|
print('')
|
|
|
|
if args.rsa:
|
|
ret_rsa = subprocess.check_output(['openssl', 'x509', '-in', '%s/live/%s/cert.rsa.pem' % (pki_directory, args.domain[0]), '-sha1', '-noout', '-fingerprint'])
|
|
ret_ecc = subprocess.check_output(['openssl', 'x509', '-in', '%s/live/%s/cert.ecc.pem' % (pki_directory, args.domain[0]), '-sha1', '-noout', '-fingerprint'])
|
|
print('SHA1:')
|
|
if args.rsa:
|
|
print(ret_rsa.decode(), end='')
|
|
print(ret_ecc.decode())
|
|
|
|
print('')
|
|
print('Do not forget to copy files on the frontend reverse proxy!')
|