コード例 #1
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)
コード例 #2
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
コード例 #3
0
ファイル: services.py プロジェクト: Drcana/BSEP
def parse_general_name(name):
    typ, value = name['typ'], name['value']
    if typ == 'DNS':
        return x509.DNSName(value)
    if typ == 'RFC822':
        return x509.RFC822Name(value)
    return x509.UniformResourceIdentifier(value)
コード例 #4
0
def _encode_user_principals(principals):
    """Encode user principals as e-mail addresses"""

    if isinstance(principals, str):
        principals = [p.strip() for p in principals.split(',')]

    return [x509.RFC822Name(name) for name in principals]
コード例 #5
0
def make(pkey_pem, emails, usage):
    private_key = serialization.load_pem_private_key(pkey_pem, password=None)
    csr = x509.CertificateSigningRequestBuilder().subject_name(
        x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, emails[0]),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, emails[0]),
        ])).add_extension(
            x509.SubjectAlternativeName([x509.RFC822Name(e) for e in emails] +
                                        [x509.DNSName(e) for e in emails]),
            critical=False,
        )
    if (usage):
        data_encipherment, key_cert_sign, crl_sign, encipher_only, decipher_only = (
            False, ) * 5
        digital_signature = 'digitalSignature' in usage
        content_commitment = 'contentCommitment' in usage
        key_encipherment = 'keyEncipherment' in usage
        key_agreement = 'keyAgreement' in usage
        csr = csr.add_extension(
            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,
            ),
            critical=True,
        )
    csr_pem = csr.sign(private_key, hashes.SHA256()).public_bytes(
        serialization.Encoding.PEM)
    return csr_pem
コード例 #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
ファイル: test_eab.py プロジェクト: noahkw/acmetk
def generate_x509_client_cert(email):

    key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
    )
    subject = issuer = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Niedersachsen"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, "Hannover"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME,
                           "Leibniz Universitaet Hannover"),
        x509.NameAttribute(NameOID.COMMON_NAME, "ACME Toolkit"),
    ])
    cert = (x509.CertificateBuilder().subject_name(subject).issuer_name(
        issuer).public_key(key.public_key()).serial_number(
            x509.random_serial_number()).not_valid_before(
                datetime.datetime.utcnow()).not_valid_after(
                    datetime.datetime.utcnow() +
                    datetime.timedelta(days=2)).add_extension(
                        x509.SubjectAlternativeName([x509.RFC822Name(email)]),
                        critical=False,
                    ).sign(key, hashes.SHA256()))
    data = cert.public_bytes(serialization.Encoding.PEM)
    return urllib.parse.quote(data)
コード例 #8
0
def test_identity_variants():
    ca = CA()

    for bad in [b"example.org", bytearray(b"example.org"), 123]:
        with pytest.raises(TypeError):
            ca.issue_cert(bad)

    cases = {
        # Traditional ascii hostname
        u"example.org": x509.DNSName(u"example.org"),

        # Wildcard
        u"*.example.org": x509.DNSName(u"*.example.org"),

        # IDN
        u"éxamplë.org": x509.DNSName(u"xn--xampl-9rat.org"),
        u"xn--xampl-9rat.org": x509.DNSName(u"xn--xampl-9rat.org"),

        # IDN + wildcard
        u"*.éxamplë.org": x509.DNSName(u"*.xn--xampl-9rat.org"),
        u"*.xn--xampl-9rat.org": x509.DNSName(u"*.xn--xampl-9rat.org"),

        # IDN that acts differently in IDNA-2003 vs IDNA-2008
        u"faß.de": x509.DNSName(u"xn--fa-hia.de"),
        u"xn--fa-hia.de": x509.DNSName(u"xn--fa-hia.de"),

        # IDN with non-permissable character (uppercase K)
        # (example taken from idna package docs)
        u"Königsgäßchen.de": x509.DNSName(u"xn--knigsgchen-b4a3dun.de"),

        # IP addresses
        u"127.0.0.1": x509.IPAddress(IPv4Address(u"127.0.0.1")),
        u"::1": x509.IPAddress(IPv6Address(u"::1")),
        # Check normalization
        u"0000::1": x509.IPAddress(IPv6Address(u"::1")),

        # IP networks
        u"127.0.0.0/24": x509.IPAddress(IPv4Network(u"127.0.0.0/24")),
        u"2001::/16": x509.IPAddress(IPv6Network(u"2001::/16")),
        # Check normalization
        u"2001:0000::/16": x509.IPAddress(IPv6Network(u"2001::/16")),

        # Email address
        u"*****@*****.**": x509.RFC822Name(u"*****@*****.**"),
    }

    for hostname, expected in cases.items():
        # Can't repr the got or expected values here, at least until
        # cryptography v2.1 is out, because in v2.0 on py2, DNSName.__repr__
        # blows up on IDNs.
        print("testing: {!r}".format(hostname))
        pem = ca.issue_cert(hostname).cert_chain_pems[0].bytes()
        cert = x509.load_pem_x509_certificate(pem, default_backend())
        san = cert.extensions.get_extension_for_class(
            x509.SubjectAlternativeName
        )
        assert_is_leaf(cert)
        got = san.value[0]
        assert got == expected
