예제 #1
0
 def load_gnames(self, gname_list):
     """Converts list of prefixed strings to GeneralName list.
     """
     gnames = []
     for alt in gname_list:
         if ':' not in alt:
             die("Invalid gname: %s", alt)
         t, val = alt.split(':', 1)
         t = t.lower().strip()
         val = val.strip()
         if t == 'dn':
             gn = x509.DirectoryName(self.load_name(parse_dn(val)))
         elif t == 'dns':
             gn = x509.DNSName(val)
         elif t == 'email':
             gn = x509.RFC822Name(val)
         elif t == 'uri':
             gn = x509.UniformResourceIdentifier(val)
         elif t == 'ip':
             if val.find(':') >= 0:
                 gn = x509.IPAddress(ipaddress.IPv6Address(val))
             else:
                 gn = x509.IPAddress(ipaddress.IPv4Address(val))
         elif t == 'dn':
             gn = x509.DirectoryName(self.load_name(parse_dn(val)))
         elif t == 'net':
             if val.find(':') >= 0:
                 gn = x509.IPAddress(ipaddress.IPv6Network(val))
             else:
                 gn = x509.IPAddress(ipaddress.IPv4Network(val))
         else:
             raise Exception('Invalid GeneralName: ' + alt)
         gnames.append(gn)
     return gnames
예제 #2
0
 def test_eq(self):
     name = x509.Name(
         [x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1')])
     name2 = x509.Name(
         [x509.NameAttribute(x509.ObjectIdentifier('oid'), 'value1')])
     gn = x509.DirectoryName(x509.Name([name]))
     gn2 = x509.DirectoryName(x509.Name([name2]))
     assert gn == gn2
예제 #3
0
 def test_dirname(self):
     """Test parsing a dirname."""
     self.assertEqual(parse_general_name('/CN=example.com'), x509.DirectoryName(x509.Name([
         x509.NameAttribute(NameOID.COMMON_NAME, 'example.com'),
     ])))
     self.assertEqual(parse_general_name('dirname:/CN=example.com'), x509.DirectoryName(x509.Name([
         x509.NameAttribute(NameOID.COMMON_NAME, 'example.com'),
     ])))
     self.assertEqual(parse_general_name('dirname:/C=AT/CN=example.com'), x509.DirectoryName(x509.Name([
         x509.NameAttribute(NameOID.COUNTRY_NAME, 'AT'),
         x509.NameAttribute(NameOID.COMMON_NAME, 'example.com'),
     ])))
예제 #4
0
 def test_repr(self):
     name = x509.Name([x509.NameAttribute(x509.OID_COMMON_NAME, 'value1')])
     gn = x509.DirectoryName(x509.Name([name]))
     assert repr(gn) == (
         "<DirectoryName(value=<Name([<Name([<NameAttribute(oid=<ObjectIden"
         "tifier(oid=2.5.4.3, name=commonName)>, value='value1')>])>])>)>"
     )
예제 #5
0
 def test_dirname(self):
     """Test formatting a dirname."""
     name = x509.DirectoryName(x509.Name([
         x509.NameAttribute(NameOID.COUNTRY_NAME, 'AT'),
         x509.NameAttribute(NameOID.COMMON_NAME, 'example.com'),
     ]))
     self.assertEqual(format_general_name(name), 'dirname:/C=AT/CN=example.com')
예제 #6
0
    def build_name(key, value):
        key = key.lower()

        if key == "dns":
            return x509.DNSName(str(value))

        if key == "email":
            return x509.RFC822Name(str(value))

        if key == "uri":
            return x509.UniformResourceIdentifier(str(value))

        if key == "dirname":
            return x509.DirectoryName(str(value))

        if key in ("ip", "ip address"):
            import ipaddress

            try:
                value = ipaddress.ip_address(str(value))
            except ValueError:
                value = ipaddress.ip_network(str(value))

            return x509.IPAddress(value)

        raise ValueError(f"Unsupported alternative name: {key}")
예제 #7
0
def get_extension_params(
    extension: list, cert: typing.Union[x509.CertificateSigningRequestBuilder, x509.CertificateBuilder, None] = None,
    issuer: typing.Optional[x509.Certificate] = None
) -> list:
    params = []

    if extension[0] == 'BasicConstraints':
        params = [extension[1].get('ca'), extension[1].get('path_length')]
    elif extension[0] == 'ExtendedKeyUsage':
        usages = []
        for ext_usage in extension[1].get('usages', []):
            usages.append(getattr(x509.oid.ExtendedKeyUsageOID, ext_usage))
        params = [usages]
    elif extension[0] == 'KeyUsage':
        params = [extension[1].get(k, False) for k in extensions()['KeyUsage']]
    elif extension[0] == 'AuthorityKeyIdentifier':
        params = [
            x509.SubjectKeyIdentifier.from_public_key(
                issuer.public_key() if issuer else cert._public_key
            ).digest if cert or issuer else None,
            None, None
        ]

        if extension[1]['authority_cert_issuer'] and cert:
            params[1:] = [
                [x509.DirectoryName(cert._issuer_name)],
                issuer.serial_number if issuer else cert._serial_number
            ]

    return params
예제 #8
0
    def generate_crl(self, ca, certs, next_update=1):
        # There is a tricky case here - what happens if the root CA is compromised ?
        # In normal world scenarios, that CA is removed from app's trust store and any
        # subsequent certs it had issues wouldn't be validated by the app then. Making a CRL
        # for a revoked root CA in normal cases doesn't make sense as the thief can sign a
        # counter CRL saying that everything is fine. As our environment is controlled,
        # i think we are safe to create a crl for root CA as well which we can publish for
        # services which make use of it i.e openvpn and they'll know that the certs/ca's have been
        # compromised.
        #
        # `ca` is root ca from where the chain `certs` starts.
        # `certs` is a list of all certs ca inclusive which are to be
        # included in the CRL ( if root ca is compromised, it will be in `certs` as well ).
        private_key = load_private_key(ca['privatekey'])
        ca_cert = x509.load_pem_x509_certificate(ca['certificate'].encode(),
                                                 default_backend())

        if not private_key:
            return None

        ca_data = self.middleware.call_sync('cryptokey.load_certificate',
                                            ca['certificate'])
        issuer = {k: ca_data.get(v) for k, v in CERT_BACKEND_MAPPINGS.items()}

        crl_builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(getattr(NameOID, k.upper()), v)
                for k, v in issuer.items() if v
            ])).last_update(datetime.datetime.utcnow()).next_update(
                datetime.datetime.utcnow() +
                datetime.timedelta(next_update, 300, 0))

        for cert in certs:
            crl_builder = crl_builder.add_revoked_certificate(
                x509.RevokedCertificateBuilder().serial_number(
                    self.middleware.call_sync(
                        'cryptokey.load_certificate',
                        cert['certificate'])['serial']).revocation_date(
                            cert['revoked_date']).build(default_backend()))

        # https://www.ietf.org/rfc/rfc5280.txt
        # We should add AuthorityKeyIdentifier and CRLNumber at the very least

        crl = crl_builder.add_extension(
            x509.AuthorityKeyIdentifier(
                x509.SubjectKeyIdentifier.from_public_key(
                    ca_cert.public_key()).digest,
                [
                    x509.DirectoryName(
                        x509.Name([
                            x509.NameAttribute(getattr(NameOID, k.upper()), v)
                            for k, v in issuer.items() if v
                        ]))
                ], ca_cert.serial_number),
            False).add_extension(x509.CRLNumber(1), False).sign(
                private_key=private_key,
                algorithm=retrieve_signing_algorithm({}, private_key),
                backend=default_backend())

        return crl.public_bytes(serialization.Encoding.PEM).decode()
예제 #9
0
def cryptography_get_name(name, what='Subject Alternative Name'):
    '''
    Given a name string, returns a cryptography x509.GeneralName object.
    Raises an OpenSSLObjectError if the name is unknown or cannot be parsed.
    '''
    try:
        if name.startswith('DNS:'):
            return x509.DNSName(to_text(name[4:]))
        if name.startswith('IP:'):
            address = to_text(name[3:])
            if '/' in address:
                return x509.IPAddress(ipaddress.ip_network(address))
            return x509.IPAddress(ipaddress.ip_address(address))
        if name.startswith('email:'):
            return x509.RFC822Name(to_text(name[6:]))
        if name.startswith('URI:'):
            return x509.UniformResourceIdentifier(to_text(name[4:]))
        if name.startswith('RID:'):
            m = re.match(r'^([0-9]+(?:\.[0-9]+)*)$', to_text(name[4:]))
            if not m:
                raise OpenSSLObjectError('Cannot parse {what} "{name}"'.format(
                    name=name, what=what))
            return x509.RegisteredID(x509.oid.ObjectIdentifier(m.group(1)))
        if name.startswith('otherName:'):
            # otherName can either be a raw ASN.1 hex string or in the format that OpenSSL works with.
            m = re.match(
                r'^([0-9]+(?:\.[0-9]+)*);([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2})*)$',
                to_text(name[10:]))
            if m:
                return x509.OtherName(x509.oid.ObjectIdentifier(m.group(1)),
                                      _parse_hex(m.group(2)))

            # See https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html - Subject Alternative Name for more
            # defailts on the format expected.
            name = to_text(name[10:], errors='surrogate_or_strict')
            if ';' not in name:
                raise OpenSSLObjectError(
                    'Cannot parse {what} otherName "{name}", must be in the '
                    'format "otherName:<OID>;<ASN.1 OpenSSL Encoded String>" or '
                    '"otherName:<OID>;<hex string>"'.format(name=name,
                                                            what=what))

            oid, value = name.split(';', 1)
            b_value = serialize_asn1_string_as_der(value)
            return x509.OtherName(x509.ObjectIdentifier(oid), b_value)
        if name.startswith('dirName:'):
            return x509.DirectoryName(x509.Name(_parse_dn(to_text(name[8:]))))
    except Exception as e:
        raise OpenSSLObjectError(
            'Cannot parse {what} "{name}": {error}'.format(name=name,
                                                           what=what,
                                                           error=e))
    if ':' not in name:
        raise OpenSSLObjectError(
            'Cannot parse {what} "{name}" (forgot "DNS:" prefix?)'.format(
                name=name, what=what))
    raise OpenSSLObjectError(
        'Cannot parse {what} "{name}" (potentially unsupported by cryptography backend)'
        .format(name=name, what=what))
