From 3d8d3a9237cd2e81c594c63c04ef1bd3d7aedccd Mon Sep 17 00:00:00 2001 From: shortcutme Date: Wed, 27 Mar 2019 03:08:37 +0100 Subject: [PATCH] Randomize SSL subject (merged ValdikSS's commit) --- src/Crypt/CryptConnection.py | 94 ++++++++++++++++++++++++++++++------ src/lib/openssl/openssl.cnf | 24 +++------ 2 files changed, 86 insertions(+), 32 deletions(-) diff --git a/src/Crypt/CryptConnection.py b/src/Crypt/CryptConnection.py index 4420ed0f..68205cbb 100644 --- a/src/Crypt/CryptConnection.py +++ b/src/Crypt/CryptConnection.py @@ -3,6 +3,7 @@ import logging import os import ssl import hashlib +import random from Config import config from util import helper @@ -12,13 +13,19 @@ class CryptConnectionManager: def __init__(self): # OpenSSL params if sys.platform.startswith("win"): - self.openssl_bin = "src\\lib\\opensslVerify\\openssl.exe" + self.openssl_bin = "dist\\openssl\\openssl.exe" else: self.openssl_bin = "openssl" self.openssl_env = {"OPENSSL_CONF": "src/lib/openssl/openssl.cnf"} self.crypt_supported = [] # Supported cryptos + self.cacert_pem = config.data_dir + "/cacert-rsa.pem" + self.cakey_pem = config.data_dir + "/cakey-rsa.pem" + self.cert_pem = config.data_dir + "/cert-rsa.pem" + self.cert_csr = config.data_dir + "/cert-rsa.csr" + self.key_pem = config.data_dir + "/key-rsa.pem" + # Select crypt that supported by both sides # Return: Name of the crypto def selectCrypt(self, client_supported): @@ -35,8 +42,9 @@ class CryptConnectionManager: ciphers += "!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK" if server: sock_wrapped = ssl.wrap_socket( - sock, server_side=server, keyfile='%s/key-rsa.pem' % config.data_dir, - certfile='%s/cert-rsa.pem' % config.data_dir, ciphers=ciphers) + sock, server_side=server, keyfile=self.key_pem, + certfile=self.cert_pem, ciphers=ciphers + ) else: sock_wrapped = ssl.wrap_socket(sock, ciphers=ciphers) if cert_pin: @@ -49,7 +57,7 @@ class CryptConnectionManager: def removeCerts(self): if config.keep_ssl_cert: return False - for file_name in ["cert-rsa.pem", "key-rsa.pem"]: + for file_name in ["cert-rsa.pem", "key-rsa.pem", "cacert-rsa.pem", "cakey-rsa.pem", "cacert-rsa.srl", "cert-rsa.csr"]: file_path = "%s/%s" % (config.data_dir, file_name) if os.path.isfile(file_path): os.unlink(file_path) @@ -65,28 +73,86 @@ class CryptConnectionManager: # Try to create RSA server cert + sign for connection encryption # Return: True on success def createSslRsaCert(self): - if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir): + casubjects = [ + "/C=US/O=Amazon/OU=Server CA 1B/CN=Amazon", + "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3", + "/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA", + "/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA" + ] + fakedomains = [ + "yahoo.com", "amazon.com", "live.com", "microsoft.com", "mail.ru", "csdn.net", "bing.com", + "amazon.co.jp", "office.com", "imdb.com", "msn.com", "samsung.com", "huawei.com", "ztedevices.com", + "godaddy.com", "w3.org", "gravatar.com", "creativecommons.org", "hatena.ne.jp", + "adobe.com", "opera.com", "apache.org", "rambler.ru", "one.com", "nationalgeographic.com", + "networksolutions.com", "php.net", "python.org", "phoca.cz", "debian.org", "ubuntu.com", + "nazwa.pl", "symantec.com" + ] + self.openssl_env['CN'] = random.choice(fakedomains) + + if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem): return True # Files already exits import subprocess - cmd = "%s req -x509 -newkey rsa:2048 -sha256 -batch -keyout %s -out %s -nodes -config %s" % helper.shellquote( + + # Generate CAcert and CAkey + cmd_params = helper.shellquote( self.openssl_bin, - config.data_dir + "/key-rsa.pem", - config.data_dir + "/cert-rsa.pem", - self.openssl_env["OPENSSL_CONF"] + self.openssl_env["OPENSSL_CONF"], + random.choice(casubjects), + self.cakey_pem, + self.cacert_pem ) + cmd = "%s req -new -newkey rsa:2048 -days 3650 -nodes -x509 -config %s -subj %s -keyout %s -out %s -batch" % cmd_params proc = subprocess.Popen( cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=self.openssl_env ) back = proc.stdout.read().strip() proc.wait() - logging.debug("Generating RSA cert and key PEM files...%s" % back) + logging.debug("Generating RSA CAcert and CAkey PEM files... %s: %s" % (cmd, back.decode())) - if os.path.isfile("%s/cert-rsa.pem" % config.data_dir) and os.path.isfile("%s/key-rsa.pem" % config.data_dir): - return True - else: - logging.error("RSA SSL cert generation failed, cert or key files not exist.") + if not (os.path.isfile(self.cacert_pem) and os.path.isfile(self.cakey_pem)): + logging.error("RSA ECC SSL CAcert generation failed, CAcert or CAkey files not exist.") return False + # Generate certificate key and signing request + cmd_params = helper.shellquote( + self.openssl_bin, + self.key_pem, + self.cert_csr, + "/CN=" + self.openssl_env['CN'], + self.openssl_env["OPENSSL_CONF"], + ) + cmd = "%s req -new -newkey rsa:2048 -keyout %s -out %s -subj %s -sha256 -nodes -batch -config %s" % cmd_params + proc = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=self.openssl_env + ) + back = proc.stdout.read().strip() + proc.wait() + logging.debug("Generating certificate key and signing request...%s" % back.decode()) + + # Sign request and generate certificate + cmd_params = helper.shellquote( + self.openssl_bin, + self.cert_csr, + self.cacert_pem, + self.cakey_pem, + self.cert_pem, + self.openssl_env["OPENSSL_CONF"] + ) + cmd = "%s x509 -req -in %s -CA %s -CAkey %s -CAcreateserial -out %s -days 730 -sha256 -extensions x509_ext -extfile %s" % cmd_params + proc = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=self.openssl_env + ) + back = proc.stdout.read().strip() + proc.wait() + logging.debug("Generating RSA cert...%s" % back.decode()) + + if os.path.isfile(self.cert_pem) and os.path.isfile(self.key_pem): + return True + else: + logging.error("RSA ECC SSL cert generation failed, cert or key files not exist.") + manager = CryptConnectionManager() diff --git a/src/lib/openssl/openssl.cnf b/src/lib/openssl/openssl.cnf index 06087bdd..1c1ec47f 100644 --- a/src/lib/openssl/openssl.cnf +++ b/src/lib/openssl/openssl.cnf @@ -1,6 +1,5 @@ [ req ] -prompt = no -default_bits = 1024 +default_bits = 2048 default_keyfile = server-key.pem distinguished_name = subject req_extensions = req_ext @@ -13,7 +12,7 @@ string_mask = utf8only countryName = US stateOrProvinceName = NY localityName = New York -organizationName = Example Company, LLC +organizationName = Example, LLC # Use a friendly name here because its presented to the user. The server's DNS # names are placed in Subject Alternate Names. Plus, DNS names here is deprecated @@ -32,8 +31,8 @@ authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth subjectAltName = @alternate_names -nsComment = "OpenSSL Generated Certificate" # RFC 5280, Section 4.2.1.12 makes EKU optional # CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused @@ -46,8 +45,8 @@ subjectKeyIdentifier = hash basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth subjectAltName = @alternate_names -nsComment = "OpenSSL Generated Certificate" # RFC 5280, Section 4.2.1.12 makes EKU optional # CA/Browser Baseline Requirements, Appendix (B)(3)(G) makes me confused @@ -55,16 +54,5 @@ nsComment = "OpenSSL Generated Certificate" [ alternate_names ] -DNS.1 = example.company.com -DNS.2 = www.example.company.com -DNS.3 = mail.example.company.com -DNS.4 = ftp.example.company.com - -# Add these if you need them. But usually you don't want them or -# need them in production. You may need them for development. -# DNS.5 = localhost -# DNS.6 = localhost.localdomain -# DNS.7 = 127.0.0.1 - -# IPv6 localhost -# DNS.8 = ::1 \ No newline at end of file +DNS.1 = $ENV::CN +DNS.2 = www.$ENV::CN \ No newline at end of file