Source code for tlspyo.credentials
from pathlib import Path
import datetime
import socket
from platformdirs import user_data_dir
from OpenSSL import crypto
__docformat__ = "google"
DEFAULT_KEYS_FOLDER = Path(user_data_dir()) / "tlspyo" / "credentials"
[docs]
def get_default_keys_folder():
"""
Creates the default credentials directory and returns it.
Returns:
pathlib.Path: default credentials directory
"""
if not DEFAULT_KEYS_FOLDER.exists():
DEFAULT_KEYS_FOLDER.mkdir(parents=True, exist_ok=True)
return DEFAULT_KEYS_FOLDER
[docs]
def generate_tls_credentials(
folder_path,
email_address="emailAddress",
common_name="default",
subject_alt_name=('DNS:default',),
country_name="CA",
locality_name="localityName",
state_or_province_name="stateOrProvinceName",
organization_name="organizationName",
organization_unit_name="organizationUnitName",
serial_number=0,
validity_end_in_seconds=10*365*24*60*60):
"""
Generates a private TLS key and a self-signed TLS certificate in the designed folder.
Args:
folder_path (path-like object): path were the files will be created
email_address (str): your email address
common_name (str): your hostname
subject_alt_name (tuple of str): your subject alt name list
country_name (str): your country code
locality_name (str): your locality name
state_or_province_name (str): your state name
organization_name (str): your organization name
organization_unit_name (str): your organization unit name
serial_number (int): the serial number of your certificate
validity_end_in_seconds (int): seconds until the generated certificate will expire
"""
key_file = Path(folder_path) / "key.pem"
cert_file = Path(folder_path) / "certificate.pem"
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 4096)
cert = crypto.X509()
subject = cert.get_subject()
subject.commonName = common_name
subject.emailAddress = email_address
subject.organizationName = organization_name
subject.organizationalUnitName = organization_unit_name
subject.localityName = locality_name
subject.stateOrProvinceName = state_or_province_name
subject.countryName = country_name
cert.set_issuer(subject)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(validity_end_in_seconds)
cert.set_pubkey(k)
cert.set_serial_number(serial_number)
cert.set_version(2) # for SAN
cert.add_extensions([
crypto.X509Extension(b'subjectAltName', False, ','.join(subject_alt_name).encode())
])
cert.sign(k, 'sha512')
with open(cert_file, "wt") as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8"))
with open(key_file, "wt") as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8"))
[docs]
def credentials_generator_tool(custom=False):
"""
Helper tool to generate credentials via CLI.
Args:
custom (bool): whether to customize the certificate
"""
folder_path = get_default_keys_folder()
email_address = "emailAddress"
common_name = "default"
subject_alt_name = ["DNS:" + common_name]
country_name = "CA"
locality_name = "localityName"
state_or_province_name = "stateOrProvinceName"
organization_name = "organizationName"
organization_unit_name = "organizationUnitName"
serial_number = 0
validity_end_in_seconds = 10 * 365 * 24 * 60 * 60
if custom:
print(f"=== TLSPYO - TLS credentials generation tool ===")
print(f"Please fill the following fields (press ENTER fo leave the default as displayed between brackets)")
print(f"\nCredentials folder [{folder_path}]:")
inp = input()
if inp != "":
folder_path = Path(inp)
print(folder_path)
print(f"\nEmail address [{email_address}]:")
inp = input()
if inp != "":
email_address = inp
print(email_address)
print(f"\nCommon name (hostname) [{common_name}]:")
inp = input()
if inp != "":
common_name = inp
print(common_name)
subject_alt_name = ["DNS:" + common_name]
print(f"\nSubject alternative name (hostnames, leave empty to stop adding) {subject_alt_name}:")
inp = input()
if inp != "":
subject_alt_name = []
while inp != "":
subject_alt_name.append(inp)
inp = input()
print(subject_alt_name)
print(f"\nCountry code [{country_name}]:")
inp = input()
if inp != "":
country_name = inp
print(country_name)
print(f"\nLocality name [{locality_name}]:")
inp = input()
if inp != "":
locality_name = inp
print(locality_name)
print(f"\nState or province [{state_or_province_name}]:")
inp = input()
if inp != "":
state_or_province_name = inp
print(state_or_province_name)
print(f"\nOrganization name [{organization_name}]:")
inp = input()
if inp != "":
organization_name = inp
print(organization_name)
print(f"\nOrganization unit [{organization_unit_name}]:")
inp = input()
if inp != "":
organization_unit_name = inp
print(organization_unit_name)
print(f"\nCertificate serial number [{serial_number}]:")
inp = input()
if inp != "":
serial_number = inp
print(serial_number)
print(f"\nCertificate validity (in seconds) [{validity_end_in_seconds}]:")
inp = input()
if inp != "":
validity_end_in_seconds = int(inp)
print(datetime.timedelta(seconds=validity_end_in_seconds))
generate_tls_credentials(folder_path=folder_path,
email_address=email_address,
common_name=common_name,
subject_alt_name=tuple(subject_alt_name),
country_name=country_name,
locality_name=locality_name,
state_or_province_name=state_or_province_name,
organization_name=organization_name,
organization_unit_name=organization_unit_name,
serial_number=serial_number,
validity_end_in_seconds=validity_end_in_seconds)
print(f"Credentials successfully generated in {folder_path}")
[docs]
def tcp_broadcast_tls_credentials(port, directory=None):
"""
Starts a server that broadcasts certificate.pem over TCP.
Args:
port: port
directory: directory where to find certificate.pem if not the default credential directory
"""
folder = get_default_keys_folder() if directory is None else directory
cert_path = folder / 'certificate.pem'
if not cert_path.exists():
print(f"certificate.pem file not found in {folder}, please generate it first.")
print("You can generate a certificate.pem in the default tlspyo directory with the following command:")
print("python -m tlspyo --generate")
return
else:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as com_srv:
com_srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
com_srv.bind(('', port))
com_srv.listen()
print("Starting credentials server, close the terminal to exit.")
while True:
try:
conn, addr = com_srv.accept()
with conn:
with open(cert_path, 'rb') as f:
conn.sendfile(f)
print(f"Sent TLS certificate to {addr} via port {port}.")
print(f"Connection closed.")
except KeyboardInterrupt:
break
[docs]
def tcp_retrieve_tls_credentials(ip, port, directory=None):
"""
Starts a client that retrieves certificate.pem over TCP.
The credential-broadcasting server must be launched via tcp_broadcast_tls_credentials on the Relay machine.
Args:
ip: ip of the credential-broadcasting server
port: port of the credential-broadcasting server
directory: directory where to write certificate.pem if not the default credentials directory
"""
folder = get_default_keys_folder() if directory is None else directory
cert_path = folder / 'certificate.pem'
print(f"Attempting to reach credentials server at address {ip}:{port}")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((ip, port))
while True:
data = s.recv(1024)
if not data:
break
with open(cert_path, 'wb') as f:
f.write(data)
print(f"TLS certificate correctly received in {cert_path}")