예제 #10
0
def _build_general_name(backend, gn):
    if gn.type == backend._lib.GEN_DNS:
        data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:]
        return x509.DNSName(idna.decode(data))
    elif gn.type == backend._lib.GEN_URI:
        data = backend._ffi.buffer(
            gn.d.uniformResourceIdentifier.data,
            gn.d.uniformResourceIdentifier.length)[:].decode("ascii")
        parsed = urllib_parse.urlparse(data)
        hostname = idna.decode(parsed.hostname)
        if parsed.port:
            netloc = hostname + u":" + six.text_type(parsed.port)
        else:
            netloc = hostname

        # Note that building a URL in this fashion means it should be
        # semantically indistinguishable from the original but is not
        # guaranteed to be exactly the same.
        uri = urllib_parse.urlunparse(
            (parsed.scheme, netloc, parsed.path, parsed.params, parsed.query,
             parsed.fragment))
        return x509.UniformResourceIdentifier(uri)
    elif gn.type == backend._lib.GEN_RID:
        oid = _obj2txt(backend, gn.d.registeredID)
        return x509.RegisteredID(x509.ObjectIdentifier(oid))
    elif gn.type == backend._lib.GEN_IPADD:
        return x509.IPAddress(
            ipaddress.ip_address(
                backend._ffi.buffer(gn.d.iPAddress.data,
                                    gn.d.iPAddress.length)[:]))
    elif gn.type == backend._lib.GEN_DIRNAME:
        return x509.DirectoryName(_build_x509_name(backend,
                                                   gn.d.directoryName))
    elif gn.type == backend._lib.GEN_EMAIL:
        data = backend._ffi.buffer(gn.d.rfc822Name.data,
                                   gn.d.rfc822Name.length)[:].decode("ascii")
        name, address = parseaddr(data)
        parts = address.split(u"@")
        if name or len(parts) > 2 or not address:
            # parseaddr has found a name (e.g. Name <email>) or the split
            # has found more than 2 parts (which means more than one @ sign)
            # or the entire value is an empty string.
            raise ValueError("Invalid rfc822name value")
        elif len(parts) == 1:
            # Single label email name. This is valid for local delivery. No
            # IDNA decoding can be done since there is no domain component.
            return x509.RFC822Name(address)
        else:
            # A normal email of the form [email protected]. Let's attempt to
            # decode the domain component and return the entire address.
            return x509.RFC822Name(parts[0] + u"@" + idna.decode(parts[1]))
    else:
        # otherName, x400Address or ediPartyName
        raise x509.UnsupportedGeneralNameType(
            "{0} is not a supported type".format(
                x509._GENERAL_NAMES.get(gn.type, gn.type)), gn.type)
예제 #11
0
def _pyasn1_to_cryptography_directoryname(dn):
    attrs = []

    # Name is CHOICE { RDNSequence } (only one possibility)
    for rdn in dn.getComponent():
        for ava in rdn:
            attr = crypto_x509.NameAttribute(
                _pyasn1_to_cryptography_oid(ava['type']),
                unicode(decoder.decode(ava['value'])[0]))
            attrs.append(attr)

    return crypto_x509.DirectoryName(crypto_x509.Name(attrs))
