#!/usr/bin/env python """ Adapted from the docs of cryptography Creates a key and self-signed certificate for local use """ import datetime import os import socket # noinspection PyPackageRequirements from cryptography.hazmat.backends import default_backend # noinspection PyPackageRequirements from cryptography.hazmat.primitives import hashes, serialization # noinspection PyPackageRequirements from cryptography.hazmat.primitives.asymmetric import rsa # noinspection PyPackageRequirements from cryptography import x509 # noinspection PyPackageRequirements from cryptography.x509.oid import NameOID from six import PY2, text_type def localipv4(): try: s_ipv4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s_ipv4.connect(('1.2.3.4', 80)) # Option: use 100.64.1.1 (IANA-Reserved IPv4 Prefix for Shared Address Space) ipv4 = s_ipv4.getsockname()[0] s_ipv4.close() except (BaseException, Exception): ipv4 = None return ipv4 # Ported from cryptography/utils.py def int_from_bytes(data, byteorder, signed=False): assert 'big' == byteorder assert not signed if not PY2: import binascii return int(binascii.hexlify(data), 16) # call bytes() on data to allow the use of bytearrays # noinspection PyUnresolvedReferences return int(bytes(data).encode('hex'), 16) # Ported from cryptography/x509/base.py def random_serial_number(): return int_from_bytes(os.urandom(20), 'big') >> 1 # Ported from cryptography docs/x509/tutorial.rst (set with no encryption) def generate_key(key_size=4096, output_file='server.key'): # Generate our key private_key = rsa.generate_private_key( public_exponent=65537, key_size=key_size, backend=default_backend() ) # Write our key to disk for safe keeping with open(output_file, 'wb') as f: f.write(private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption() )) return private_key # Ported from cryptography docs/x509/tutorial.rst def generate_local_cert(private_key, days_valid=3650, output_file='server.crt', loc_name=None, org_name=None): def_name = 'SickGear' # Various details about who we are. For a self-signed certificate the # subject and issuer are always the same. subject = issuer = x509.Name([ x509.NameAttribute(NameOID.LOCALITY_NAME, loc_name or def_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, org_name or def_name) ]) # build Subject Alternate Names (aka SAN) list # First the host names, add with x509.DNSName(): san_list = [x509.DNSName('localhost')] try: thishostname = text_type(socket.gethostname()) san_list.append(x509.DNSName(thishostname)) except (BaseException, Exception): pass # Then the host IP addresses, add with x509.IPAddress() # Inside a try-except, just to be sure try: # noinspection PyCompatibility from ipaddress import IPv4Address, IPv6Address san_list.append(x509.IPAddress(IPv4Address('127.0.0.1'))) san_list.append(x509.IPAddress(IPv6Address('::1'))) # append local v4 ip mylocalipv4 = localipv4() if mylocalipv4: san_list.append(x509.IPAddress(IPv4Address('' + mylocalipv4))) except (ImportError, Exception): pass cert = x509.CertificateBuilder() \ .subject_name(subject) \ .issuer_name(issuer) \ .public_key(private_key.public_key()) \ .not_valid_before(datetime.datetime.utcnow()) \ .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=days_valid)) \ .serial_number(random_serial_number()) \ .add_extension(x509.SubjectAlternativeName(san_list), critical=True) \ .sign(private_key, hashes.SHA256(), default_backend()) # Write the certificate out to disk. with open(output_file, 'wb') as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) return cert