1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00

Started refactoring code - roughly demo ready

This commit is contained in:
James Kasten
2014-11-10 07:30:36 -05:00
parent cbec87e181
commit 1712a024e5
7 changed files with 291 additions and 248 deletions

View File

@@ -10,6 +10,8 @@ BACKUP_DIR = WORK_DIR + "backups/"
TEMP_CHECKPOINT_DIR = WORK_DIR + "temp_checkpoint/"
# Directory used before a permanent checkpoint is finalized
IN_PROGRESS_DIR = BACKUP_DIR + "IN_PROGRESS/"
# Directory where all certificates/keys are stored - used for easy revocation
CERT_KEY_BACKUP = WORK_DIR + "keys-certs/"
# Where all keys should be stored
KEY_DIR = SERVER_ROOT + "ssl/"
# Certificate storage
@@ -28,12 +30,15 @@ APACHE_CHALLENGE_CONF = CONFIG_DIR + "choc_sni_cert_challenge.conf"
S_SIZE = 32
NONCE_SIZE = 16
# Key Sizes
RSA_KEY_SIZE = 2048
# bits of hashcash to generate
difficulty = 23
# Trustify cert and chain files
cert_file = CERT_DIR + "trustify-cert.pem"
chain_file = CERT_DIR + "trustify-chain.pem"
CERT_PATH = CERT_DIR + "trustify-cert.pem"
CHAIN_PATH = CERT_DIR + "trustify-chain.pem"
#Invalid Extension
INVALID_EXT = ".acme.invalid"

View File

