SickGear/lib/certgen.py

128 lines
4.1 KiB
Python
Raw Normal View History

#!/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