コード例 #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
ファイル: fields.py プロジェクト: zeus911/lemur
    def _deserialize(self, value, attr, data):
        general_names = []
        for name in value:
            if name['nameType'] == 'DNSName':
                validators.sensitive_domain(name['value'])
                general_names.append(x509.DNSName(name['value']))

            elif name['nameType'] == 'IPAddress':
                general_names.append(x509.IPAddress(ipaddress.ip_address(name['value'])))

            elif name['nameType'] == 'IPNetwork':
                general_names.append(x509.IPAddress(ipaddress.ip_network(name['value'])))

            elif name['nameType'] == 'uniformResourceIdentifier':
                general_names.append(x509.UniformResourceIdentifier(name['value']))

            elif name['nameType'] == 'directoryName':
                # TODO: Need to parse a string in name['value'] like:
                # 'CN=Common Name, O=Org Name, OU=OrgUnit Name, C=US, ST=ST, L=City/[email protected]'
                # or
                # 'CN=Common Name/O=Org Name/OU=OrgUnit Name/C=US/ST=NH/L=City/[email protected]'
                # and turn it into something like:
                # x509.Name([
                #     x509.NameAttribute(x509.OID_COMMON_NAME, "Common Name"),
                #     x509.NameAttribute(x509.OID_ORGANIZATION_NAME, "Org Name"),
                #     x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, "OrgUnit Name"),
                #     x509.NameAttribute(x509.OID_COUNTRY_NAME, "US"),
                #     x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, "NH"),
                #     x509.NameAttribute(x509.OID_LOCALITY_NAME, "City"),
                #     x509.NameAttribute(x509.OID_EMAIL_ADDRESS, "*****@*****.**")
                # ]
                # general_names.append(x509.DirectoryName(x509.Name(BLAH))))
                pass

            elif name['nameType'] == 'rfc822Name':
                general_names.append(x509.RFC822Name(name['value']))

            elif name['nameType'] == 'registeredID':
                general_names.append(x509.RegisteredID(x509.ObjectIdentifier(name['value'])))

            elif name['nameType'] == 'otherName':
                # This has two inputs (type and value), so it doesn't fit the mold of the rest of these GeneralName entities.
                # general_names.append(x509.OtherName(name['type'], bytes(name['value']), 'utf-8'))
                pass

            elif name['nameType'] == 'x400Address':
                # The Python Cryptography library doesn't support x400Address types (yet?)
                pass

            elif name['nameType'] == 'EDIPartyName':
                # The Python Cryptography library doesn't support EDIPartyName types (yet?)
                pass

            else:
                current_app.logger.warning('Unable to deserialize SubAltName with type: {name_type}'.format(name_type=name['nameType']))

        return x509.SubjectAlternativeName(general_names)
