Ejemplo n.º 1
0
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')
Ejemplo n.º 2
0
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')
Ejemplo n.º 3
0
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')
Ejemplo n.º 4
0
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')
Ejemplo n.º 5
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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)
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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