@@ -10,7 +10,7 @@ import M2Crypto
import urllib2, json
# XXX TODO: per https://docs.google.com/document/pub?id=1roBIeSJsYq3Ntpf6N0PIeeAAvu4ddn7mGo6Qb7aL7ew, urllib2 is unsafe (!) and must be replaced
import os, grp, pwd, sys, time, random, sys, shutil
import hashlib, binascii, jose, csv
import jose, csv
import subprocess
from M2Crypto import EVP, X509, RSA
from Crypto.Random import get_random_bytes
@@ -23,15 +23,14 @@ from trustify.client.sni_challenge import SNI_Challenge
from trustify.client.payment_challenge import Payment_Challenge
from trustify.client import configurator
from trustify.client import logger
from trustify.client import trustify_util
from trustify.client.CONFIG import NONCE_SIZE, CERT_PATH, CHAIN_PATH
from trustify.client import trustify_util, crypto_util, display
from trustify.client.CONFIG import NONCE_SIZE, RSA_KEY_SIZE, CERT_PATH, CHAIN_PATH
from trustify.client.CONFIG import SERVER_ROOT, KEY_DIR, CERT_DIR, CERT_KEY_BACKUP
from trustify.client.CONFIG import CHALLENGE_PREFERENCES, EXCLUSIVE_CHALLENGES
# it's weird to point to chocolate servers via raw IPv6 addresses, and such
# addresses can be %SCARY in some contexts, so out of paranoia let's disable
# them by default
allow_raw_ipv6_server = False
RSA_KEY_SIZE = 2048
class Client(object):
# In case of import, dialog needs scope over the class
@@ -97,8 +96,6 @@ class Client(object):
challenge_dict = self.is_expected_msg(challenge_dict, "challenge")
#assert self.is_challenge(challenge_dict)
#Perform Challenges
@@ -124,6 +121,7 @@ class Client(object):
self.install_certificate(certificate_dict, vhost)
# Perform optimal config changes
self.optimize_config(vhost)
self.config.save("Completed Augeas Authentication")
@@ -133,20 +131,20 @@ class Client(object):
def revoke(self, c):
x = M2Crypto.X509.load_cert(c["cert_file"])
x = M2Crypto.X509.load_cert(c["backup_cert_file"])
cert_der = x.as_der()
#self.find_key_for_cert()
revocation_dict = self.send(self.revocation_request(cert_der))
revocation_dict = self.send(self.revocation_request(c["backup_key_file"], cert_der))
revocation_dict = self.is_expected_msg(revocation_dict, "revocation")
self.d.msgbox("You have successfully revoked the certificate for %s" % c["cn"], width=70, height=16)
self.remove_cert_key(c["cert_file"], c["key_file"])
self.remove_cert_key(c)
sys.exit(0)
def remove_cert_key(self, c_file, k_file):
def remove_cert_key(self, c):
list_file = CERT_KEY_BACKUP + "LIST"
list_file2 = CERT_KEY_BACKUP + "LIST.tmp"
with open(list_file, 'rb') as orgfile:
@@ -154,12 +152,15 @@ class Client(object):
with open(list_file2, 'wb') as newfile:
csvwriter = csv.writer(newfile)
for row in csvreader:
if not (row[1] == c_file and row[2] == k_file):
if not (row[0] == str(c["idx"]) and row[1] == c["orig_cert_file"] and row[2] == c["orig_key_file"]):
csvwriter.writerow(row)
# remember that these are
shutil.copy2(list_file2, list_file)
os.remove(c_file)
os.remove(k_file)
os.remove(list_file2)
os.remove(c['backup_cert_file'])
os.remove(c['backup_key_file'])
def store_revocation_token(self, token):
return
@@ -202,9 +203,12 @@ class Client(object):
with open(list_file, 'rb') as csvfile:
csvreader = csv.reader(csvfile)
for row in csvreader:
c = trustify_util.get_cert_info(row[1])
c["key_file"] = row[2]
c["cert_file"] = row[1]
c = crypto_util.get_cert_info(row[1])
c["orig_key_file"] = row[2]
c["orig_cert_file"] = row[1]
c["backup_key_file"] = CERT_KEY_BACKUP + os.path.basename(row[2]) + "_" + row[0]
c["backup_cert_file"] = CERT_KEY_BACKUP + os.path.basename(row[1]) + "_" + row[0]
c["idx"] = int(row[0])
certs.append(c)
self.__display_certs(certs)
@@ -219,47 +223,30 @@ class Client(object):
if code == self.d.DIALOG_OK:
self.__confirm_revocation(certs[int(selection)-1])
if display.confirm_revocation(certs[int(selection)-1]):
self.revoke(c)
elif code == self.d.DIALOG_CANCEL:
exit(0)
elif code == "help":
self.__more_info_cert(certs[int(selection)-1])
display.more_info_cert(certs[int(selection)-1])
def __more_info_cert(self, cert):
text = "Certificate Information:\n"
text += "-" * 66
text += trustify_util.cert_info_string(cert)
text += "-" * 66
self.d.msgbox(text, width=70, height=16)
def __confirm_revocation(self, cert):
text = "Are you sure you would like to revoke the following certificate:\n"
text += "-" * 66 + "\n"
text += trustify_util.cert_info_string(cert)
text += "-" * 66
text += "This action cannot be reversed!"
a = self.d.yesno(text, width=70, height=16)
if a == self.d.DIALOG_OK:
self.revoke(cert)
def revocation_request(self, cert_der):
return {"type":"revocationRequest", "certificate":jose.b64encode_url(cert_der), "signature":self.create_sig(cert_der)}
def convert_b64_cert_to_pem(self, b64_der_cert):
x = M2Crypto.X509.load_cert_der_string(jose.b64decode_url(b64_der_cert))
return x.as_pem()
def revocation_request(self, key_file, cert_der):
return {"type":"revocationRequest", "certificate":jose.b64encode_url(cert_der), "signature":crypto_util.create_sig(cert_der, key_file)}
def install_certificate(self, certificate_dict, vhost):
cert_chain_abspath = None
cert_fd, self.cert_file = trustify_util.unique_file(CERT_PATH, 644)
cert_fd.write(self.convert_b64_cert_to_pem(certificate_dict["certificate"]))
cert_fd.write(crypto_util.convert_b64_cert_to_pem(certificate_dict["certificate"]))
cert_fd.close()
logger.info("Server issued certificate; certificate written to %s" % self.cert_file)
if certificate_dict.get("chain", None):
chain_fd, chain_fn = trustify_util.unique_file(CHAIN_PATH, 644)
for c in certificate_dict.get("chain", []):
chain_fd.write(self.convert_b64_cert_to_pem(c))
chain_fd.write(crypto_util.convert_b64_cert_to_pem(c))
chain_fd.close()
logger.info("Cert chain written to %s" % chain_fn)
@@ -277,19 +264,18 @@ class Client(object):
# sites may have been enabled / final cleanup
self.config.restart(quiet=self.curses)
if self.curses:
self.d.msgbox("\nCongratulations! You have successfully enabled " + self.gen_https_names(self.names) + "!", width=70)
if self.by_default():
self.config.enable_mod("rewrite")
self.redirect_to_ssl(vhost)
self.config.restart(quiet=self.curses)
else:
logger.info("Congratulations! You have successfully enabled " + self.gen_https_names(self.names) + "!")
display.success_installation(self.curses, self.names)
def optimize_config(self, vhost):
if display.redirect_by_default(self.curses):
self.config.enable_mod("rewrite")
self.redirect_to_ssl(vhost)
self.config.restart(quiet=self.curses)
def certificate_request(self, csr_der, key):
logger.info("Preparing and sending CSR..")
return {"type":"certificateRequest", "csr":jose.b64encode_url(csr_der), "signature":self.create_sig(csr_der)}
return {"type":"certificateRequest", "csr":jose.b64encode_url(csr_der), "signature":crypto_util.create_sig(csr_der, self.key_file)}
def cleanup_challenges(self, challenge_objs):
logger.info("Cleaning up challenges...")
@@ -307,6 +293,11 @@ class Client(object):
logger.info("Waiting for %d seconds..." % delay)
time.sleep(delay)
msg_dict = self.send(self.status_request(msg_dict["token"]))
else:
logger.fatal("Received unexpected message")
logger.fatal("Expected: %s" % expected)
logger.fatal("Received: " + msg_dict)
sys.exit(33)
logger.error("Server has deferred past the max of %d seconds" % (rounds * delay))
return None
@@ -314,45 +305,15 @@ class Client(object):
def authorization_request(self, id, name, server_nonce, responses):
auth_req = {"type":"authorizationRequest", "sessionID":id, "nonce":server_nonce}
auth_req["signature"] = self.create_sig(name + jose.b64decode_url(server_nonce))
auth_req["signature"] = crypto_util.create_sig(name + jose.b64decode_url(server_nonce), self.key_file)
auth_req["responses"] = responses
return auth_req
def status_request(self, token):
return {"type":"statusRequest", "token":token}
def __leading_zeros(self, s):
if len(s) % 2:
return "0" + s
return s
def create_sig(self, msg, signer_nonce = None, signer_nonce_len = NONCE_SIZE):
# DOES prepend signer_nonce to message
# TODO: Change this over to M2Crypto... PKey
# Protect against crypto unicode errors... is this sufficient? Do I need to escape?
msg = str(msg)
key = RSA.importKey(open(self.key_file).read())
if signer_nonce is None:
signer_nonce = get_random_bytes(signer_nonce_len)
h = SHA256.new(signer_nonce + msg)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
#print "signing:", signer_nonce + msg
#print "signature:", signature
n, e = key.n, key.e
n_bytes = binascii.unhexlify(self.__leading_zeros(hex(n)[2:].replace("L", "")))
e_bytes = binascii.unhexlify(self.__leading_zeros(hex(e)[2:].replace("L", "")))
n_encoded = jose.b64encode_url(n_bytes)
e_encoded = jose.b64encode_url(e_bytes)
signer_nonce_encoded = jose.b64encode_url(signer_nonce)
sig_encoded = jose.b64encode_url(signature)
jwk = { "kty": "RSA", "n": n_encoded, "e": e_encoded }
signature = { "nonce": signer_nonce_encoded, "alg": "RS256", "jwk": jwk, "sig": sig_encoded }
# return json.dumps(signature)
return (signature)
def challenge_request(self, names):
logger.info("Temporarily only enabling one name")
#logger.info("Temporarily only enabling one name")
return {"type":"challengeRequest", "identifier": names[0]}
def verify_identity(self, c):
@@ -510,7 +471,7 @@ class Client(object):
key_pem = None
csr_pem = None
if not self.key_file:
key_pem = self.make_key(RSA_KEY_SIZE)
key_pem = crypto_util.make_key(RSA_KEY_SIZE)
# Save file
trustify_util.make_or_verify_dir(KEY_DIR, 0700)
key_f, self.key_file = trustify_util.unique_file(KEY_DIR + "key-trustify.pem", 0600)
@@ -525,7 +486,7 @@ class Client(object):
sys.exit(1)
if not self.csr_file:
csr_pem, csr_der = trustify_util.make_csr(self.key_file, self.names)
csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names)
# Save CSR
trustify_util.make_or_verify_dir(CERT_DIR, 0755)
csr_f, self.csr_file = trustify_util.unique_file(CERT_DIR + "csr-trustify.pem", 0644)
@@ -533,6 +494,7 @@ class Client(object):
csr_f.close()
logger.info("Creating CSR: %s" % self.csr_file)
else:
#TODO fix this der situation
try:
csr_pem = open(self.csr_file).read().replace("\r", "")
except:
@@ -544,48 +506,15 @@ class Client(object):
return key_pem, csr_pem
# based on M2Crypto unit test written by Toby Allsopp
def make_key(self, bits=RSA_KEY_SIZE):
"""
Returns new RSA key in PEM form with specified bits
"""
rsa = RSA.gen_key(bits, 65537)
key_pem = rsa.as_pem(cipher=None)
rsa = None # should not be freed here
return key_pem
def __rsa_sign(self, key, data):
"""
Sign this data with this private key. For client-side use.
@type key: str
@param key: PEM-encoded string of the private key.
@type data: str
@param data: The data to be signed. Will be hashed (sha256) prior to
signing.
@return: binary string of the signature
"""
key = str(key)
data = str(data)
privkey = M2Crypto.RSA.load_key_string(key)
return privkey.sign(hashlib.sha256(data).digest(), 'sha256')
def filter_names(self, names):
choices = [(n, "", 1) for n in names]
choices = [(n, "", 0) for n in names]
result = self.d.checklist("Which names would you like to activate HTTPS for?", choices=choices)
if result[0] != 0 or not result[1]:
sys.exit(1)
return result[1]
def choice_of_ca(self):
choices = self.get_cas()
result = self.d.menu("Pick a Certificate Authority. They're all unique and special!", width=70, choices=choices)
@@ -611,11 +540,11 @@ class Client(object):
else:
EV_choices.append(choice)
random.shuffle(DV_choices)
random.shuffle(OV_choices)
random.shuffle(EV_choices)
# random.shuffle(DV_choices)
# random.shuffle(OV_choices)
# random.shuffle(EV_choices)
choices = DV_choices + OV_choices + EV_choices
#choices = [line.split(";", 1) for line in f]
except IOError as e:
logger.fatal("Unable to find .ca_offerings file")
sys.exit(1)
@@ -664,34 +593,6 @@ class Client(object):
except:
return False
def gen_https_names(self, domains):
"""
Returns a string of the domains formatted nicely with https:// prepended
to each
"""
result = ""
if len(domains) > 2:
for i in range(len(domains)-1):
result = result + "https://" + domains[i] + ", "
result = result + "and "
if len(domains) == 2:
return "https://" + domains[0] + " and https://" + domains[1]
if domains:
result = result + "https://" + domains[len(domains)-1]
return result
def by_default(self):
d = dialog.Dialog()
choices = [("Easy", "Allow both HTTP and HTTPS access to these sites"), ("Secure", "Make all requests redirect to secure HTTPS access")]
result = d.menu("Please choose whether HTTPS access is required or optional.", width=70, choices=choices)
if result[0] != 0:
sys.exit(1)
return result[1] == "Secure"
def sha256(m):
return hashlib.sha256(m).hexdigest()
def old_cert(cert_filename, days_left):
cert = M2Crypto.X509.load_cert(cert_filename)