예제 #12
0
def _decode_general_name(backend, gn):
    if gn.type == backend._lib.GEN_DNS:
        data = _asn1_string_to_bytes(backend, gn.d.dNSName)
        return x509.DNSName(data)
    elif gn.type == backend._lib.GEN_URI:
        data = _asn1_string_to_bytes(backend, gn.d.uniformResourceIdentifier)
        return x509.UniformResourceIdentifier(data)
    elif gn.type == backend._lib.GEN_RID:
        oid = _obj2txt(backend, gn.d.registeredID)
        return x509.RegisteredID(x509.ObjectIdentifier(oid))
    elif gn.type == backend._lib.GEN_IPADD:
        data = _asn1_string_to_bytes(backend, gn.d.iPAddress)
        data_len = len(data)
        if data_len == 8 or data_len == 32:
            # This is an IPv4 or IPv6 Network and not a single IP. This
            # type of data appears in Name Constraints. Unfortunately,
            # ipaddress doesn't support packed bytes + netmask. Additionally,
            # IPv6Network can only handle CIDR rather than the full 16 byte
            # netmask. To handle this we convert the netmask to integer, then
            # find the first 0 bit, which will be the prefix. If another 1
            # bit is present after that the netmask is invalid.
            base = ipaddress.ip_address(data[:data_len // 2])
            netmask = ipaddress.ip_address(data[data_len // 2:])
            bits = bin(int(netmask))[2:]
            prefix = bits.find('0')
            # If no 0 bits are found it is a /32 or /128
            if prefix == -1:
                prefix = len(bits)

            if "1" in bits[prefix:]:
                raise ValueError("Invalid netmask")

            ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
        else:
            ip = ipaddress.ip_address(data)

        return x509.IPAddress(ip)
    elif gn.type == backend._lib.GEN_DIRNAME:
        return x509.DirectoryName(
            _decode_x509_name(backend, gn.d.directoryName))
    elif gn.type == backend._lib.GEN_EMAIL:
        data = _asn1_string_to_bytes(backend, gn.d.rfc822Name)
        return x509.RFC822Name(data)
    elif gn.type == backend._lib.GEN_OTHERNAME:
        type_id = _obj2txt(backend, gn.d.otherName.type_id)
        value = _asn1_to_der(backend, gn.d.otherName.value)
        return x509.OtherName(x509.ObjectIdentifier(type_id), value)
    else:
        # x400Address or ediPartyName
        raise x509.UnsupportedGeneralNameType(
            "{0} is not a supported type".format(
                x509._GENERAL_NAMES.get(gn.type, gn.type)), gn.type)
예제 #13
0
def make_saml_cert(key, country='', state='', locality='', org='', cn='',
                   email='', alt_names=None, days=365):
    if isinstance(key, basestring):
        key = private_key_from_cleaned_text(key)
    if alt_names and isinstance(alt_names, basestring):
        alt_names = alt_names.decode('utf-8').split()
    subject = x509.Name([
        # Provide various details about who we are.
        x509.NameAttribute(NameOID.COUNTRY_NAME, country.decode('utf-8')),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state.decode('utf-8')),
        x509.NameAttribute(NameOID.LOCALITY_NAME, locality.decode('utf-8')),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, org.decode('utf-8')),
        x509.NameAttribute(NameOID.COMMON_NAME, cn.decode('utf-8')),
        x509.NameAttribute(NameOID.EMAIL_ADDRESS, email.decode('utf-8')),
    ])
    pkey = key.public_key()
    skid = x509.SubjectKeyIdentifier.from_public_key(pkey)
    # Create self-signed
    serial_number = random_serial_number()
    crt = x509.CertificateBuilder().subject_name(
        subject
    ).issuer_name(
        subject
    ).public_key(
        pkey
    ).serial_number(
        serial_number
    ).not_valid_before(
        datetime.datetime.utcnow()
    ).not_valid_after(
        # Our certificate will be valid for 10 days
        datetime.datetime.utcnow() + datetime.timedelta(days=days)
    ).add_extension(
        x509.BasicConstraints(True, 0), False
    ).add_extension(
        skid, False
    ).add_extension(
        x509.AuthorityKeyIdentifier(
            skid.digest, [x509.DirectoryName(subject)], serial_number), False
    )
    if alt_names:
        # Describe what sites we want this certificate for.
        crt = crt.add_extension(
            x509.SubjectAlternativeName([
                x509.DNSName(n) for n in altnames]),
            critical=False)
    crt = crt.sign(key, hashes.SHA256(), default_backend())
    crt_text = crt.public_bytes(serialization.Encoding.PEM)
    return (cleanup_x509_text(crt_text), crt)
예제 #14
0
def _decode_general_name(backend, gn):
  if gn.type == backend._lib.GEN_DNS:
    data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8")
    return x509.DNSName._init_without_validation(data)
  elif gn.type == backend._lib.GEN_URI:
    data = _asn1_string_to_bytes(
      backend, gn.d.uniformResourceIdentifier
    ).decode("utf8")

    return x509.UniformResourceIdentifier._init_without_validation(data)
  elif gn.type == backend._lib.GEN_RID:
    oid = _asn1_string_to_bytes(backend, gn.d.iPAddress)
    data_len = len(data)
    if data_len == 8 or data_len == 32:
      base = ipaddress.ip_address(data[:data_len // 2])
      netmask = ipaddress.ip_address(data[data_len // 2:])
      bits = bin(int(netmask))[2:]
      prefix = bits.find('0')

      if prefix == -1:
        prefix = len(bits)

      if "1" in bits[prefix]:
        raise ValueError("Invalid netmask")

      ip = piaddress.ip_network(base.exploded + u"/{}".format(prefix))
    else:
      ip = ipaddress.ip_address(data)

    return x509.IPAddress(ip)
  elif gn.type == backend._lib.GEN_DIRNAME:
    return x509.DirectoryName(
      _decode_x509_name(backend, gn.d.directoryName)
    )
  elif gn.type == backend._lib.GEN_EMAIL:
    data = _asn1_string_to_bytes(backend, gn.d.rfc822Name).decode("utf8")

    return x509.RFC822Name._init_without_validation(data)
  elif gn.type == backend._lib.GEN_OTHERNAME:
    type_id = _obj2txt(backend, gn.d.otherName.type_id)
    value = _asn1_to_der(backend, gn.d.otherName.value)
    return x509.OtherName(x509.ObjectIdentifier(type_id), value)
  else:
    raise x509.UnsupportedGeneralnameType(
      "{} is not a supported type".format(
        x509._GENERAL_NAMES.get(gn.type, gn.type)
      ),
      gn.type
    )
예제 #15
0
def gen_self_signed(key: rsa.RSAPrivateKey, cert_opts: Dict,
                    crypto_opts: Dict) -> None:  # noqa
    # subject / issuer
    subject = issuer = _subject(cert_opts)

    # init builder
    builder = x509.CertificateBuilder()

    # set subject and issuer
    builder = builder.subject_name(subject)
    builder = builder.issuer_name(issuer)

    # set public key
    builder = builder.public_key(key.public_key())

    # set serial number
    serial = x509.random_serial_number()
    builder = builder.serial_number(serial)

    # set expiration
    now = datetime.datetime.utcnow()
    days = cert_opts['days']
    builder = builder.not_valid_before(now)
    builder = builder.not_valid_after(now + datetime.timedelta(days=days))

    # add base extensions
    for is_critical, ext in _extensions(key, cert_opts):
        builder = builder.add_extension(ext, critical=is_critical)

    # add AuthorityKeyIdentifier extension (experimental feature)
    pkey = key.public_key()
    key_identifier = x509.extensions._key_identifier_from_public_key(pkey)
    authority_cert_issuer = [x509.DirectoryName(issuer)]
    builder = builder.add_extension(x509.AuthorityKeyIdentifier(
        key_identifier, authority_cert_issuer, serial),
                                    critical=False)

    # sign the certificate
    md_alg = MD_ALGS[crypto_opts['md_alg']]
    crt = builder.sign(key, md_alg)

    # write to file
    crt_out = crypto_opts['crt_out']
    with open(str(crt_out), "wb") as fp:
        fp.write(crt.public_bytes(serialization.Encoding.PEM))
        fp.close()
예제 #16
0
def selfsign_req(csr, issuer_pkey, serial=None, days=3650):
    if not serial:  # FIXME: 排重
        serial = random.getrandbits(64)
    cert = x509.CertificateBuilder()\
               .subject_name(csr.subject)\
               .issuer_name(csr.subject)\
               .public_key(csr.public_key())\
               .serial_number(serial)\
               .not_valid_before(datetime.utcnow())\
               .not_valid_after(datetime.utcnow()+timedelta(days=10))
    cert._extensions = csr.extensions._extensions
    subkeyid = x509.SubjectKeyIdentifier.from_public_key(csr.public_key())
    cert = cert.add_extension(subkeyid, critical=False)
    authkeyid = _key_identifier_from_public_key(issuer_pkey.public_key())
    authkeyid = x509.AuthorityKeyIdentifier(
        authkeyid, [x509.DirectoryName(csr.subject), ], serial)
    cert = cert.add_extension(authkeyid, critical=False)
    return cert.sign(issuer_pkey, hashes.SHA256(), default_backend())
예제 #17
0
파일: util.py 프로젝트: zeus911/AutoSSL
def create_ca_certificate(ca_name,
                          key_size=4096,
                          certificate_validity_days=365):
    key = rsa.generate_private_key(public_exponent=65537,
                                   key_size=key_size,
                                   backend=default_backend())
    key_id = x509.SubjectKeyIdentifier.from_public_key(key.public_key())

    subject = issuer = x509.Name(
        [x509.NameAttribute(NameOID.COMMON_NAME, u"%s" % ca_name)])

    now = datetime.datetime.utcnow()
    serial = x509.random_serial_number()
    cert = x509.CertificateBuilder() \
        .subject_name(subject) \
        .issuer_name(issuer) \
        .public_key(key.public_key()) \
        .serial_number(serial) \
        .not_valid_before(now) \
        .not_valid_after(now + datetime.timedelta(days=certificate_validity_days)) \
        .add_extension(key_id, critical=False) \
        .add_extension(x509.AuthorityKeyIdentifier(key_id.digest,
                                                   [x509.DirectoryName(issuer)],
                                                   serial),
                       critical=False) \
        .add_extension(x509.BasicConstraints(ca=True, path_length=3), critical=True) \
        .add_extension(x509.KeyUsage(digital_signature=True,
                                     content_commitment=False,
                                     key_encipherment=False,
                                     data_encipherment=False,
                                     key_agreement=False,
                                     key_cert_sign=True,
                                     crl_sign=True,
                                     encipher_only=False,
                                     decipher_only=False),
                       critical=True) \
        .sign(key, hashes.SHA256(), default_backend())

    cert = cert.public_bytes(serialization.Encoding.PEM)
    key = key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption())
    return key, cert
def cryptography_get_name(name):
    '''
    Given a name string, returns a cryptography x509.Name object.
    Raises an OpenSSLObjectError if the name is unknown or cannot be parsed.
    '''
    try:
        if name.startswith('DNS:'):
            return x509.DNSName(to_text(name[4:]))
        if name.startswith('IP:'):
            return x509.IPAddress(ipaddress.ip_address(to_text(name[3:])))
        if name.startswith('email:'):
            return x509.RFC822Name(to_text(name[6:]))
        if name.startswith('URI:'):
            return x509.UniformResourceIdentifier(to_text(name[4:]))
        if name.startswith('RID:'):
            m = re.match(r'^([0-9]+(?:\.[0-9]+)*)$', to_text(name[4:]))
            if not m:
                raise OpenSSLObjectError(
                    'Cannot parse Subject Alternative Name "{0}"'.format(name))
            return x509.RegisteredID(x509.oid.ObjectIdentifier(m.group(1)))
        if name.startswith('otherName:'):
            m = re.match(
                r'^([0-9]+(?:\.[0-9]+)*);([0-9a-fA-F]{1,2}(?::[0-9a-fA-F]{1,2})*)$',
                to_text(name[10:]))
            if not m:
                raise OpenSSLObjectError(
                    'Cannot parse Subject Alternative Name "{0}"'.format(name))
            return x509.OtherName(x509.oid.ObjectIdentifier(m.group(1)),
                                  _parse_hex(m.group(2)))
        if name.startswith('dirName:'):
            return x509.DirectoryName(x509.Name(_parse_dn(to_text(name[8:]))))
    except Exception as e:
        raise OpenSSLObjectError(
            'Cannot parse Subject Alternative Name "{0}": {1}'.format(name, e))
    if ':' not in name:
        raise OpenSSLObjectError(
            'Cannot parse Subject Alternative Name "{0}" (forgot "DNS:" prefix?)'
            .format(name))
    raise OpenSSLObjectError(
        'Cannot parse Subject Alternative Name "{0}" (potentially unsupported by cryptography backend)'
        .format(name))
예제 #19
0
def parse_general_name(name):
    """Parse a general name from user input.

    This function will do its best to detect the intended type of any value passed to it:

    >>> parse_general_name('example.com')
    <DNSName(value='example.com')>
    >>> parse_general_name('*.example.com')
    <DNSName(value='*.example.com')>
    >>> parse_general_name('.example.com')  # Syntax used e.g. for NameConstraints: All levels of subdomains
    <DNSName(value='.example.com')>
    >>> parse_general_name('*****@*****.**')
    <RFC822Name(value='*****@*****.**')>
    >>> parse_general_name('https://example.com')
    <UniformResourceIdentifier(value='https://example.com')>
    >>> parse_general_name('1.2.3.4')
    <IPAddress(value=1.2.3.4)>
    >>> parse_general_name('fd00::1')
    <IPAddress(value=fd00::1)>
    >>> parse_general_name('/CN=example.com')
    <DirectoryName(value=<Name(CN=example.com)>)>

    The default fallback is to assume a :py:class:`~cg:cryptography.x509.DNSName`. If this doesn't
    work, an exception will be raised:

    >>> parse_general_name('foo..bar`*123')  # doctest: +ELLIPSIS
    Traceback (most recent call last):
        ...
    idna.core.IDNAError: ...

    If you want to override detection, you can prefix the name to match :py:const:`GENERAL_NAME_RE`:

    >>> parse_general_name('email:[email protected]')
    <RFC822Name(value='*****@*****.**')>
    >>> parse_general_name('URI:https://example.com')
    <UniformResourceIdentifier(value='https://example.com')>
    >>> parse_general_name('dirname:/CN=example.com')
    <DirectoryName(value=<Name(CN=example.com)>)>

    Some more exotic values can only be generated by using this prefix:

    >>> parse_general_name('rid:2.5.4.3')
    <RegisteredID(value=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>)>
    >>> parse_general_name('otherName:2.5.4.3;UTF8:example.com')
    <OtherName(type_id=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=b'example.com')>

    If you give a prefixed value, this function is less forgiving of any typos and does not catch any
    exceptions:

    >>> parse_general_name('email:foo@bar com')
    Traceback (most recent call last):
        ...
    ValueError: Invalid domain: bar com

    """
    name = force_text(name)
    typ = None
    match = GENERAL_NAME_RE.match(name)
    if match is not None:
        typ, name = match.groups()
        typ = typ.lower()

    if typ is None:
        if re.match('[a-z0-9]{2,}://', name):  # Looks like a URI
            try:
                return x509.UniformResourceIdentifier(name)
            except Exception:  # pragma: no cover - this really accepts anything
                pass

        if '@' in name:  # Looks like an Email address
            try:
                return x509.RFC822Name(validate_email(name))
            except Exception:
                pass

        if name.strip().startswith('/'):  # maybe it's a dirname?
            return x509.DirectoryName(x509_name(name))

        # Try to parse this as IPAddress/Network
        try:
            return x509.IPAddress(ip_address(name))
        except ValueError:
            pass
        try:
            return x509.IPAddress(ip_network(name))
        except ValueError:
            pass

        # Try to encode as domain name. DNSName() does not validate the domain name, but this check will fail.
        if name.startswith('*.'):
            idna.encode(name[2:])
        elif name.startswith('.'):
            idna.encode(name[1:])
        else:
            idna.encode(name)

        # Almost anything passes as DNS name, so this is our default fallback
        return x509.DNSName(name)

    if typ == 'uri':
        return x509.UniformResourceIdentifier(name)
    elif typ == 'email':
        return x509.RFC822Name(validate_email(name))
    elif typ == 'ip':
        try:
            return x509.IPAddress(ip_address(name))
        except ValueError:
            pass

        try:
            return x509.IPAddress(ip_network(name))
        except ValueError:
            pass

        raise ValueError('Could not parse IP address.')
    elif typ == 'rid':
        return x509.RegisteredID(x509.ObjectIdentifier(name))
    elif typ == 'othername':
        regex = "(.*);(.*):(.*)"
        if re.match(regex, name) is not None:
            oid, asn_typ, val = re.match(regex, name).groups()
            oid = x509.ObjectIdentifier(oid)
            if asn_typ == 'UTF8':
                val = val.encode('utf-8')
            elif asn_typ == 'OctetString':
                val = bytes(bytearray.fromhex(val))
                val = OctetString(val).dump()
            else:
                raise ValueError('Unsupported ASN type in otherName: %s' %
                                 asn_typ)
            val = force_bytes(val)
            return x509.OtherName(oid, val)
        else:
            raise ValueError('Incorrect otherName format: %s' % name)
    elif typ == 'dirname':
        return x509.DirectoryName(x509_name(name))
    else:
        # Try to encode the domain name. DNSName() does not validate the domain name, but this
        # check will fail.
        if name.startswith('*.'):
            idna.encode(name[2:])
        elif name.startswith('.'):
            idna.encode(name[1:])
        else:
            idna.encode(name)

        return x509.DNSName(name)
예제 #20
0
def _new_cert(issuer=None, is_issuer=False, serial_number=None, **subject):
    backend = backends.default_backend()
    private_key = rsa.generate_private_key(public_exponent=65537,
                                           key_size=4096,
                                           backend=backend)
    public_key = private_key.public_key()
    subject = x509.Name([
        x509.NameAttribute(getattr(oid.NameOID, key.upper()), value)
        for key, value in subject.items()
    ])
    builder = (x509.CertificateBuilder().subject_name(subject).public_key(
        public_key).serial_number(serial_number
                                  or int.from_bytes(os.urandom(8), "big")))
    if issuer:
        issuer_cert, signing_key = issuer
        builder = (builder.issuer_name(issuer_cert.subject).not_valid_before(
            issuer_cert.not_valid_before).not_valid_after(
                issuer_cert.not_valid_after))
        aki_ext = x509.AuthorityKeyIdentifier(
            key_identifier=issuer_cert.extensions.get_extension_for_class(
                x509.SubjectKeyIdentifier).value.digest,
            authority_cert_issuer=[x509.DirectoryName(issuer_cert.subject)],
            authority_cert_serial_number=issuer_cert.serial_number,
        )
    else:
        signing_key = private_key
        builder = (builder.issuer_name(subject).not_valid_before(
            datetime.datetime.today() - datetime.timedelta(
                days=1)).not_valid_after(datetime.datetime.today() +
                                         datetime.timedelta(weeks=1000)))
        aki_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(
            public_key)
    if is_issuer:
        builder = (builder.add_extension(
            x509.BasicConstraints(ca=True, path_length=None),
            critical=False,
        ).add_extension(
            x509.SubjectKeyIdentifier.from_public_key(public_key),
            critical=False,
        ).add_extension(
            aki_ext,
            critical=False,
        ))
    else:
        builder = (builder.add_extension(
            x509.KeyUsage(
                digital_signature=True,
                content_commitment=False,
                key_encipherment=True,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=False,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False,
            ),
            critical=False,
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None),
            critical=False,
        ).add_extension(
            x509.ExtendedKeyUsage([oid.ExtendedKeyUsageOID.SERVER_AUTH]),
            critical=False,
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName("localhost")]),
            critical=False,
        ).add_extension(
            x509.SubjectKeyIdentifier.from_public_key(public_key),
            critical=False,
        ).add_extension(
            aki_ext,
            critical=False,
        ))
    certificate = builder.sign(
        private_key=signing_key,
        algorithm=hashes.SHA256(),
        backend=backend,
    )
    return certificate, private_key
예제 #21
0
def _decode_general_name(backend, gn):
    if gn.type == backend._lib.GEN_DNS:
        # Convert to bytes and then decode to utf8. We don't use
        # asn1_string_to_utf8 here because it doesn't properly convert
        # utf8 from ia5strings.
        data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8")
        # We don't use the constructor for DNSName so we can bypass validation
        # This allows us to create DNSName objects that have unicode chars
        # when a certificate (against the RFC) contains them.
        return x509.DNSName._init_without_validation(data)
    elif gn.type == backend._lib.GEN_URI:
        # Convert to bytes and then decode to utf8. We don't use
        # asn1_string_to_utf8 here because it doesn't properly convert
        # utf8 from ia5strings.
        data = _asn1_string_to_bytes(
            backend, gn.d.uniformResourceIdentifier).decode("utf8")
        # We don't use the constructor for URI so we can bypass validation
        # This allows us to create URI objects that have unicode chars
        # when a certificate (against the RFC) contains them.
        return x509.UniformResourceIdentifier._init_without_validation(data)
    elif gn.type == backend._lib.GEN_RID:
        oid = _obj2txt(backend, gn.d.registeredID)
        return x509.RegisteredID(x509.ObjectIdentifier(oid))
    elif gn.type == backend._lib.GEN_IPADD:
        data = _asn1_string_to_bytes(backend, gn.d.iPAddress)
        data_len = len(data)
        if data_len == 8 or data_len == 32:
            # This is an IPv4 or IPv6 Network and not a single IP. This
            # type of data appears in Name Constraints. Unfortunately,
            # ipaddress doesn't support packed bytes + netmask. Additionally,
            # IPv6Network can only handle CIDR rather than the full 16 byte
            # netmask. To handle this we convert the netmask to integer, then
            # find the first 0 bit, which will be the prefix. If another 1
            # bit is present after that the netmask is invalid.
            base = ipaddress.ip_address(data[:data_len // 2])
            netmask = ipaddress.ip_address(data[data_len // 2:])
            bits = bin(int(netmask))[2:]
            prefix = bits.find("0")
            # If no 0 bits are found it is a /32 or /128
            if prefix == -1:
                prefix = len(bits)

            if "1" in bits[prefix:]:
                raise ValueError("Invalid netmask")

            ip = ipaddress.ip_network(base.exploded + "/{}".format(prefix))
        else:
            ip = ipaddress.ip_address(data)

        return x509.IPAddress(ip)
    elif gn.type == backend._lib.GEN_DIRNAME:
        return x509.DirectoryName(
            _decode_x509_name(backend, gn.d.directoryName))
    elif gn.type == backend._lib.GEN_EMAIL:
        # Convert to bytes and then decode to utf8. We don't use
        # asn1_string_to_utf8 here because it doesn't properly convert
        # utf8 from ia5strings.
        data = _asn1_string_to_bytes(backend, gn.d.rfc822Name).decode("utf8")
        # We don't use the constructor for RFC822Name so we can bypass
        # validation. This allows us to create RFC822Name objects that have
        # unicode chars when a certificate (against the RFC) contains them.
        return x509.RFC822Name._init_without_validation(data)
    elif gn.type == backend._lib.GEN_OTHERNAME:
        type_id = _obj2txt(backend, gn.d.otherName.type_id)
        value = _asn1_to_der(backend, gn.d.otherName.value)
        return x509.OtherName(x509.ObjectIdentifier(type_id), value)
    else:
        # x400Address or ediPartyName
        raise x509.UnsupportedGeneralNameType(
            "{} is not a supported type".format(
                x509._GENERAL_NAMES.get(gn.type, gn.type)),
            gn.type,
        )
예제 #22
0
파일: plugin.py 프로젝트: sotnasf/lemur
def issue_certificate(csr, options, private_key=None):
    csr = x509.load_pem_x509_csr(csr.encode('utf-8'), default_backend())

    if options.get("parent"):
        # creating intermediate authorities will have options['parent'] to specify the issuer
        # creating certificates will have options['authority'] to specify the issuer
        # This works around that by making sure options['authority'] can be referenced for either
        options['authority'] = options['parent']

    if options.get("authority"):
        # Issue certificate signed by an existing lemur_certificates authority
        issuer_subject = options['authority'].authority_certificate.subject
        issuer_private_key = options['authority'].authority_certificate.private_key
        chain_cert_pem = options['authority'].authority_certificate.body
        authority_key_identifier_public = options['authority'].authority_certificate.public_key
        authority_key_identifier_subject = x509.SubjectKeyIdentifier.from_public_key(authority_key_identifier_public)
        authority_key_identifier_issuer = issuer_subject
        authority_key_identifier_serial = int(options['authority'].authority_certificate.serial)
        # TODO figure out a better way to increment serial
        # New authorities have a value at options['serial_number'] that is being ignored here.
        serial = int(uuid.uuid4())
    else:
        # Issue certificate that is self-signed (new lemur_certificates root authority)
        issuer_subject = csr.subject
        issuer_private_key = private_key
        chain_cert_pem = ""
        authority_key_identifier_public = csr.public_key()
        authority_key_identifier_subject = None
        authority_key_identifier_issuer = csr.subject
        authority_key_identifier_serial = options['serial_number']
        # TODO figure out a better way to increment serial
        serial = int(uuid.uuid4())

    extensions = normalize_extensions(csr)

    builder = x509.CertificateBuilder(
        issuer_name=issuer_subject,
        subject_name=csr.subject,
        public_key=csr.public_key(),
        not_valid_before=options['validity_start'],
        not_valid_after=options['validity_end'],
        serial_number=serial,
        extensions=extensions)

    for k, v in options.get('extensions', {}).items():
        if k == 'authority_key_identifier':
            # One or both of these options may be present inside the aki extension
            (authority_key_identifier, authority_identifier) = (False, False)
            for k2, v2 in v.items():
                if k2 == 'use_key_identifier' and v2:
                    authority_key_identifier = True
                if k2 == 'use_authority_cert' and v2:
                    authority_identifier = True
            if authority_key_identifier:
                if authority_key_identifier_subject:
                    # FIXME in python-cryptography.
                    # from_issuer_subject_key_identifier(cls, ski) is looking for ski.value.digest
                    # but the digest of the ski is at just ski.digest. Until that library is fixed,
                    # this function won't work. The second line has the same result.
                    # aki = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(authority_key_identifier_subject)
                    aki = x509.AuthorityKeyIdentifier(authority_key_identifier_subject.digest, None, None)
                else:
                    aki = x509.AuthorityKeyIdentifier.from_issuer_public_key(authority_key_identifier_public)
            elif authority_identifier:
                aki = x509.AuthorityKeyIdentifier(None, [x509.DirectoryName(authority_key_identifier_issuer)], authority_key_identifier_serial)
            builder = builder.add_extension(aki, critical=False)
        if k == 'certificate_info_access':
            # FIXME: Implement the AuthorityInformationAccess extension
            # descriptions = [
            #     x509.AccessDescription(x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier(u"http://FIXME")),
            #     x509.AccessDescription(x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier(u"http://FIXME"))
            # ]
            # for k2, v2 in v.items():
            #     if k2 == 'include_aia' and v2 == True:
            #         builder = builder.add_extension(
            #             x509.AuthorityInformationAccess(descriptions),
            #             critical=False
            #         )
            pass
        if k == 'crl_distribution_points':
            # FIXME: Implement the CRLDistributionPoints extension
            # FIXME: Not implemented in lemur/schemas.py yet https://github.com/Netflix/lemur/issues/662
            pass

    private_key = serialization.load_pem_private_key(
        bytes(str(issuer_private_key).encode('utf-8')),
        password=None,
        backend=default_backend()
    )

    cert = builder.sign(private_key, hashes.SHA256(), default_backend())
    cert_pem = cert.public_bytes(
        encoding=serialization.Encoding.PEM
    ).decode('utf-8')

    return cert_pem, chain_cert_pem
예제 #23
0
    def get_ca_cert(self, key=None):
        """Get the CA Certificate.

        Args:
            key (cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
                A key in cryptography RSAPrivateKey format.

        Returns:
            cryptography.x509.Certificate: The CA Certificate.
        Returns:
            cryptography.x509.Certificate: CA Certificate
        """
        ca_cert_path = self.config.get("ca", "cert")
        if not os.path.isabs(ca_cert_path):
            ca_cert_path = os.path.abspath(
                os.path.join(os.path.dirname(__file__), ca_cert_path))

        # Grab the CA Certificate from filesystem if it exists and return
        if os.path.isfile(ca_cert_path):
            with open(ca_cert_path, "rb") as cert_file:
                ca_cert = x509.load_pem_x509_certificate(
                    cert_file.read(), default_backend())
                return ca_cert

        if key is None:
            raise CertProcessorKeyNotFoundError()

        key_id = x509.SubjectKeyIdentifier.from_public_key(key.public_key())
        subject = issuer = x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME,
                               self.config.get("ca", "issuer"))
        ])
        now = datetime.datetime.utcnow()
        serial = x509.random_serial_number()
        ca_cert = (x509.CertificateBuilder().subject_name(subject).issuer_name(
            issuer).public_key(
                key.public_key()).serial_number(serial).not_valid_before(now).
                   not_valid_after(now +
                                   datetime.timedelta(days=365)).add_extension(
                                       key_id, critical=False).add_extension(
                                           x509.AuthorityKeyIdentifier(
                                               key_id.digest,
                                               [x509.DirectoryName(issuer)],
                                               serial),
                                           critical=False,
                                       ).add_extension(
                                           x509.BasicConstraints(
                                               ca=True, path_length=0),
                                           critical=True).add_extension(
                                               x509.KeyUsage(
                                                   digital_signature=True,
                                                   content_commitment=False,
                                                   key_encipherment=False,
                                                   data_encipherment=False,
                                                   key_agreement=False,
                                                   key_cert_sign=True,
                                                   crl_sign=True,
                                                   encipher_only=False,
                                                   decipher_only=False,
                                               ),
                                               critical=True,
                                           ).sign(key, hashes.SHA256(),
                                                  default_backend()))
        with open(ca_cert_path, "wb") as f:
            f.write(ca_cert.public_bytes(serialization.Encoding.PEM))
        return ca_cert
예제 #24
0
def make_saml_cert(key,
                   country=None,
                   state=None,
                   locality=None,
                   org=None,
                   domain=None,
                   email=None,
                   alt_names=None,
                   days=365,
                   self_sign=True):
    if isinstance(key, basestring):
        key = private_key_from_cleaned_text(key)
    if alt_names and isinstance(alt_names, basestring):
        alt_names = alt_names.split()
    name_components = []
    if country:
        name_components.append(
            x509.NameAttribute(NameOID.COUNTRY_NAME, country))
    if state:
        name_components.append(
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state))
    if locality:
        name_components.append(
            x509.NameAttribute(NameOID.LOCALITY_NAME, locality))
    if org:
        name_components.append(
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, org))
    if domain:
        name_components.append(x509.NameAttribute(NameOID.COMMON_NAME, domain))
    if email:
        name_components.append(x509.NameAttribute(NameOID.EMAIL_ADDRESS,
                                                  email))
    subject = x509.Name(name_components)
    pkey = key.public_key()
    skid = x509.SubjectKeyIdentifier.from_public_key(pkey)
    if self_sign:
        # Create self-signed
        serial_number = random_serial_number()
        builder = x509.CertificateBuilder()
    else:
        builder = x509.CertificateSigningRequestBuilder()
    builder = builder.subject_name(subject).add_extension(
        x509.KeyUsage(True, False, True, True, False, False, False, False,
                      False), False)
    if alt_names:
        # Describe what sites we want this certificate for.
        builder = builder.add_extension(x509.SubjectAlternativeName(
            [x509.DNSName(n) for n in alt_names]),
                                        critical=False)
    if self_sign:
        builder = builder.public_key(pkey).issuer_name(subject).serial_number(
            serial_number).not_valid_before(
                datetime.datetime.utcnow()).not_valid_after(
                    datetime.datetime.utcnow() +
                    datetime.timedelta(days=days)).add_extension(
                        skid, False).add_extension(
                            x509.AuthorityKeyIdentifier(
                                skid.digest, [x509.DirectoryName(subject)],
                                serial_number), False)
    else:
        builder = builder.add_extension(x509.BasicConstraints(True, 0), False)

    builder = builder.sign(key, hashes.SHA256(), default_backend())
    builder_text = builder.public_bytes(serialization.Encoding.PEM)
    return (builder_text, builder)
예제 #25
0
파일: utils.py 프로젝트: psydox/django-ca
def parse_general_name(name: ParsableGeneralName) -> x509.GeneralName:
    """Parse a general name from user input.

    This function will do its best to detect the intended type of any value passed to it:

    >>> parse_general_name('example.com')
    <DNSName(value='example.com')>
    >>> parse_general_name('*.example.com')
    <DNSName(value='*.example.com')>
    >>> parse_general_name('.example.com')  # Syntax used e.g. for NameConstraints: All levels of subdomains
    <DNSName(value='.example.com')>
    >>> parse_general_name('*****@*****.**')
    <RFC822Name(value='*****@*****.**')>
    >>> parse_general_name('https://example.com')
    <UniformResourceIdentifier(value='https://example.com')>
    >>> parse_general_name('1.2.3.4')
    <IPAddress(value=1.2.3.4)>
    >>> parse_general_name('fd00::1')
    <IPAddress(value=fd00::1)>
    >>> parse_general_name('/CN=example.com')
    <DirectoryName(value=<Name(CN=example.com)>)>

    The default fallback is to assume a :py:class:`~cg:cryptography.x509.DNSName`. If this doesn't
    work, an exception will be raised:

    >>> parse_general_name('foo..bar`*123')  # doctest: +ELLIPSIS
    Traceback (most recent call last):
        ...
    ValueError: Could not parse name: foo..bar`*123

    If you want to override detection, you can prefix the name to match :py:const:`GENERAL_NAME_RE`:

    >>> parse_general_name('email:[email protected]')
    <RFC822Name(value='*****@*****.**')>
    >>> parse_general_name('URI:https://example.com')
    <UniformResourceIdentifier(value='https://example.com')>
    >>> parse_general_name('dirname:/CN=example.com')
    <DirectoryName(value=<Name(CN=example.com)>)>

    Some more exotic values can only be generated by using this prefix:

    >>> parse_general_name('rid:2.5.4.3')
    <RegisteredID(value=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>)>
    >>> parse_general_name('otherName:2.5.4.3;UTF8:example.com')
    <OtherName(type_id=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=b'example.com')>

    If you give a prefixed value, this function is less forgiving of any typos and does not catch any
    exceptions:

    >>> parse_general_name('email:foo@bar com')
    Traceback (most recent call last):
        ...
    ValueError: Invalid domain: bar com

    """
    # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements

    if isinstance(name, x509.GeneralName):
        return name
    if not isinstance(name, str):
        raise ValueError(
            "Cannot parse general name %s: Must be of type str (was: %s)." % (name, type(name).__name__)
        )

    typ = None
    match = GENERAL_NAME_RE.match(name)
    if match is not None:
        typ, name = match.groups()
        typ = typ.lower()

    if typ is None:
        if re.match("[a-z0-9]{2,}://", name):  # Looks like a URI
            try:
                return x509.UniformResourceIdentifier(encode_url(name))
            except idna.IDNAError:
                pass

        if "@" in name:  # Looks like an Email address
            try:
                return x509.RFC822Name(validate_email(name))
            except ValueError:
                pass

        if name.strip().startswith("/"):  # maybe it's a dirname?
            return x509.DirectoryName(x509_name(name))

        # Try to parse this as IPAddress/Network
        try:
            return x509.IPAddress(ip_address(name))
        except ValueError:
            pass
        try:
            return x509.IPAddress(ip_network(name))
        except ValueError:
            pass

        # Almost anything passes as DNS name, so this is our default fallback
        try:
            return x509.DNSName(encode_dns(name))
        except idna.IDNAError as e:
            raise ValueError("Could not parse name: %s" % name) from e

    if typ == "uri":
        try:
            return x509.UniformResourceIdentifier(encode_url(name))
        except idna.IDNAError as e:
            raise ValueError("Could not parse DNS name in URL: %s" % name) from e
    elif typ == "email":
        return x509.RFC822Name(validate_email(name))  # validate_email already raises ValueError
    elif typ == "ip":
        try:
            return x509.IPAddress(ip_address(name))
        except ValueError:
            pass

        try:
            return x509.IPAddress(ip_network(name))
        except ValueError:
            pass

        raise ValueError("Could not parse IP address.")
    elif typ == "rid":
        return x509.RegisteredID(x509.ObjectIdentifier(name))
    elif typ == "othername":
        match = re.match("(.*);(.*):(.*)", name)
        if match is not None:
            oid, asn_typ, val = match.groups()
            if asn_typ == "UTF8":
                parsed_value = val.encode("utf-8")
            elif asn_typ == "OctetString":
                parsed_value = OctetString(bytes(bytearray.fromhex(val))).dump()
            else:
                raise ValueError("Unsupported ASN type in otherName: %s" % asn_typ)

            return x509.OtherName(x509.ObjectIdentifier(oid), parsed_value)

        raise ValueError("Incorrect otherName format: %s" % name)
    elif typ == "dirname":
        return x509.DirectoryName(x509_name(name))
    else:
        try:
            return x509.DNSName(encode_dns(name))
        except idna.IDNAError as e:
            raise ValueError("Could not parse DNS name: %s" % name) from e
예제 #26
0
def _decode_general_name(backend, gn):
    if gn.type == backend._lib.GEN_DNS:
        data = _asn1_string_to_bytes(backend, gn.d.dNSName)
        if data.startswith(b"*."):
            # This is a wildcard name. We need to remove the leading wildcard,
            # IDNA decode, then re-add the wildcard. Wildcard characters should
            # always be left-most (RFC 2595 section 2.4).
            decoded = u"*." + idna.decode(data[2:])
        else:
            # Not a wildcard, decode away. If the string has a * in it anywhere
            # invalid this will raise an InvalidCodePoint
            decoded = idna.decode(data)
            if data.startswith(b"."):
                # idna strips leading periods. Name constraints can have that
                # so we need to re-add it. Sigh.
                decoded = u"." + decoded

        return x509.DNSName(decoded)
    elif gn.type == backend._lib.GEN_URI:
        data = _asn1_string_to_ascii(backend, gn.d.uniformResourceIdentifier)
        parsed = urllib_parse.urlparse(data)
        hostname = idna.decode(parsed.hostname)
        if parsed.port:
            netloc = hostname + u":" + six.text_type(parsed.port)
        else:
            netloc = hostname

        # Note that building a URL in this fashion means it should be
        # semantically indistinguishable from the original but is not
        # guaranteed to be exactly the same.
        uri = urllib_parse.urlunparse((
            parsed.scheme,
            netloc,
            parsed.path,
            parsed.params,
            parsed.query,
            parsed.fragment
        ))
        return x509.UniformResourceIdentifier(uri)
    elif gn.type == backend._lib.GEN_RID:
        oid = _obj2txt(backend, gn.d.registeredID)
        return x509.RegisteredID(x509.ObjectIdentifier(oid))
    elif gn.type == backend._lib.GEN_IPADD:
        data = _asn1_string_to_bytes(backend, gn.d.iPAddress)
        data_len = len(data)
        if data_len == 8 or data_len == 32:
            # This is an IPv4 or IPv6 Network and not a single IP. This
            # type of data appears in Name Constraints. Unfortunately,
            # ipaddress doesn't support packed bytes + netmask. Additionally,
            # IPv6Network can only handle CIDR rather than the full 16 byte
            # netmask. To handle this we convert the netmask to integer, then
            # find the first 0 bit, which will be the prefix. If another 1
            # bit is present after that the netmask is invalid.
            base = ipaddress.ip_address(data[:data_len // 2])
            netmask = ipaddress.ip_address(data[data_len // 2:])
            bits = bin(int(netmask))[2:]
            prefix = bits.find('0')
            # If no 0 bits are found it is a /32 or /128
            if prefix == -1:
                prefix = len(bits)

            if "1" in bits[prefix:]:
                raise ValueError("Invalid netmask")

            ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
        else:
            ip = ipaddress.ip_address(data)

        return x509.IPAddress(ip)
    elif gn.type == backend._lib.GEN_DIRNAME:
        return x509.DirectoryName(
            _decode_x509_name(backend, gn.d.directoryName)
        )
    elif gn.type == backend._lib.GEN_EMAIL:
        data = _asn1_string_to_ascii(backend, gn.d.rfc822Name)
        name, address = parseaddr(data)
        parts = address.split(u"@")
        if name or not address:
            # parseaddr has found a name (e.g. Name <email>) or the entire
            # value is an empty string.
            raise ValueError("Invalid rfc822name value")
        elif len(parts) == 1:
            # Single label email name. This is valid for local delivery. No
            # IDNA decoding can be done since there is no domain component.
            return x509.RFC822Name(address)
        else:
            # A normal email of the form [email protected]. Let's attempt to
            # decode the domain component and return the entire address.
            return x509.RFC822Name(
                parts[0] + u"@" + idna.decode(parts[1])
            )
    elif gn.type == backend._lib.GEN_OTHERNAME:
        type_id = _obj2txt(backend, gn.d.otherName.type_id)
        value = _asn1_to_der(backend, gn.d.otherName.value)
        return x509.OtherName(x509.ObjectIdentifier(type_id), value)
    else:
        # x400Address or ediPartyName
        raise x509.UnsupportedGeneralNameType(
            "{0} is not a supported type".format(
                x509._GENERAL_NAMES.get(gn.type, gn.type)
            ),
            gn.type
        )
예제 #27
0
    def test_not_name(self):
        with pytest.raises(TypeError):
            x509.DirectoryName(b"notaname")

        with pytest.raises(TypeError):
            x509.DirectoryName(1.3)
예제 #28
0
    def generate(self,
                 csr,
                 issuer_crt,
                 issuer_key,
                 profile,
                 ca=False,
                 selfSigned=False,
                 start=None,
                 duration=None,
                 digest=None,
                 sans=[]):
        """Generate a certificate using:
            - Certificate request (csr)
            - Issuer certificate (issuer_crt)
            - Issuer key (issuer_key)
            - profile object (profile)
        Optional parameters set:
            - a CA certificate role (ca)
            - a self-signed certificate (selfSigned)
            - a specific start timestamp (start) 
        """

        # Retrieve subject from csr
        subject = csr.subject
        self.output('Subject found: {s}'.format(s=subject.rfc4514_string()),
                    level="DEBUG")
        dn = self._get_dn(subject)
        self.output('DN found is {d}'.format(d=dn), level="DEBUG")

        try:
            alt_names = None
            alt_names = csr.extensions.get_extension_for_oid(
                ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
            self.output('Subject alternate found: {s}'.format(s=alt_names),
                        level="DEBUG")
        except x509.ExtensionNotFound as err:
            pass

        # Force default if necessary
        now = datetime.datetime.utcnow(
        ) if start is None else datetime.fromtimestamp(start)
        duration = profile['duration'] if duration is None else duration

        # Generate serial number
        try:
            serial_number = self._generate_serial()
        except Exception as err:
            raise Exception(
                'Error during serial number generation: {e}'.format(e=err))

        # For self-signed certificate issuer is certificate itself
        issuer_name = subject if selfSigned else issuer_crt.issuer
        issuer_serial = serial_number if selfSigned else issuer_crt.serial_number

        try:
            # Define basic constraints
            if ca:
                basic_contraints = x509.BasicConstraints(ca=True,
                                                         path_length=0)
            else:
                basic_contraints = x509.BasicConstraints(ca=False,
                                                         path_length=None)
            builder = (x509.CertificateBuilder().subject_name(
                subject).issuer_name(issuer_name).public_key(
                    csr.public_key()).serial_number(
                        serial_number).not_valid_before(now).not_valid_after(
                            now +
                            datetime.timedelta(days=duration)).add_extension(
                                basic_contraints, critical=True))
        except Exception as err:
            raise Exception('Unable to build structure: {e}'.format(e=err))

        # We never trust CSR extensions
        # they may have been alterated by the user
        try:
            # Due to uPKI design (TLS for renew), digital_signature MUST be setup
            digital_signature = True
            # Initialize key usage
            content_commitment = False
            key_encipherment = False
            data_encipherment = False
            key_agreement = False
            key_cert_sign = False
            crl_sign = False
            encipher_only = False
            decipher_only = False

            # Build Key Usages from profile
            for usage in profile['keyUsage']:
                if usage == 'digitalSignature':
                    digital_signature = True
                elif usage == 'nonRepudiation':
                    content_commitment = True
                elif usage == 'keyEncipherment':
                    key_encipherment = True
                elif usage == 'dataEncipherment':
                    data_encipherment = True
                elif usage == 'keyAgreement':
                    key_agreement = True
                elif usage == 'keyCertSign':
                    key_cert_sign = True
                elif usage == 'cRLSign':
                    crl_sign = True
                elif usage == 'encipherOnly':
                    encipher_only = True
                elif usage == 'decipherOnly':
                    decipher_only = True

            # Setup X509 Key Usages
            key_usages = x509.KeyUsage(digital_signature=digital_signature,
                                       content_commitment=content_commitment,
                                       key_encipherment=key_encipherment,
                                       data_encipherment=data_encipherment,
                                       key_agreement=key_agreement,
                                       key_cert_sign=key_cert_sign,
                                       crl_sign=crl_sign,
                                       encipher_only=encipher_only,
                                       decipher_only=decipher_only)
            builder = builder.add_extension(key_usages, critical=True)
        except KeyError:
            # If no Key Usages are set, thats strange
            raise Exception('No Key Usages set.')
        except Exception as err:
            raise Exception('Unable to set Key Usages: {e}'.format(e=err))

        try:
            # Build Key Usages extended based on profile
            key_usages_extended = list()
            for eusage in profile['extendedKeyUsage']:
                if eusage == 'serverAuth':
                    key_usages_extended.append(ExtendedKeyUsageOID.SERVER_AUTH)
                elif eusage == 'clientAuth':
                    key_usages_extended.append(ExtendedKeyUsageOID.CLIENT_AUTH)
                elif eusage == 'codeSigning':
                    key_usages_extended.append(
                        ExtendedKeyUsageOID.CODE_SIGNING)
                elif eusage == 'emailProtection':
                    key_usages_extended.append(
                        ExtendedKeyUsageOID.EMAIL_PROTECTION)
                elif eusage == 'timeStamping':
                    key_usages_extended.append(
                        ExtendedKeyUsageOID.TIME_STAMPING)
                elif eusage == 'OCSPSigning':
                    key_usages_extended.append(
                        ExtendedKeyUsageOID.OCSP_SIGNING)

            #### CHECK TROUBLES ASSOCIATED WITH THIS CHOICE #####
            # Always add 'clientAuth' for automatic renewal
            if ExtendedKeyUsageOID.CLIENT_AUTH not in key_usages_extended:
                key_usages_extended.append(ExtendedKeyUsageOID.CLIENT_AUTH)
            #####################################################

            # Add Deprecated nsCertType (still required by some software)
            # nsCertType_oid = x509.ObjectIdentifier('2.16.840.1.113730.1.1')
            # for c_type in profile['certType']:
            #     if c_type.lower() in ['client', 'server', 'email', 'objsign']:
            #         builder.add_extension(nsCertType_oid, c_type.lower())

            # Set Key Usages if needed
            if len(key_usages_extended):
                builder = builder.add_extension(
                    x509.ExtendedKeyUsage(key_usages_extended), critical=False)
        except KeyError:
            # If no extended key usages are set, do nothing
            pass
        except Exception as err:
            raise Exception(
                'Unable to set Extended Key Usages: {e}'.format(e=err))

        # Add alternate names if found in CSR
        if alt_names is not None:
            # Verify each time that SANS entry was registered
            # We can NOT trust CSR data (client manipulation)
            subject_alt = list([])

            for entry in alt_names.value.get_values_for_type(x509.IPAddress):
                if entry not in sans:
                    continue
                subject_alt.append(x509.IPAddress(ipaddress.ip_address(entry)))

            for entry in alt_names.value.get_values_for_type(x509.DNSName):
                if entry not in sans:
                    continue
                subject_alt.append(x509.DNSName(entry))

            for entry in alt_names.value.get_values_for_type(x509.RFC822Name):
                if entry not in sans:
                    continue
                subject_alt.append(x509.RFC822Name(entry))

            for entry in alt_names.value.get_values_for_type(
                    x509.UniformResourceIdentifier):
                if entry not in sans:
                    continue
                subject_alt.append(x509.UniformResourceIdentifier(entry))

            try:
                # Add all alternates to certificate
                builder = builder.add_extension(
                    x509.SubjectAlternativeName(subject_alt), critical=False)
            except Exception as err:
                raise Exception(
                    'Unable to set alternatives name: {e}'.format(e=err))

        try:
            # Register signing authority
            issuer_key_id = x509.SubjectKeyIdentifier.from_public_key(
                issuer_key.public_key())
            builder = builder.add_extension(x509.AuthorityKeyIdentifier(
                issuer_key_id.digest, [x509.DirectoryName(issuer_name)],
                issuer_serial),
                                            critical=False)
        except Exception as err:
            raise Exception(
                'Unable to setup Authority Identifier: {e}'.format(e=err))

        ca_endpoints = list()
        try:
            # Default value if not set in profile
            ca_url = profile['ca'] if profile[
                'ca'] else "https://certificates.{d}/certs/ca.crt".format(
                    d=profile['domain'])
        except KeyError:
            ca_url = None
        try:
            # Default value if not set in profile
            ocsp_url = profile['ocsp'] if profile[
                'ocsp'] else "https://certificates.{d}/ocsp".format(
                    d=profile['domain'])
        except KeyError:
            ocsp_url = None

        try:
            # Add CA certificate distribution point and OCSP validation url
            if ca_url:
                ca_endpoints.append(
                    x509.AccessDescription(
                        x509.oid.AuthorityInformationAccessOID.OCSP,
                        x509.UniformResourceIdentifier(ca_url)))
            if ocsp_url:
                ca_endpoints.append(
                    x509.AccessDescription(
                        x509.oid.AuthorityInformationAccessOID.OCSP,
                        x509.UniformResourceIdentifier(ocsp_url)))
            builder = builder.add_extension(
                x509.AuthorityInformationAccess(ca_endpoints), critical=False)
        except Exception as err:
            raise Exception(
                'Unable to setup OCSP/CA endpoint: {e}'.format(e=err))

        try:
            # Add CRL distribution point
            crl_endpoints = list()
            # Default value if not set in profile
            url = "https://certificates.{d}/certs/crl.pem".format(
                d=profile['domain'])
            try:
                if profile['csr']:
                    url = profile['csr']
            except KeyError:
                pass
            crl_endpoints.append(
                x509.DistributionPoint(
                    [x509.UniformResourceIdentifier(url)], None, None,
                    [x509.DNSName(issuer_name.rfc4514_string())]))
            builder = builder.add_extension(
                x509.CRLDistributionPoints(crl_endpoints), critical=False)
        except Exception as err:
            raise Exception('Unable to setup CRL endpoints: {e}'.format(e=err))

        if digest is None:
            digest = profile['digest']

        if digest == 'md5':
            digest = hashes.MD5()
        elif digest == 'sha1':
            digest = hashes.SHA1()
        elif digest == 'sha256':
            digest = hashes.SHA256()
        elif digest == 'sha512':
            digest = hashed.SHA512()
        else:
            raise NotImplementedError(
                'Private key only support {s} digest signatures'.format(
                    s=self._allowed.Digest))

        try:
            pub_crt = builder.sign(private_key=issuer_key,
                                   algorithm=digest,
                                   backend=self.__backend)
        except Exception as err:
            raise Exception('Unable to sign certificate: {e}'.format(e=err))

        return pub_crt
예제 #29
0
def _decode_general_name(backend, gn):
    if gn.type == backend._lib.GEN_DNS:
        data = _asn1_string_to_bytes(backend, gn.d.dNSName)
        if data.startswith(b"*."):
            # This is a wildcard name. We need to remove the leading wildcard,
            # IDNA decode, then re-add the wildcard. Wildcard characters should
            # always be left-most (RFC 2595 section 2.4).
            decoded = u"*." + idna.decode(data[2:])
        else:
            # Not a wildcard, decode away. If the string has a * in it anywhere
            # invalid this will raise an InvalidCodePoint
            decoded = idna.decode(data)
            if data.startswith(b"."):
                # idna strips leading periods. Name constraints can have that
                # so we need to re-add it. Sigh.
                decoded = u"." + decoded

        return x509.DNSName(decoded)
    elif gn.type == backend._lib.GEN_URI:
        data = _asn1_string_to_ascii(backend, gn.d.uniformResourceIdentifier)
        parsed = urllib_parse.urlparse(data)
        hostname = idna.decode(parsed.hostname)
        if parsed.port:
            netloc = hostname + u":" + six.text_type(parsed.port)
        else:
            netloc = hostname

        # Note that building a URL in this fashion means it should be
        # semantically indistinguishable from the original but is not
        # guaranteed to be exactly the same.
        uri = urllib_parse.urlunparse(
            (parsed.scheme, netloc, parsed.path, parsed.params, parsed.query,
             parsed.fragment))
        return x509.UniformResourceIdentifier(uri)
    elif gn.type == backend._lib.GEN_RID:
        oid = _obj2txt(backend, gn.d.registeredID)
        return x509.RegisteredID(x509.ObjectIdentifier(oid))
    elif gn.type == backend._lib.GEN_IPADD:
        return x509.IPAddress(
            ipaddress.ip_address(_asn1_string_to_bytes(backend,
                                                       gn.d.iPAddress)))
    elif gn.type == backend._lib.GEN_DIRNAME:
        return x509.DirectoryName(
            _decode_x509_name(backend, gn.d.directoryName))
    elif gn.type == backend._lib.GEN_EMAIL:
        data = _asn1_string_to_ascii(backend, gn.d.rfc822Name)
        name, address = parseaddr(data)
        parts = address.split(u"@")
        if name or len(parts) > 2 or not address:
            # parseaddr has found a name (e.g. Name <email>) or the split
            # has found more than 2 parts (which means more than one @ sign)
            # or the entire value is an empty string.
            raise ValueError("Invalid rfc822name value")
        elif len(parts) == 1:
            # Single label email name. This is valid for local delivery. No
            # IDNA decoding can be done since there is no domain component.
            return x509.RFC822Name(address)
        else:
            # A normal email of the form [email protected]. Let's attempt to
            # decode the domain component and return the entire address.
            return x509.RFC822Name(parts[0] + u"@" + idna.decode(parts[1]))
    else:
        # otherName, x400Address or ediPartyName
        raise x509.UnsupportedGeneralNameType(
            "{0} is not a supported type".format(
                x509._GENERAL_NAMES.get(gn.type, gn.type)), gn.type)
예제 #30
0
def setup_crts(ca_common_name,
               cert_common_name,
               san_list,
               ca_cert_file="./ca.crt",
               ca_key_file="./ca.key",
               cert_file="./chain.pem",
               key_file="./pkey.key"):
    """
    Generate certificate authority cert,  key and chain cert and key signed by CA generated.

    :param ca_common_name:
    :param cert_common_name:
    :param san_list:
    :param ca_cert_file:
    :param ca_key_file:
    :param cert_file:
    :param key_file:
    """
    logger.info("Generating CA private key")
    root_key = rsa.generate_private_key(public_exponent=65537,
                                        key_size=2048,
                                        backend=default_backend())
    subject = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, ca_common_name),
    ])
    issuer = [
        x509.DirectoryName(
            x509.Name([
                x509.NameAttribute(x509.OID_COMMON_NAME, ca_common_name),
            ]))
    ]
    skid = x509.SubjectKeyIdentifier.from_public_key(root_key.public_key())
    root_serial_number = x509.random_serial_number()
    logger.info("Building CA certificate")
    root_cert = x509.CertificateBuilder().subject_name(subject).issuer_name(
        subject).public_key(root_key.public_key(
        )).serial_number(root_serial_number).not_valid_before(
            datetime.datetime.utcnow()).not_valid_after(
                datetime.datetime.utcnow() +
                datetime.timedelta(days=3650)).add_extension(
                    x509.BasicConstraints(ca=True, path_length=None),
                    critical=False,
                ).add_extension(
                    x509.KeyUsage(digital_signature=False,
                                  key_encipherment=False,
                                  content_commitment=False,
                                  data_encipherment=False,
                                  key_agreement=False,
                                  key_cert_sign=True,
                                  crl_sign=True,
                                  encipher_only=False,
                                  decipher_only=False),
                    critical=False).add_extension(
                        skid, critical=False).add_extension(
                            x509.AuthorityKeyIdentifier(
                                key_identifier=skid.digest,
                                authority_cert_issuer=issuer,
                                authority_cert_serial_number=root_serial_number
                            ),
                            critical=False).sign(root_key, hashes.SHA256(),
                                                 default_backend())

    logger.info(
        "Building {} certificate signed by CA".format(cert_common_name))
    # Generate cert for CA
    cert_key = rsa.generate_private_key(public_exponent=65537,
                                        key_size=2048,
                                        backend=default_backend())
    new_subject = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, cert_common_name),
    ])
    x509_sans = []
    for san in san_list:
        x509_sans.append(x509.DNSName(san))
    cert = x509.CertificateBuilder().subject_name(new_subject).issuer_name(
        root_cert.subject).public_key(cert_key.public_key()).serial_number(
            x509.random_serial_number()).not_valid_before(
                datetime.datetime.utcnow()).not_valid_after(
                    datetime.datetime.utcnow() +
                    datetime.timedelta(days=365)).add_extension(
                        x509.SubjectAlternativeName(x509_sans),
                        critical=False,
                    ).add_extension(
                        x509.BasicConstraints(ca=False, path_length=None),
                        critical=False,
                    ).add_extension(
                        x509.ExtendedKeyUsage([
                            ExtendedKeyUsageOID.SERVER_AUTH,
                        ]),
                        critical=False,
                    ).add_extension(
                        x509.KeyUsage(digital_signature=True,
                                      key_encipherment=True,
                                      content_commitment=False,
                                      data_encipherment=False,
                                      key_agreement=False,
                                      key_cert_sign=False,
                                      crl_sign=False,
                                      encipher_only=False,
                                      decipher_only=False),
                        critical=False).add_extension(
                            x509.SubjectKeyIdentifier.from_public_key(
                                cert_key.public_key()),
                            critical=False
                        ).add_extension(x509.AuthorityKeyIdentifier(
                            key_identifier=skid.digest,
                            authority_cert_issuer=issuer,
                            authority_cert_serial_number=root_serial_number),
                                        critical=False).sign(
                                            root_key, hashes.SHA256(),
                                            default_backend())
    # Dump to scratch
    ca_cert = root_cert.public_bytes(encoding=serialization.Encoding.PEM)
    ca_key = root_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption(),
    )
    # Return PEM
    cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM)

    cert_key_pem = cert_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption(),
    )
    crt = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
                                          cert_pem)
    crt_header = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT,
                                                 crt)
    logger.info("Dumping {}".format(ca_cert_file))
    with open(ca_cert_file, "wb") as f:
        f.write(ca_cert)
    logger.info("Dumping {}".format(ca_key_file))
    with open(ca_key_file, "wb") as f:
        f.write(ca_key)
    logger.info("Dumping {}".format(cert_file))
    with open(cert_file, "wb") as f:
        f.write(crt_header + cert_pem)
    logger.info("Dumping {}".format(key_file))
    with open(key_file, "wb") as f:
        f.write(cert_key_pem)