コード例 #11
0
    def build_csr(self):
        if not self.private_key:
            if self.key_type == KeyTypes.RSA:
                self.private_key = rsa.generate_private_key(
                    public_exponent=65537,
                    key_size=self.key_length,
                    backend=default_backend())
            elif self.key_type == KeyTypes.ECDSA:
                if self.key_curve == "P521":
                    curve = ec.SECP521R1()
                elif self.key_curve == "P384":
                    curve = ec.SECP384R1()
                elif self.key_curve == "P256":
                    curve = ec.SECP256R1()
                elif self.key_curve == "P224":
                    curve = ec.SECP224R1()
                else:
                    curve = ec.SECP521R1()
                self.private_key = ec.generate_private_key(
                    curve, default_backend())
            else:
                raise ClientBadData
            self.public_key_from_private()

        csr_builder = x509.CertificateSigningRequestBuilder()
        subject = [
            x509.NameAttribute(
                NameOID.COMMON_NAME,
                self.common_name,
            )
        ]
        csr_builder = csr_builder.subject_name(x509.Name(subject))

        alt_names = []
        if self.ip_addresses:
            for ip in self.ip_addresses:
                alt_names.append(x509.IPAddress(ipaddress.IPv4Address(ip)))

        if self.san_dns:
            for ns in self.san_dns:
                alt_names.append(x509.DNSName(ns))

        if self.email_addresses:
            for mail in self.email_addresses:
                alt_names.append(x509.RFC822Name(mail))

        csr_builder = csr_builder.add_extension(
            x509.SubjectAlternativeName(alt_names),
            critical=False,
        )

        csr_builder = csr_builder.sign(self.private_key, hashes.SHA256(),
                                       default_backend())
        self.csr = csr_builder.public_bytes(
            serialization.Encoding.PEM).decode()
        return
コード例 #12
0
def parse_general_name(name):
    if not isinstance(name, dict):
        raise ValueError('General name unprocessable. Object expected.')
    typ, value = name['type'], name['value']
    if typ == 'DNS':
        return x509.DNSName(value)
    if typ == 'rfc822name':
        return x509.RFC822Name(value)
    if typ == 'URI':
        return x509.UniformResourceIdentifier(value)
    raise ValueError(f'General name {typ} not recognized or not supported.')
コード例 #13
0
    def _create_client_certificate(self, cert: Certificate_model,
                                   private_key: Key,
                                   issuer_key: Key) -> x509.Certificate:
        # TODO implement checks
        # countryName = match
        # stateOrProvinceName = match
        # localityName = match
        # organizationName = match
        # organizationalUnitName = optional
        # commonName = supplied
        # emailAddress = optional
        if cert.parent.type != CertificateTypes.INTERMEDIATE and cert.parent.type != CertificateTypes.ROOT:
            raise RuntimeError("A root or intermediate parent is expected ")

        self._builder = x509.CertificateBuilder()
        self._set_basic(cert, private_key, issuer_key)

        self._builder = self._builder.add_extension(
            x509.KeyUsage(digital_signature=True,
                          content_commitment=True,
                          key_encipherment=True,
                          data_encipherment=False,
                          key_agreement=False,
                          key_cert_sign=False,
                          crl_sign=False,
                          encipher_only=False,
                          decipher_only=False),
            critical=True,
        )

        self._builder = self._builder.add_extension(
            x509.ExtendedKeyUsage([
                ExtendedKeyUsageOID.CLIENT_AUTH,
                ExtendedKeyUsageOID.EMAIL_PROTECTION
            ]),
            critical=False,
        )

        if cert.dn.subjectAltNames:
            alts = []
            for altname in cert.dn.subjectAltNames:
                try:
                    alt = x509.RFC822Name(altname)
                    alts.append(alt)
                    continue
                except:
                    pass

            self._builder = self._builder.add_extension(
                x509.SubjectAlternativeName(alts),
                critical=False,
            )

        return self._sign_certificate(issuer_key)