View File

@@ -12,7 +12,7 @@ import errno
from trustify.client.CONFIG import SERVER_ROOT, BACKUP_DIR
from trustify.client.CONFIG import REWRITE_HTTPS_ARGS, CONFIG_DIR, WORK_DIR
from trustify.client.CONFIG import TEMP_CHECKPOINT_DIR, IN_PROGRESS_DIR
from trustify.client.CONFIG import OPTIONS_SSL_CONF
from trustify.client.CONFIG import OPTIONS_SSL_CONF, TRUSTIFY_VHOST_EXT
from trustify.client import logger, trustify_util
#from CONFIG import SERVER_ROOT, BACKUP_DIR, REWRITE_HTTPS_ARGS, CONFIG_DIR, WORK_DIR, TEMP_CHECKPOINT_DIR, IN_PROGRESS_DIR, OPTIONS_SSL_CONF, TRUSTIFY_VHOST_EXT
#import logger, trustify_util

View File

@@ -0,0 +1,151 @@
import M2Crypto
import time, jose, binascii
import hashlib
from Crypto.Random import get_random_bytes
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from M2Crypto import EVP, X509, ASN1
from trustify.client import logger
from trustify.client.CONFIG import NONCE_SIZE, RSA_KEY_SIZE
def convert_b64_cert_to_pem(b64_der_cert):
x = M2Crypto.X509.load_cert_der_string(jose.b64decode_url(b64_der_cert))
return x.as_pem()
def create_sig(msg, key_file, signer_nonce = None, signer_nonce_len = NONCE_SIZE):
# DOES prepend signer_nonce to message
# TODO: Change this over to M2Crypto... PKey
# Protect against crypto unicode errors... is this sufficient? Do I need to escape?
msg = str(msg)
key = RSA.importKey(open(key_file).read())
if signer_nonce is None:
signer_nonce = get_random_bytes(signer_nonce_len)
h = SHA256.new(signer_nonce + msg)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
#print "signing:", signer_nonce + msg
#print "signature:", signature
n, e = key.n, key.e
n_bytes = binascii.unhexlify(leading_zeros(hex(n)[2:].replace("L", "")))
e_bytes = binascii.unhexlify(leading_zeros(hex(e)[2:].replace("L", "")))
n_encoded = jose.b64encode_url(n_bytes)
e_encoded = jose.b64encode_url(e_bytes)
signer_nonce_encoded = jose.b64encode_url(signer_nonce)
sig_encoded = jose.b64encode_url(signature)
jwk = { "kty": "RSA", "n": n_encoded, "e": e_encoded }
signature = { "nonce": signer_nonce_encoded, "alg": "RS256", "jwk": jwk, "sig": sig_encoded }
# return json.dumps(signature)
return (signature)
def leading_zeros(s):
if len(s) % 2:
return "0" + s
return s
def sha256(m):
return hashlib.sha256(m).hexdigest()
# based on M2Crypto unit test written by Toby Allsopp
def make_key(bits=RSA_KEY_SIZE):
"""
Returns new RSA key in PEM form with specified bits
"""
rsa = M2Crypto.RSA.gen_key(bits, 65537)
key_pem = rsa.as_pem(cipher=None)
rsa = None # should not be freed here
return key_pem
def make_csr(key_file, domains):
"""
Returns new CSR in PEM and DER form using key_file containing all domains
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key(key_file)
pk = EVP.PKey()
pk.assign_rsa(rsa_key)
x = X509.Request()
x.set_pubkey(pk)
name = x.get_subject()
name.C = "US"
name.ST = "Michigan"
name.L = "Ann Arbor"
name.O = "EFF"
name.OU = "University of Michigan"
name.CN = domains[0]
extstack = X509.X509_Extension_Stack()
ext = X509.new_extension('subjectAltName', ", ".join(["DNS:%s" % d for d in domains]))
extstack.push(ext)
x.add_extensions(extstack)
x.sign(pk,'sha256')
assert x.verify(pk)
pk2 = x.get_pubkey()
assert x.verify(pk2)
return x.as_pem(), x.as_der()
def make_ss_cert(key_file, domains):
"""
Returns new self-signed cert in PEM form using key_file containing all domains
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key(key_file)
pk = EVP.PKey()
pk.assign_rsa(rsa_key)
x = X509.X509()
x.set_pubkey(pk)
x.set_serial_number(1337)
x.set_version(2)
t = long(time.time())
current = ASN1.ASN1_UTCTIME()
current.set_time(t)
expire = ASN1.ASN1_UTCTIME()
expire.set_time((7 * 24 * 60 * 60) + t)
x.set_not_before(current)
x.set_not_after(expire)
name = x.get_subject()
name.C = "US"
name.ST = "Michigan"
name.L = "Ann Arbor"
name.O = "University of Michigan and the EFF"
name.CN = domains[0]
x.set_issuer(x.get_subject())
x.add_ext(X509.new_extension('basicConstraints', 'CA:FALSE'))
#x.add_ext(X509.new_extension('extendedKeyUsage', 'TLS Web Server Authentication'))
x.add_ext(X509.new_extension('subjectAltName', ", ".join(["DNS:%s" % d for d in domains])))
x.sign(pk, 'sha256')
assert x.verify(pk)
assert x.verify()
#print check_purpose(,0
return x.as_pem()
def get_cert_info(filename):
d = {}
# M2Crypto Library only supports RSA right now
x = M2Crypto.X509.load_cert(filename)
d["not_before"] = x.get_not_before().get_datetime()
d["not_after"] = x.get_not_after().get_datetime()
d["subject"] = x.get_subject().as_text()
d["cn"] = x.get_subject().CN
d["issuer"] = x.get_issuer().as_text()
d["fingerprint"] = x.get_fingerprint(md='sha1')
try:
d["san"] = x.get_ext("subjectAltName").get_value()
except:
d["san"] = ""
d["serial"] = x.get_serial_number()
d["pub_key"] = "RSA " + str(x.get_pubkey().size() * 8)
return d

