def generate_bad_key_usage_cert(domain, base_year, quiet=False): if not quiet: write('Generating bad-key-usage cert ... ', end='') full_domain = 'bad-key-usage.{}'.format(domain) ca_private_key = load_private('ca') ca_cert = load_cert('ca') public_key = load_public('host') builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Bad TLS Limited', 'common_name': full_domain, }, public_key ) builder.issuer = ca_cert builder.subject_alt_domains = [full_domain] builder.begin_date = datetime(base_year, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.end_date = datetime(base_year + 3, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.key_usage = set(['crl_sign']) builder.extended_key_usage = set(['email_protection']) certificate = builder.build(ca_private_key) dump_cert('bad-key-usage', certificate) if not quiet: write('done')
def generate_auth_cert(domain, base_year, quiet=False): if not quiet: write('Generating auth cert ... ', end='') ca_private_key = load_private('ca') ca_cert = load_cert('ca') public_key = load_public('host') builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Bad TLS Limited', 'common_name': 'required-auth.{}'.format(domain), }, public_key ) builder.issuer = ca_cert builder.subject_alt_domains = [ 'required-auth.{}'.format(domain), 'optional-auth.{}'.format(domain), ] builder.begin_date = datetime(base_year, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.end_date = datetime(base_year + 3, 1, 1, 0, 0, 0, tzinfo=timezone.utc) certificate = builder.build(ca_private_key) dump_cert('auth', certificate) if not quiet: write('done')
def generate_expired_cert(domain, base_year, quiet=False): if not quiet: write('Generating expired cert ... ', end='') full_domain = 'expired.{}'.format(domain) ca_private_key = load_private('ca') ca_cert = load_cert('ca') public_key = load_public('host') builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Bad TLS Limited', 'common_name': full_domain, }, public_key) builder.issuer = ca_cert builder.subject_alt_domains = [full_domain] builder.begin_date = datetime(base_year - 1, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.end_date = datetime(base_year, 1, 1, 0, 0, 0, tzinfo=timezone.utc) certificate = builder.build(ca_private_key) dump_cert('expired', certificate) if not quiet: write('done')
def generate_weak_sig_cert(domain, base_year, quiet=False): if not quiet: write('Generating weak-sig cert ... ', end='') full_domain = 'weak-sig.{}'.format(domain) ca_private_key = load_private('ca') ca_cert = load_cert('ca') public_key = load_public('host') builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Bad TLS Limited', 'common_name': full_domain, }, public_key ) builder.issuer = ca_cert builder.subject_alt_domains = [full_domain] # Hack since API doesn't allow selection of weak algo builder._hash_algo = 'md5' builder.begin_date = datetime(base_year, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.end_date = datetime(base_year + 3, 1, 1, 0, 0, 0, tzinfo=timezone.utc) certificate = builder.build(ca_private_key) dump_cert('weak-sig', certificate) if not quiet: write('done')
def generate_expired_1963_cert(domain, quiet=False): if not quiet: write('Generating expired-1963 cert ... ', end='') full_domain = 'expired-1963.{}'.format(domain) ca_private_key = load_private('ca') ca_cert = load_cert('ca') public_key = load_public('host') builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Bad TLS Limited', 'common_name': full_domain, }, public_key ) builder.issuer = ca_cert builder.subject_alt_domains = [full_domain] builder.begin_date = datetime(1962, 1, 1, 0, 0, 0, tzinfo=timezone.utc) builder.end_date = datetime(1963, 1, 1, 0, 0, 0, tzinfo=timezone.utc) certificate = builder.build(ca_private_key) dump_cert('expired-1963', certificate) if not quiet: write('done')
def test_subject_alt_name_shortcuts(self): public_key, private_key = self.ec_secp256r1 builder = CertificateBuilder( { 'country_name': 'US', 'common_name': 'Test' }, public_key) builder.self_signed = True self.assertEqual(builder.subject_alt_domains, []) builder.subject_alt_domains = ['example.com', 'example.org'] builder.subject_alt_emails = ['*****@*****.**', '*****@*****.**'] builder.subject_alt_ips = ['127.0.0.1'] builder.subject_alt_uris = ['http://example.com', 'https://bücher.ch'] self.assertEqual(builder.subject_alt_domains, ['example.com', 'example.org']) self.assertEqual(builder.subject_alt_emails, ['*****@*****.**', '*****@*****.**']) self.assertEqual(builder.subject_alt_ips, ['127.0.0.1']) self.assertEqual(builder.subject_alt_uris, ['http://example.com', 'https://bücher.ch']) builder.subject_alt_domains = [] self.assertEqual(builder.subject_alt_domains, []) builder.subject_alt_emails = [] self.assertEqual(builder.subject_alt_emails, []) builder.subject_alt_ips = [] self.assertEqual(builder.subject_alt_ips, []) builder.subject_alt_uris = [] self.assertEqual(builder.subject_alt_uris, []) builder.subject_alt_uris = ['https://bücher.ch'] certificate = builder.build(private_key) self.assertEqual(b'\x86\x18https://xn--bcher-kva.ch', certificate.subject_alt_name_value[0].contents)
def gencert(name): ca_cert_data, ca_private_data = _gen_ca() public_data, private_data = _cert_key() builder = CertificateBuilder({"common_name": name}, _load_public(public_data)) builder.issuer = _load_cert(ca_cert_data) builder.subject_alt_domains = [name] cert = builder.build(_load_private(ca_private_data)) return (_dump_cert(cert), private_data, ca_cert_data)
def test_build_end_entity_cert(self): public_key, private_key = self.ec_secp256r1 builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Codex Non Sufficit LC', 'common_name': 'Will Bond', }, public_key ) builder.self_signed = True builder.subject_alt_domains = ['example.com'] certificate = builder.build(private_key) der_bytes = certificate.dump() new_certificate = asn1crypto.x509.Certificate.load(der_bytes) self.assertEqual('sha256', new_certificate.hash_algo) self.assertEqual( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Codex Non Sufficit LC', 'common_name': 'Will Bond', }, new_certificate.issuer.native ) self.assertEqual( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Codex Non Sufficit LC', 'common_name': 'Will Bond', }, new_certificate.subject.native ) self.assertEqual('ecdsa', new_certificate.signature_algo) self.assertEqual(set(['key_usage']), new_certificate.critical_extensions) self.assertEqual(set(['digital_signature', 'key_encipherment']), new_certificate.key_usage_value.native) self.assertEqual(['server_auth', 'client_auth'], new_certificate.extended_key_usage_value.native) self.assertEqual(None, new_certificate.authority_key_identifier) self.assertEqual(False, new_certificate.ca) self.assertEqual(True, new_certificate.self_issued) self.assertEqual('yes', new_certificate.self_signed) self.assertEqual(certificate.public_key.sha1, new_certificate.key_identifier) self.assertEqual(['example.com'], new_certificate.valid_domains)
def test_subject_alt_name_shortcuts(self): public_key, private_key = self.ec_secp256r1 builder = CertificateBuilder( {'country_name': 'US', 'common_name': 'Test'}, public_key ) builder.self_signed = True self.assertEqual(builder.subject_alt_domains, []) builder.subject_alt_domains = ['example.com', 'example.org'] builder.subject_alt_emails = ['*****@*****.**', '*****@*****.**'] builder.subject_alt_ips = ['127.0.0.1'] builder.subject_alt_uris = ['http://example.com', 'https://bücher.ch'] self.assertEqual(builder.subject_alt_domains, ['example.com', 'example.org']) self.assertEqual(builder.subject_alt_emails, ['*****@*****.**', '*****@*****.**']) self.assertEqual(builder.subject_alt_ips, ['127.0.0.1']) self.assertEqual(builder.subject_alt_uris, ['http://example.com', 'https://bücher.ch']) builder.subject_alt_domains = [] self.assertEqual(builder.subject_alt_domains, []) builder.subject_alt_emails = [] self.assertEqual(builder.subject_alt_emails, []) builder.subject_alt_ips = [] self.assertEqual(builder.subject_alt_ips, []) builder.subject_alt_uris = [] self.assertEqual(builder.subject_alt_uris, []) builder.subject_alt_uris = ['https://bücher.ch'] certificate = builder.build(private_key) self.assertEqual(b'\x86\x18https://xn--bcher-kva.ch', certificate.subject_alt_name_value[0].contents)
def create_certificate_for_client(client_details): # load client public key path_for_client_public_key = PUBLIC_KEY_STORE + file_names.get_public_key_name( client_details['domain_name']) client_public_key = asymmetric.load_public_key\ (get_byte_stream_for_file(path_for_client_public_key)) root_ca_private_key = asymmetric.load_private_key\ (get_byte_stream_for_file(ROOT_CA_PRIVATE_KEY_PATH), ROOT_CA_PRIVATE_KEY_PASSWORD) root_ca_public_key = asymmetric.load_public_key\ (get_byte_stream_for_file(ROOT_CA_PUBLIC_KEY)) root_ca_certificate = asymmetric.load_certificate\ (get_byte_stream_for_file(ROOT_CA_CERTIFICATE)) builder = CertificateBuilder( { u'country_name': client_details['country'], #.decode('utf-8'), u'state_or_province_name': client_details['state'], #.decode('utf-8'), u'locality_name': client_details['city'], #.decode('utf-8'), u'organization_name': client_details['name'], #.decode('utf-8'), u'common_name': get_dns_formatted_name( client_details['domain_name']), #.decode('utf-8'), }, client_public_key) dns_name = [] dns_name.append(u'opengns.com') builder.issuer = root_ca_certificate builder.subject_alt_domains = dns_name #builder.set_extension(u'authority_information_access', u'1.3.6.1.5.5.7.1.1') client_certificate = builder.build(root_ca_private_key) client_certificate_name = file_names.get_certificate_name( client_details['domain_name']) total_path = CERTIFICATE_OUTPUT_DIRECTORY + client_certificate_name with open(total_path, 'wb') as f: f.write(pem_armor_certificate(client_certificate)) return client_certificate_name
def _sign(csr, buf, profile, skip_notify=False, skip_push=False, overwrite=False, signer=None): # TODO: CRLDistributionPoints, OCSP URL, Certificate URL assert buf.startswith(b"-----BEGIN ") assert isinstance(csr, CertificationRequest) csr_pubkey = asymmetric.load_public_key( csr["certification_request_info"]["subject_pk_info"]) common_name = csr["certification_request_info"]["subject"].native[ "common_name"] cert_path = os.path.join(config.SIGNED_DIR, "%s.pem" % common_name) renew = False attachments = [ (buf, "application/x-pem-file", common_name + ".csr"), ] revoked_path = None overwritten = False # Move existing certificate if necessary if os.path.exists(cert_path): with open(cert_path, "rb") as fh: prev_buf = fh.read() header, _, der_bytes = pem.unarmor(prev_buf) prev = x509.Certificate.load(der_bytes) # TODO: assert validity here again? renew = \ asymmetric.load_public_key(prev["tbs_certificate"]["subject_public_key_info"]) == \ csr_pubkey # BUGBUG: is this enough? if overwrite: # TODO: is this the best approach? # TODO: why didn't unittest detect bugs here? prev_serial_hex = "%x" % prev.serial_number revoked_path = os.path.join(config.REVOKED_DIR, "%040x.pem" % prev.serial_number) os.rename(cert_path, revoked_path) attachments += [(prev_buf, "application/x-pem-file", "deprecated.crt" if renew else "overwritten.crt")] overwritten = True else: raise FileExistsError("Will not overwrite existing certificate") builder = CertificateBuilder( cn_to_dn(common_name, const.FQDN, o=certificate["tbs_certificate"]["subject"].native.get( "organization_name"), ou=profile.ou), csr_pubkey) builder.serial_number = generate_serial() now = datetime.utcnow() builder.begin_date = now - const.CLOCK_SKEW_TOLERANCE builder.end_date = now + timedelta(days=profile.lifetime) builder.issuer = certificate builder.ca = profile.ca builder.key_usage = profile.key_usage builder.extended_key_usage = profile.extended_key_usage builder.subject_alt_domains = [common_name] builder.ocsp_url = profile.responder_url builder.crl_url = profile.revoked_url end_entity_cert = builder.build(private_key) end_entity_cert_buf = asymmetric.dump_certificate(end_entity_cert) with open(cert_path + ".part", "wb") as fh: fh.write(end_entity_cert_buf) os.rename(cert_path + ".part", cert_path) attachments.append( (end_entity_cert_buf, "application/x-pem-file", common_name + ".crt")) cert_serial_hex = "%x" % end_entity_cert.serial_number # Create symlink link_name = os.path.join(config.SIGNED_BY_SERIAL_DIR, "%040x.pem" % end_entity_cert.serial_number) assert not os.path.exists( link_name ), "Certificate with same serial number already exists: %s" % link_name os.symlink("../%s.pem" % common_name, link_name) # Copy filesystem attributes to newly signed certificate if revoked_path: for key in listxattr(revoked_path): if not key.startswith(b"user."): continue setxattr(cert_path, key, getxattr(revoked_path, key)) # Attach signer username if signer: setxattr(cert_path, "user.signature.username", signer) if not skip_notify: # Send mail if renew: # Same keypair mailer.send("certificate-renewed.md", **locals()) else: # New keypair mailer.send("certificate-signed.md", **locals()) if not skip_push: url = config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest() click.echo("Publishing certificate at %s ..." % url) requests.post(url, data=end_entity_cert_buf, headers={ "User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert" }) if renew: # TODO: certificate-renewed event push.publish("certificate-revoked", common_name) push.publish("request-signed", common_name) else: push.publish("request-signed", common_name) return end_entity_cert, end_entity_cert_buf
def _sign(csr, buf, overwrite=False): # TODO: CRLDistributionPoints, OCSP URL, Certificate URL assert buf.startswith("-----BEGIN CERTIFICATE REQUEST-----\n") assert isinstance(csr, CertificationRequest) csr_pubkey = asymmetric.load_public_key( csr["certification_request_info"]["subject_pk_info"]) common_name = csr["certification_request_info"]["subject"].native[ "common_name"] cert_path = os.path.join(config.SIGNED_DIR, "%s.pem" % common_name) renew = False attachments = [ (buf, "application/x-pem-file", common_name + ".csr"), ] revoked_path = None overwritten = False # Move existing certificate if necessary if os.path.exists(cert_path): with open(cert_path) as fh: prev_buf = fh.read() header, _, der_bytes = pem.unarmor(prev_buf) prev = x509.Certificate.load(der_bytes) # TODO: assert validity here again? renew = \ asymmetric.load_public_key(prev["tbs_certificate"]["subject_public_key_info"]) == \ csr_pubkey # BUGBUG: is this enough? if overwrite: # TODO: is this the best approach? prev_serial_hex = "%x" % prev.serial_number revoked_path = os.path.join(config.REVOKED_DIR, "%s.pem" % prev_serial_hex) os.rename(cert_path, revoked_path) attachments += [(prev_buf, "application/x-pem-file", "deprecated.crt" if renew else "overwritten.crt")] overwritten = True else: raise EnvironmentError("Will not overwrite existing certificate") # Sign via signer process builder = CertificateBuilder({u'common_name': common_name}, csr_pubkey) builder.serial_number = random.randint( 0x1000000000000000000000000000000000000000, 0xffffffffffffffffffffffffffffffffffffffff) now = datetime.utcnow() builder.begin_date = now - timedelta(minutes=5) builder.end_date = now + timedelta( days=config.SERVER_CERTIFICATE_LIFETIME if server_flags(common_name) else config.CLIENT_CERTIFICATE_LIFETIME) builder.issuer = certificate builder.ca = False builder.key_usage = set([u"digital_signature", u"key_encipherment"]) # OpenVPN uses CN while StrongSwan uses SAN if server_flags(common_name): builder.subject_alt_domains = [common_name] builder.extended_key_usage = set( [u"server_auth", u"1.3.6.1.5.5.8.2.2", u"client_auth"]) else: builder.extended_key_usage = set([u"client_auth"]) end_entity_cert = builder.build(private_key) end_entity_cert_buf = asymmetric.dump_certificate(end_entity_cert) with open(cert_path + ".part", "wb") as fh: fh.write(end_entity_cert_buf) os.rename(cert_path + ".part", cert_path) attachments.append( (end_entity_cert_buf, "application/x-pem-file", common_name + ".crt")) cert_serial_hex = "%x" % end_entity_cert.serial_number # Create symlink link_name = os.path.join(config.SIGNED_BY_SERIAL_DIR, "%x.pem" % end_entity_cert.serial_number) assert not os.path.exists( link_name ), "Certificate with same serial number already exists: %s" % link_name os.symlink("../%s.pem" % common_name, link_name) # Copy filesystem attributes to newly signed certificate if revoked_path: for key in listxattr(revoked_path): if not key.startswith("user."): continue setxattr(cert_path, key, getxattr(revoked_path, key)) # Send mail if renew: # Same keypair mailer.send("certificate-renewed.md", **locals()) else: # New keypair mailer.send("certificate-signed.md", **locals()) url = config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest() click.echo("Publishing certificate at %s ..." % url) requests.post(url, data=end_entity_cert_buf, headers={ "User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert" }) push.publish("request-signed", common_name) return end_entity_cert, end_entity_cert_buf
def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, profile="default", signer=None): # TODO: CRLDistributionPoints, OCSP URL, Certificate URL if profile not in config.PROFILES: raise ValueError("Invalid profile supplied '%s'" % profile) assert buf.startswith(b"-----BEGIN ") assert isinstance(csr, CertificationRequest) csr_pubkey = asymmetric.load_public_key( csr["certification_request_info"]["subject_pk_info"]) common_name = csr["certification_request_info"]["subject"].native[ "common_name"] cert_path = os.path.join(config.SIGNED_DIR, "%s.pem" % common_name) renew = False attachments = [ (buf, "application/x-pem-file", common_name + ".csr"), ] revoked_path = None overwritten = False # Move existing certificate if necessary if os.path.exists(cert_path): with open(cert_path, "rb") as fh: prev_buf = fh.read() header, _, der_bytes = pem.unarmor(prev_buf) prev = x509.Certificate.load(der_bytes) # TODO: assert validity here again? renew = \ asymmetric.load_public_key(prev["tbs_certificate"]["subject_public_key_info"]) == \ csr_pubkey # BUGBUG: is this enough? if overwrite: # TODO: is this the best approach? prev_serial_hex = "%x" % prev.serial_number revoked_path = os.path.join(config.REVOKED_DIR, "%s.pem" % prev_serial_hex) os.rename(cert_path, revoked_path) attachments += [(prev_buf, "application/x-pem-file", "deprecated.crt" if renew else "overwritten.crt")] overwritten = True else: raise FileExistsError("Will not overwrite existing certificate") # Sign via signer process dn = {u'common_name': common_name} profile_server_flags, lifetime, dn[ "organizational_unit_name"], _ = config.PROFILES[profile] lifetime = int(lifetime) builder = CertificateBuilder(dn, csr_pubkey) builder.serial_number = random.randint( 0x1000000000000000000000000000000000000000, 0x7fffffffffffffffffffffffffffffffffffffff) now = datetime.utcnow() builder.begin_date = now - timedelta(minutes=5) builder.end_date = now + timedelta(days=lifetime) builder.issuer = certificate builder.ca = False builder.key_usage = set(["digital_signature", "key_encipherment"]) # If we have FQDN and profile suggests server flags, enable them if server_flags(common_name) and profile_server_flags: builder.subject_alt_domains = [ common_name ] # OpenVPN uses CN while StrongSwan uses SAN to match hostname of the server builder.extended_key_usage = set( ["server_auth", "1.3.6.1.5.5.8.2.2", "client_auth"]) else: builder.subject_alt_domains = [common_name ] # iOS demands SAN also for clients builder.extended_key_usage = set(["client_auth"]) end_entity_cert = builder.build(private_key) end_entity_cert_buf = asymmetric.dump_certificate(end_entity_cert) with open(cert_path + ".part", "wb") as fh: fh.write(end_entity_cert_buf) os.rename(cert_path + ".part", cert_path) attachments.append( (end_entity_cert_buf, "application/x-pem-file", common_name + ".crt")) cert_serial_hex = "%x" % end_entity_cert.serial_number # Create symlink link_name = os.path.join(config.SIGNED_BY_SERIAL_DIR, "%x.pem" % end_entity_cert.serial_number) assert not os.path.exists( link_name ), "Certificate with same serial number already exists: %s" % link_name os.symlink("../%s.pem" % common_name, link_name) # Copy filesystem attributes to newly signed certificate if revoked_path: for key in listxattr(revoked_path): if not key.startswith(b"user."): continue setxattr(cert_path, key, getxattr(revoked_path, key)) # Attach signer username if signer: setxattr(cert_path, "user.signature.username", signer) if not skip_notify: # Send mail if renew: # Same keypair mailer.send("certificate-renewed.md", **locals()) else: # New keypair mailer.send("certificate-signed.md", **locals()) if not skip_push: url = config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest() click.echo("Publishing certificate at %s ..." % url) requests.post(url, data=end_entity_cert_buf, headers={ "User-Agent": "Certidude API", "Content-Type": "application/x-x509-user-cert" }) push.publish("request-signed", common_name) return end_entity_cert, end_entity_cert_buf