コード例 #14
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)
コード例 #15
0
    def san_general_names(self):
        """
        Return SAN general names from a python-cryptography
        certificate object.  If the SAN extension is not present,
        return an empty sequence.

        Because python-cryptography does not yet provide a way to
        handle unrecognised critical extensions (which may occur),
        we must parse the certificate and extract the General Names.
        For uniformity with other code, we manually construct values
        of python-crytography GeneralName subtypes.

        python-cryptography does not yet provide types for
        ediPartyName or x400Address, so we drop these name types.

        otherNames are NOT instantiated to more specific types where
        the type is known.  Use ``process_othernames`` to do that.

        When python-cryptography can handle certs with unrecognised
        critical extensions and implements ediPartyName and
        x400Address, this function (and helpers) will be redundant
        and should go away.

        """
        gns = self.__pyasn1_get_san_general_names()

        GENERAL_NAME_CONSTRUCTORS = {
            'rfc822Name':
            lambda x: crypto_x509.RFC822Name(unicode(x)),
            'dNSName':
            lambda x: crypto_x509.DNSName(unicode(x)),
            'directoryName':
            _pyasn1_to_cryptography_directoryname,
            'registeredID':
            _pyasn1_to_cryptography_registeredid,
            'iPAddress':
            _pyasn1_to_cryptography_ipaddress,
            'uniformResourceIdentifier':
            lambda x: crypto_x509.UniformResourceIdentifier(unicode(x)),
            'otherName':
            _pyasn1_to_cryptography_othername,
        }

        result = []

        for gn in gns:
            gn_type = gn.getName()
            if gn_type in GENERAL_NAME_CONSTRUCTORS:
                result.append(GENERAL_NAME_CONSTRUCTORS[gn_type](
                    gn.getComponent()))

        return result
コード例 #16
0
    def _create_client_certificate(self, cert: CertificateType,
                                   private_key: Key,
                                   issuer_key: Key) -> x509.Certificate:

        Certificate._check_issuer_provided(cert)
        Certificate._check_policies(cert)

        self._builder = x509.CertificateBuilder()
        self._set_basic(cert, private_key, issuer_key)

        self._builder = self._builder.add_extension(
            x509.KeyUsage(
                digital_signature=True,
                content_commitment=True,
                key_encipherment=True,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=False,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False,
            ),
            critical=True,
        )

        self._builder = self._builder.add_extension(
            x509.ExtendedKeyUsage([
                ExtendedKeyUsageOID.CLIENT_AUTH,
                ExtendedKeyUsageOID.EMAIL_PROTECTION
            ]),
            critical=False,
        )

        if cert.dn.subjectAltNames:
            alts = []
            for altname in cert.dn.subjectAltNames:
                try:
                    alt = x509.RFC822Name(altname)
                    alts.append(alt)
                    continue
                except ValueError:
                    pass

            self._builder = self._builder.add_extension(
                x509.SubjectAlternativeName(alts),
                critical=False,
            )

        return self._sign_certificate(issuer_key)
コード例 #17
0
    def test_subject_alt_name_unsupported_general_name(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)

        builder = x509.CertificateSigningRequestBuilder().subject_name(
            x509.Name([
                x509.NameAttribute(x509.OID_COMMON_NAME, u"SAN"),
            ])).add_extension(
                x509.SubjectAlternativeName([
                    x509.RFC822Name(u"*****@*****.**"),
                ]),
                critical=False,
            )

        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA256(), backend)
コード例 #18
0
ファイル: crypto.py プロジェクト: aSauerwein/ca-helper
def create_csr(key, **kwargs):
    csr = x509.CertificateSigningRequestBuilder().subject_name(
        x509.Name([
            # Provide various details about who we are.
            x509.NameAttribute(NameOID.COUNTRY_NAME,
                               kwargs["name"].get("country")),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME,
                               kwargs["name"].get("state_provice")),
            x509.NameAttribute(NameOID.LOCALITY_NAME,
                               kwargs["name"].get("locality")),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME,
                               kwargs["name"].get("organization")),
            x509.NameAttribute(NameOID.COMMON_NAME,
                               kwargs["name"].get("common")),
        ]))
    # build list of sans
    # valid types:
    # x509.DNSName
    # x509.IPAddress
    # x509.RFC822Name - email
    san = []
    for entry in kwargs.get("san"):
        if re.match(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", entry):
            # ipv4 address
            san.append(x509.IPAddress(ipaddress.IPv4Address(entry)))
        elif re.match(r"^[A-Za-z0-9-\.]{1,63}", entry):
            # dns
            san.append(x509.DNSName(entry))
        elif re.match(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)",
                      entry):
            # email
            san.append(x509.RFC822Name(entry))
        else:
            print(f"{entry} not valid for SAN")

    # only add extension if san is not empty
    if san:
        csr = csr.add_extension(
            x509.SubjectAlternativeName(san),
            critical=False,
            # Sign the CSR with our private key.
        )

    csr = csr.sign(key, hashes.SHA256())
    return csr