View File

@@ -0,0 +1,76 @@
import dialog
from trustify.client import logger
d = dialog.Dialog()
WIDTH = 70
HEIGHT = 16
# TODO: This whole class really needs to be refactored into two classes - one for curses one for text
def success_installation(curses, domains):
if curses:
d.msgbox("\nCongratulations! You have successfully enabled " + gen_https_names(domains) + "!", width=WIDTH)
else:
logger.info("Congratulations! You have successfully enabled " + gen_https_names(domains) + "!")
def redirect_by_default(curses):
if curses:
choices = [("Easy", "Allow both HTTP and HTTPS access to these sites"), ("Secure", "Make all requests redirect to secure HTTPS access")]
result = d.menu("Please choose whether HTTPS access is required or optional.", width=WIDTH, choices=choices)
if result[0] != 0:
return False
return result[1] == "Secure"
else:
ans = raw_input("Would you like to redirect all normal HTTP traffic to HTTPS? y/n")
return ans.startswith('y') or ans.startswith('Y')
def gen_https_names(domains):
"""
Returns a string of the domains formatted nicely with https:// prepended
to each
"""
result = ""
if len(domains) > 2:
for i in range(len(domains)-1):
result = result + "https://" + domains[i] + ", "
result = result + "and "
if len(domains) == 2:
return "https://" + domains[0] + " and https://" + domains[1]
if domains:
result = result + "https://" + domains[len(domains)-1]
return result
def confirm_revocation(cert):
text = "Are you sure you would like to revoke the following certificate:\n"
text += cert_info_frame(cert)
text += "This action cannot be reversed!"
a = d.yesno(text, width=WIDTH, height=HEIGHT)
return a == d.DIALOG_OK
def cert_info_frame(cert):
text = "-" * (WIDTH - 4) + "\n"
text += cert_info_string(cert)
text += "-" * (WIDTH - 4)
return text
def more_info_cert(cert):
text = "Certificate Information:\n"
text += cert_info_frame(cert)
d.msgbox(text, width=WIDTH, height=HEIGHT)
def cert_info_string(cert):
text = "Subject: %s\n" % cert["subject"]
text += "SAN: %s\n" % cert["san"]
text += "Issuer: %s\n" % cert["issuer"]
text += "Public Key: %s\n" % cert["pub_key"]
text += "Not Before: %s\n" % str(cert["not_before"])
text += "Not After: %s\n" % str(cert["not_after"])
text += "Serial Number: %s\n" % cert["serial"]
text += "SHA1: %s\n" % cert["fingerprint"]
return text

