# -*- coding: utf-8 -*- import bcrypt import datetime import os from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID from . import config from .paths import ACME_CRON, CERT_PUB_FILE, CERT_KEY_FILE def create_selfsigned_cert(): # Create selfsigned certificate with wildcard alternative subject name domain = config.get_host()['domain'] private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) public_key = private_key.public_key() subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, domain)]) now = datetime.datetime.utcnow() cert = x509.CertificateBuilder() \ .subject_name(subject) \ .issuer_name(subject) \ .public_key(public_key) \ .serial_number(x509.random_serial_number()) \ .not_valid_before(now) \ .not_valid_after(now + datetime.timedelta(days=7305)) \ .add_extension(x509.SubjectAlternativeName((x509.DNSName(domain), x509.DNSName(f'*.{domain}'))), critical=False) \ .add_extension(x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False) \ .add_extension(x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key), critical=False) \ .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True) \ .add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True) \ .add_extension(x509.ExtendedKeyUsage((ExtendedKeyUsageOID.SERVER_AUTH, ExtendedKeyUsageOID.CLIENT_AUTH)), critical=False) \ .sign(private_key, hashes.SHA256(), default_backend()) with open(CERT_PUB_FILE, 'wb') as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) with open(CERT_KEY_FILE, 'wb') as f: f.write(private_key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())) os.chmod(CERT_KEY_FILE, 0o600) def get_cert_info(): # Gather certificate data important for setup-host with open(CERT_PUB_FILE, 'rb') as f: cert = x509.load_pem_x509_certificate(f.read(), default_backend()) data = {'subject': cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value, 'issuer': cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value, 'expires': f'{cert.not_valid_after} UTC', 'method': 'manual'} if os.access(ACME_CRON, os.X_OK): data['method'] = 'automatic' # Naive method of inferring if the cert is selfsigned # Good enough as reputable CAs will never have the same subject and issuer CN # and the 'method' field is used just to populate a GUI element and not for any real cryptography elif data['subject'] == data['issuer']: data['method'] = 'selfsigned' return data def adminpwd_hash(password): return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() def adminpwd_verify(password): return bcrypt.checkpw(password.encode(), config.get_host()['adminpwd'].encode())