コード例 #19
0
ファイル: common.py プロジェクト: wgoulet/vcert-python
    def build_csr(self):
        if not self.private_key:
            self._gen_key()

        csr_builder = x509.CertificateSigningRequestBuilder()
        subject = [x509.NameAttribute(NameOID.COMMON_NAME, self.common_name,)]
        if self.locality:
            subject.append(x509.NameAttribute(NameOID.LOCALITY_NAME, self.locality))
        if self.province:
            subject.append(x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, self.province))
        if self.country:
            subject.append(x509.NameAttribute(NameOID.COUNTRY_NAME, self.country))
        if self.organization:
            subject.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, self.organization))
        if self.organizational_unit:
            if isinstance(self.organizational_unit, string_types):
                subject.append(x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, self.organizational_unit))
            elif isinstance(self.organizational_unit, list):
                for u in self.organizational_unit:
                    subject.append(x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, u))

        csr_builder = csr_builder.subject_name(x509.Name(subject))

        alt_names = []
        if self.ip_addresses:
            for ip in self.ip_addresses:
                alt_names.append(x509.IPAddress(ipaddress.IPv4Address(ip)))

        if self.san_dns:
            for ns in self.san_dns:
                alt_names.append(x509.DNSName(ns))

        if self.email_addresses:
            for mail in self.email_addresses:
                alt_names.append(x509.RFC822Name(mail))

        csr_builder = csr_builder.add_extension(
            x509.SubjectAlternativeName(alt_names),
            critical=False,
        )

        csr_builder = csr_builder.sign(self.private_key, hashes.SHA256(),
                                       default_backend())
        self.csr = csr_builder.public_bytes(serialization.Encoding.PEM).decode()
        return
コード例 #20
0
def _identity_string_to_x509(identity):
    # Because we are a DWIM library for lazy slackers, we cheerfully pervert
    # the cryptography library's carefully type-safe API, and silently DTRT
    # for any of the following identity types:
    #
    # - "example.org"
    # - "example.org"
    # - "éxamplë.org"
    # - "xn--xampl-9rat.org"
    # - "xn--xampl-9rat.org"
    # - "127.0.0.1"
    # - "::1"
    # - "10.0.0.0/8"
    # - "2001::/16"
    # - "*****@*****.**"
    #
    # plus wildcard variants of the identities.
    if not isinstance(identity, unicode):
        raise TypeError("identities must be text (unicode on py2, str on py3)")

    if u"@" in identity:
        return x509.RFC822Name(identity)

    # Have to try ip_address first, because ip_network("127.0.0.1") is
    # interpreted as being the network 127.0.0.1/32. Which I guess would be
    # fine, actually, but why risk it.
    for ip_converter in [ipaddress.ip_address, ipaddress.ip_network]:
        try:
            ip_hostname = ip_converter(identity)
        except ValueError:
            continue
        else:
            return x509.IPAddress(ip_hostname)

    # Encode to an A-label, like cryptography wants
    if identity.startswith("*."):
        alabel_bytes = b"*." + idna.encode(identity[2:], uts46=True)
    else:
        alabel_bytes = idna.encode(identity, uts46=True)
    # Then back to text, which is mandatory on cryptography 2.0 and earlier,
    # and may or may not be deprecated in cryptography 2.1.
    alabel = alabel_bytes.decode("ascii")
    return x509.DNSName(alabel)