View File

@@ -17,7 +17,7 @@ from trustify.client import configurator
from trustify.client.CONFIG import CONFIG_DIR, WORK_DIR, SERVER_ROOT
from trustify.client.CONFIG import CHOC_CERT_CONF, OPTIONS_SSL_CONF, APACHE_CHALLENGE_CONF, INVALID_EXT
from trustify.client.CONFIG import S_SIZE, NONCE_SIZE
from trustify.client import logger, trustify_util
from trustify.client import logger, crypto_util
from trustify.client.challenge import Challenge
# import configurator
@@ -141,7 +141,7 @@ DocumentRoot " + CONFIG_DIR + "challenge_page/ \n \
self.createCHOC_CERT_CONF(name, ext)
self.configurator.register_file_creation(True, self.getDvsniCertFile(nonce))
cert_pem = trustify_util.make_ss_cert(key, [nonce + INVALID_EXT, name, ext])
cert_pem = crypto_util.make_ss_cert(key, [nonce + INVALID_EXT, name, ext])
with open(self.getDvsniCertFile(nonce), 'w') as f:
f.write(cert_pem)

View File

@@ -4,75 +4,10 @@ import stat
import os, pwd, grp
import M2Crypto
import time
from M2Crypto import EVP, X509, RSA, ASN1
from trustify.client import logger
#import logger
def make_csr(key_file, domains):
"""
Returns new CSR in PEM and DER form using key_file containing all domains
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key(key_file)
pk = EVP.PKey()
pk.assign_rsa(rsa_key)
x = X509.Request()
x.set_pubkey(pk)
name = x.get_subject()
name.CN = domains[0]
extstack = X509.X509_Extension_Stack()
ext = X509.new_extension('subjectAltName', ", ".join(["DNS:%s" % d for d in domains]))
extstack.push(ext)
x.add_extensions(extstack)
x.sign(pk,'sha256')
assert x.verify(pk)
pk2 = x.get_pubkey()
assert x.verify(pk2)
return x.as_pem(), x.as_der()
def make_ss_cert(key_file, domains):
"""
Returns new self-signed cert in PEM form using key_file containing all domains
"""
assert domains, "Must provide one or more hostnames for the CSR."
rsa_key = M2Crypto.RSA.load_key(key_file)
pk = EVP.PKey()
pk.assign_rsa(rsa_key)
x = X509.X509()
x.set_pubkey(pk)
x.set_serial_number(1337)
x.set_version(2)
t = long(time.time())
current = ASN1.ASN1_UTCTIME()
current.set_time(t)
expire = ASN1.ASN1_UTCTIME()
expire.set_time((7 * 24 * 60 * 60) + t)
x.set_not_before(current)
x.set_not_after(expire)
name = x.get_subject()
name.C = "US"
name.ST = "Michigan"
name.L = "Ann Arbor"
name.O = "University of Michigan and the EFF"
name.CN = domains[0]
x.set_issuer(x.get_subject())
x.add_ext(X509.new_extension('basicConstraints', 'CA:FALSE'))
#x.add_ext(X509.new_extension('extendedKeyUsage', 'TLS Web Server Authentication'))
x.add_ext(X509.new_extension('subjectAltName', ", ".join(["DNS:%s" % d for d in domains])))
x.sign(pk, 'sha256')
assert x.verify(pk)
assert x.verify()
#print check_purpose(,0
return x.as_pem()
def make_or_verify_dir(directory, permissions=0755, uid=0):
try:
@@ -107,31 +42,6 @@ def unique_file(default_name, mode = 0777):
count += 1
def get_cert_info(filename):
d = {}
# M2Crypto Library only supports RSA right now
x = M2Crypto.X509.load_cert(filename)
d["not_before"] = x.get_not_before().get_datetime()
d["not_after"] = x.get_not_after().get_datetime()
d["subject"] = x.get_subject().as_text()
d["cn"] = x.get_subject().CN
d["issuer"] = x.get_issuer().as_text()
d["fingerprint"] = x.get_fingerprint(md='sha1')
d["san"] = x.get_ext("subjectAltName").get_value()
d["serial"] = x.get_serial_number()
d["pub_key"] = "RSA " + str(x.get_pubkey().size() * 8)
return d
def cert_info_string(cert):
text = "Subject: %s\n" % cert["subject"]
text += "SAN: %s\n" % cert["san"]
text += "Issuer: %s\n" % cert["issuer"]
text += "Public Key: %s\n" % cert["pub_key"]
text += "Not Before: %s\n" % str(cert["not_before"])
text += "Not After: %s\n" % str(cert["not_after"])
text += "Serial Number: %s\n" % cert["serial"]
text += "SHA1: %s\n" % cert["fingerprint"]
return text
def drop_privs():
nogroup = grp.getgrnam("nogroup").gr_gid