コード例 #21
0
def cert_directory():
    """
    Provides a directory of certificates that are used to run the tests. By
    building these certificates each time we run the tests, we slow the test
    execution down quite substantially, but in return we get to ensure that the
    certificates are always in-date and valid.

    One thing this does *not* do is generate the keys each time. This is
    because keygen is the slowest and most CPU-intensive part of this process,
    and there is simply no need to forcibly repeat that process on a regular
    basis.
    """
    # Begin by creating our temporary directory and copying all our keys into
    # it.
    tempdir = tempfile.mkdtemp()
    keys = glob.glob('keys/*.key')
    for key in keys:
        shutil.copy(key, tempdir)

    # Build our root and intermediate CA
    ca_data = build_ca_cert(tempdir)
    intermediate_data = build_intermediate_cert(tempdir, ca_data['cert'],
                                                ca_data['key'])

    # Ok, build our leaves. We need one client cert and one root cert, with the
    # root cert being valid for localhost.
    client_san = x509.SubjectAlternativeName(
        [x509.RFC822Name(u'*****@*****.**')])
    server_san = x509.SubjectAlternativeName([x509.DNSName(u'localhost')])
    build_leaf_certificate(tempdir, 'client.key', 'client.crt',
                           u'PEP 543 Client Certificate', client_san,
                           intermediate_data['cert'], intermediate_data['key'])
    build_leaf_certificate(tempdir, 'server.key', 'server.crt', u'localhost',
                           server_san, intermediate_data['cert'],
                           intermediate_data['key'])

    # Ok, we've set up. Let the tests run.
    global TEMPDIR
    TEMPDIR = tempdir
    yield

    # To cleanup, blow away the temporary directory.
    shutil.rmtree(tempdir)
コード例 #22
0
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:]))
    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))
コード例 #23
0
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))
コード例 #24
0
 def _add_client_usages(csr: Any,
                        issuer: Credentials,
                        rfc82name: str = None) -> Any:
     cert = csr.add_extension(
         x509.BasicConstraints(ca=False, path_length=None),
         critical=True,
     ).add_extension(
         x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
             issuer.certificate.extensions.get_extension_for_class(
                 x509.SubjectKeyIdentifier).value),
         critical=False)
     if rfc82name:
         cert.add_extension(
             x509.SubjectAlternativeName([x509.RFC822Name(rfc82name)]),
             critical=True,
         )
     cert.add_extension(x509.ExtendedKeyUsage([
         ExtendedKeyUsageOID.CLIENT_AUTH,
     ]),
                        critical=True)
     return cert
コード例 #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 generate_csr(self, key):
        """Generates a CSR.

        Args:
            key - The users key

        Returns:
            csr - The CSR
        """
        click.secho(
            "Generating CSR for {server}".format(server=self.server),
            fg="yellow",
        )
        country = self.config.get(self.server, "country", fallback=None)
        state = self.config.get(self.server, "state", fallback=None)
        locality = self.config.get(self.server, "locality", fallback=None)
        organization_name = self.config.get(self.server, "organization_name")
        email = self.config.get(self.server, "email")
        csr_subject_arr = [
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization_name),
            x509.NameAttribute(NameOID.COMMON_NAME, self.friendly_name),
        ]
        if self.config.getboolean(
            self.server, "sendEmailAsNameAttribute", fallback=False
        ):
            csr_subject_arr.append(
                x509.NameAttribute(NameOID.EMAIL_ADDRESS, email)
            )
        if state:
            csr_subject_arr.append(
                x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state)
            )
        if country:
            csr_subject_arr.append(
                x509.NameAttribute(NameOID.COUNTRY_NAME, country)
            )
        if locality:
            csr_subject_arr.append(
                x509.NameAttribute(NameOID.LOCALITY_NAME, locality)
            )
        builder = x509.CertificateSigningRequestBuilder()
        builder = builder.subject_name(x509.Name(csr_subject_arr))
        builder = builder.add_extension(
            x509.SubjectAlternativeName([x509.RFC822Name(email)]), False
        )
        csr = builder.sign(key, hashes.SHA256(), default_backend())
        csr_fname = "{}.csr.asc".format(self.server)

        # If the user overrides the ini configuration programatically, don't
        # save the CSR as this could be a one off.
        if self.override:
            return csr

        with open(
            "{config}/{server}/{csr}".format(
                config=self.CONFIG_FOLDER_PATH,
                server=self.server,
                csr=csr_fname,
            ),
            "wb",
        ) as f:
            enc_csr = self.encrypt(
                csr.public_bytes(serialization.Encoding.PEM).decode("utf-8"),
                self.config.get(self.server, "fingerprint"),
            )
            f.write(bytes(str(enc_csr), "utf-8"))
        return csr
コード例 #28
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)
コード例 #29
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
コード例 #30
0
    def generate(self, pkey, cn, profile, sans=None):
        """Generate a request based on:
            - privatekey (pkey)
            - commonName (cn)
            - profile object (profile)
        add Additional CommonName if needed sans argument
        """

        subject = list([])
        # Extract subject from profile
        try:
            for entry in profile['subject']:
                for subj, value in entry.items():
                    subj = subj.upper()
                    if subj == 'C':
                        subject.append(
                            x509.NameAttribute(NameOID.COUNTRY_NAME, value))
                    elif subj == 'ST':
                        subject.append(
                            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME,
                                               value))
                    elif subj == 'L':
                        subject.append(
                            x509.NameAttribute(NameOID.LOCALITY_NAME, value))
                    elif subj == 'O':
                        subject.append(
                            x509.NameAttribute(NameOID.ORGANIZATION_NAME,
                                               value))
                    elif subj == 'OU':
                        subject.append(
                            x509.NameAttribute(
                                NameOID.ORGANIZATIONAL_UNIT_NAME, value))
        except Exception as err:
            raise Exception('Unable to extract subject: {e}'.format(e=err))

        try:
            # Append cn at the end
            subject.append(x509.NameAttribute(NameOID.COMMON_NAME, cn))
        except Exception as err:
            raise Exception('Unable to setup subject name: {e}'.format(e=err))

        try:
            builder = x509.CertificateSigningRequestBuilder().subject_name(
                x509.Name(subject))
        except Exception as err:
            raise Exception('Unable to create structure: {e}'.format(e=err))

        subject_alt = list([])
        # Best pratices wants to include FQDN in SANS for servers
        if profile['altnames']:
            # Add IPAddress for Goland compliance
            if validators.ipv4(cn):
                subject_alt.append(x509.DNSName(cn))
                subject_alt.append(x509.IPAddress(ipaddress.ip_address(cn)))
            elif validators.domain(cn):
                subject_alt.append(x509.DNSName(cn))
            elif validators.email(cn):
                subject_alt.append(x509.RFC822Name(cn))
            elif validators.url(cn):
                subject_alt.append(x509.UniformResourceIdentifier(cn))
            else:
                if 'server' in profile['certType']:
                    self.output(
                        'ADD ALT NAMES {c}.{d} FOR SERVER USAGE'.format(
                            c=cn, d=profile['domain']))
                    subject_alt.append(
                        x509.DNSName("{c}.{d}".format(c=cn,
                                                      d=profile['domain'])))
                if 'email' in profile['certType']:
                    subject_alt.append(
                        x509.RFC822Name("{c}@{d}".format(c=cn,
                                                         d=profile['domain'])))

        # Add alternate names if needed
        if isinstance(sans, list) and len(sans):
            for entry in sans:
                # Add IPAddress for Goland compliance
                if validators.ipv4(entry):
                    if x509.DNSName(entry) not in subject_alt:
                        subject_alt.append(x509.DNSName(entry))
                    if x509.IPAddress(
                            ipaddress.ip_address(entry)) not in subject_alt:
                        subject_alt.append(
                            x509.IPAddress(ipaddress.ip_address(entry)))
                elif validators.domain(entry) and (x509.DNSName(entry)
                                                   not in subject_alt):
                    subject_alt.append(x509.DNSName(entry))
                elif validators.email(entry) and (x509.RFC822Name(entry)
                                                  not in subject_alt):
                    subject_alt.append(x509.RFC822Name(entry))

        if len(subject_alt):
            try:
                builder = builder.add_extension(
                    x509.SubjectAlternativeName(subject_alt), critical=False)
            except Exception as err:
                raise Exception(
                    'Unable to add alternate name: {e}'.format(e=err))

        # 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())

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

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

        return csr