Exemple #1
0
def _decode_name_constraints(backend, nc):
    nc = backend._ffi.cast("NAME_CONSTRAINTS *", nc)
    nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
    permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
    excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
    return x509.NameConstraints(permitted_subtrees=permitted,
                                excluded_subtrees=excluded)
Exemple #2
0
    def _setNameConstraints(self, extConf: dict) -> None:
        isCritical = True if extConf.get("critical") == "true" else False

        permittedList = []
        if (extConf.get("permitted_subtrees")
                is not None) and (len(extConf["permitted_subtrees"]) >= 1):
            for p in extConf["permitted_subtrees"]:
                permittedList.append(self.conf.extensionMapping[p["type"]](
                    p["value"]))
        else:
            permittedList = None  # type: ignore

        excludedList = []
        if (extConf.get("excluded_subtrees")
                is not None) and (len(extConf["excluded_subtrees"]) >= 1):
            for p in extConf["excluded_subtrees"]:
                excludedList.append(self.conf.extensionMapping[p["type"]](
                    p["value"]))
        else:
            excludedList = None  # type: ignore

        self.builder = self.builder.add_extension(
            x509.NameConstraints(permitted_subtrees=permittedList,
                                 excluded_subtrees=excludedList),
            critical=isCritical,
        )
Exemple #3
0
def _decode_name_constraints(backend, ext):
    nc = backend._ffi.cast("NAME_CONSTRAINTS *",
                           backend._lib.X509V3_EXT_d2i(ext))
    assert nc != backend._ffi.NULL
    nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
    permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
    excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
    return x509.NameConstraints(permitted_subtrees=permitted,
                                excluded_subtrees=excluded)
Exemple #4
0
def parse_name_constraints(constraints):
    permitted, excluded = [], []
    if 'permitted' in constraints:
        for constraint in constraints['permitted']:
            permitted.append(parse_general_name(constraint))
    if 'excluded' in constraints:
        for constraint in constraints['excluded']:
            excluded.append(parse_general_name(constraint))
    return x509.NameConstraints(permitted, excluded)
Exemple #5
0
def authority_certificate(ip):
    auth_cert_filepath, _ = get_filepaths(ip, AUTHORITY_DIRNAME)

    auth_key = make_key()

    issuer = subject = x509.Name([
        x509.NameAttribute(NameOID.COUNTRY_NAME, "AU"),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "NSW"),
        x509.NameAttribute(NameOID.LOCALITY_NAME, "Wagga Wagga"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Quarc"),
        x509.NameAttribute(NameOID.COMMON_NAME, "quarc.services")
    ])

    if os.path.exists(auth_cert_filepath):
        # print(
        #     "\nTLS authority certificate:\n"
        #     "    {}\n".format(auth_cert_filepath))
        pass
    else:
        # Uses name constraints
        # https://nameconstraints.bettertls.com/

        cert = x509.CertificateBuilder().subject_name(subject).issuer_name(
            issuer).public_key(auth_key.public_key()).serial_number(
                x509.random_serial_number()).not_valid_before(
                    datetime.datetime.utcnow()).not_valid_after(
                        datetime.datetime.utcnow() +
                        datetime.timedelta(days=3650)).add_extension(
                            x509.NameConstraints(
                                [x509.IPAddress(ipaddress.ip_network(ip))],
                                None),
                            critical=True,
                        ).sign(auth_key, hashes.SHA256(), default_backend())

        with open(auth_cert_filepath, 'wb') as file:
            file.write(cert.public_bytes(serialization.Encoding.PEM))

        print(
            "\n"
            "================================================================="
            "==============="
            "\n\n"
            "  A TLS certificate for your current IP address has been created at:\n\n"
            "      {}\n\n"
            "  This certificate is to be installed as a certificate authority\n"
            "  on each client that needs access to this server. This certificate\n"
            "  will only allow authentication for servers hosted at the followng\n"
            "  address:\n\n"
            "      https://{}:PORT\n\n"
            "  This is achieved by implementing the Name Constraints extension\n"
            "  and by not storing the certificate authority private key to disk.\n\n"
            "================================================================="
            "==============="
            "\n".format(auth_cert_filepath, ip))

    return auth_cert_filepath, subject, auth_key
Exemple #6
0
    def test_external_ca_constrained(self):
        install_server_external_ca_step1(self.master)

        # name constraints for IPA DNS domain (dot prefix)
        nameconstraint = x509.NameConstraints(permitted_subtrees=[
            x509.DNSName("." + self.master.domain.name),
        ],
                                              excluded_subtrees=None)

        root_ca_fname, ipa_ca_fname = tasks.sign_ca_and_transport(
            self.master,
            paths.ROOT_IPA_CSR,
            ROOT_CA,
            IPA_CA,
            root_ca_extensions=[nameconstraint],
        )

        install_server_external_ca_step2(self.master, ipa_ca_fname,
                                         root_ca_fname)

        tasks.kinit_admin(self.master)
        self.master.run_command(['ipa', 'ping'])
Exemple #7
0
def parse_name_constraints(constraints):
    if not isinstance(constraints, dict):
        raise ValueError(
            'Name constraints field unprocessable. Object expected.')
    permitted, excluded = [], []
    if 'permitted' not in constraints and 'excluded' not in constraints:
        raise UserWarning(
            'Neither permitted nor excluded constraints present, please inspect request for errors.'
        )
    if 'permitted' in constraints:
        if not isinstance(constraints['permitted'], list):
            raise ValueError(
                'Permitted constraints field unprocessable. List of objects expected.'
            )
        for constraint in constraints['permitted']:
            permitted.append(parse_general_name(constraint))
    if 'excluded' in constraints:
        if not isinstance(constraints['excluded'], list):
            raise ValueError(
                'Excluded constraints field unprocessable. List of objects expected.'
            )
        for constraint in constraints['excluded']:
            excluded.append(parse_general_name(constraint))
    return x509.NameConstraints(permitted, excluded)
Exemple #8
0
    def install_extensions(self, builder):
        """Add common extensions to Cert- or CSR builder.
        """

        # BasicConstraints, critical
        if self.ca:
            ext = x509.BasicConstraints(ca=True, path_length=self.path_length)
        else:
            ext = x509.BasicConstraints(ca=False, path_length=None)
        builder = builder.add_extension(ext, critical=True)

        # KeyUsage, critical
        ku_args = {k: k in self.usage for k in KU_FIELDS}
        if self.ca:
            ku_args['key_cert_sign'] = True
            ku_args['crl_sign'] = True
            ext = make_key_usage(**ku_args)
        else:
            ku_args['digital_signature'] = True
            ku_args['key_encipherment'] = True
            ext = make_key_usage(**ku_args)
        builder = builder.add_extension(ext, critical=True)

        # ExtendedKeyUsage, critical
        xku = [x for x in self.usage if x not in KU_FIELDS]
        xku_bad = [x for x in xku if x not in XKU_CODE_TO_OID]
        if xku_bad:
            die("Unknown usage keywords: %s", ','.join(xku_bad))
        if xku:
            xku_oids = [XKU_CODE_TO_OID[x] for x in xku]
            ext = x509.ExtendedKeyUsage(xku_oids)
            builder = builder.add_extension(ext, critical=True)

        # NameConstraints, critical
        if (self.exclude_subtrees or self.permit_subtrees) and self.ca:
            allow = self.load_gnames(self.permit_subtrees) or None
            disallow = self.load_gnames(self.exclude_subtrees) or None
            ext = x509.NameConstraints(allow, disallow)
            builder = builder.add_extension(ext, critical=True)

        # SubjectAlternativeName
        if self.san:
            ext = x509.SubjectAlternativeName(self.get_san_gnames())
            builder = builder.add_extension(ext, critical=False)

        # CRLDistributionPoints
        if self.crl_urls:
            full_names = self.get_crl_gnames()
            reasons = None
            crl_issuer = None
            point = x509.DistributionPoint(full_names, None, reasons, crl_issuer)
            ext = x509.CRLDistributionPoints([point])
            builder = builder.add_extension(ext, critical=False)

        # AuthorityInformationAccess
        if self.ocsp_urls or self.issuer_urls:
            oid = AuthorityInformationAccessOID.OCSP
            ocsp_list = [x509.AccessDescription(oid, gn) for gn in self.get_ocsp_gnames()]
            oid = AuthorityInformationAccessOID.CA_ISSUERS
            ca_list = [x509.AccessDescription(oid, gn) for gn in self.get_issuer_urls_gnames()]
            ext = x509.AuthorityInformationAccess(ocsp_list + ca_list)
            builder = builder.add_extension(ext, critical=False)

        # OCSPNoCheck
        if self.ocsp_nocheck:
            ext = x509.OCSPNoCheck()
            builder = builder.add_extension(ext, critical=False)

        # configured builder
        return builder
Exemple #9
0
    def init(self,
             name,
             key_size,
             key_type,
             algorithm,
             expires,
             parent,
             subject,
             pathlen=None,
             issuer_url=None,
             issuer_alt_name=None,
             crl_url=None,
             ocsp_url=None,
             ca_issuer_url=None,
             ca_crl_url=None,
             ca_ocsp_url=None,
             name_constraints=None,
             password=None,
             parent_password=None):
        """Create a new certificate authority.

        Parameters
        ----------

        key_size : int
            Integer, must be a power of two (e.g. 2048, 4096, ...)
        key_type: str, optional
            Either ``"RSA"`` or ``"DSA"`` for a RSA or DSA key, with ``"RSA"`` being the default.
        algorithm : :py:class:`~cryptography:cryptography.hazmat.primitives.hashes.HashAlgorithm`
            Hash algorithm used when signing the certificate. Must be an instance of
            :py:class:`~cryptography:cryptography.hazmat.primitives.hashes.HashAlgorithm`, e.g.
            :py:class:`~cryptography:cryptography.hazmat.primitives.hashes.SHA512`.
        expires : datetime
            Datetime for when this certificate expires.
        parent : :py:class:`~django_ca.models.CertificateAuthority`, optional
            Parent certificate authority for the new CA. This means that this CA will be an intermediate
            authority.
        subject : str
            Subject string, e.g. ``"/CN=example.com"``.
        pathlen : int, optional
        password : bytes, optional
            Password to encrypt the private key with.
        parent_password : bytes, optional
            Password that the private key of the parent CA is encrypted with.
        """
        # NOTE: This is already verified by KeySizeAction, so none of these checks should ever be
        #       True in the real world. None the less they are here as a safety precaution.
        if not is_power2(key_size):
            raise RuntimeError("%s: Key size must be a power of two." %
                               key_size)
        elif key_size < ca_settings.CA_MIN_KEY_SIZE:
            raise RuntimeError("%s: Key size must be least %s bits." %
                               (key_size, ca_settings.CA_MIN_KEY_SIZE))

        if key_type == 'DSA':
            private_key = dsa.generate_private_key(key_size=key_size,
                                                   backend=default_backend())
        else:
            private_key = rsa.generate_private_key(public_exponent=65537,
                                                   key_size=key_size,
                                                   backend=default_backend())
        public_key = private_key.public_key()
        subject = x509_name(subject)

        builder = get_cert_builder(expires)
        builder = builder.public_key(public_key)
        builder = builder.subject_name(subject)

        builder = builder.add_extension(x509.BasicConstraints(
            ca=True, path_length=pathlen),
                                        critical=True)
        builder = builder.add_extension(x509.KeyUsage(key_cert_sign=True,
                                                      crl_sign=True,
                                                      digital_signature=False,
                                                      content_commitment=False,
                                                      key_encipherment=False,
                                                      data_encipherment=False,
                                                      key_agreement=False,
                                                      encipher_only=False,
                                                      decipher_only=False),
                                        critical=True)

        subject_key_id = x509.SubjectKeyIdentifier.from_public_key(public_key)
        builder = builder.add_extension(subject_key_id, critical=False)

        if parent is None:
            builder = builder.issuer_name(subject)
            private_sign_key = private_key
            auth_key_id = x509.AuthorityKeyIdentifier(
                key_identifier=subject_key_id.digest,
                authority_cert_issuer=None,
                authority_cert_serial_number=None)
        else:
            builder = builder.issuer_name(parent.x509.subject)
            private_sign_key = parent.key(parent_password)
            auth_key_id = parent.x509.extensions.get_extension_for_oid(
                ExtensionOID.AUTHORITY_KEY_IDENTIFIER).value

        builder = builder.add_extension(auth_key_id, critical=False)

        for critical, ext in self.get_common_extensions(
                ca_issuer_url, ca_crl_url, ca_ocsp_url):
            builder = builder.add_extension(ext, critical=critical)

        # TODO: pass separate lists maybe?
        if name_constraints:
            excluded = []
            permitted = []
            for constraint in name_constraints:
                typ, name = constraint.split(',', 1)
                parsed = parse_general_name(name)
                if typ == 'permitted':
                    permitted.append(parsed)
                else:
                    excluded.append(parsed)

            builder = builder.add_extension(x509.NameConstraints(
                permitted_subtrees=permitted, excluded_subtrees=excluded),
                                            critical=True)

        certificate = builder.sign(private_key=private_sign_key,
                                   algorithm=algorithm,
                                   backend=default_backend())

        if crl_url is not None:
            crl_url = '\n'.join(crl_url)

        ca = self.model(name=name,
                        issuer_url=issuer_url,
                        issuer_alt_name=issuer_alt_name,
                        ocsp_url=ocsp_url,
                        crl_url=crl_url,
                        parent=parent)
        ca.x509 = certificate
        ca.private_key_path = os.path.join(ca_settings.CA_DIR,
                                           '%s.key' % ca.serial)
        ca.save()

        if password is None:
            encryption = serialization.NoEncryption()
        else:
            encryption = serialization.BestAvailableEncryption(password)

        # write private key to file
        oldmask = os.umask(247)
        pem = private_key.private_bytes(
            encoding=Encoding.PEM,
            format=PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=encryption)
        with open(ca.private_key_path, 'wb') as key_file:
            key_file.write(pem)
        os.umask(oldmask)

        return ca
Exemple #10
0
public_key = private_key.public_key()

certificate = x509.CertificateBuilder().subject_name(
    x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, u"My Test CA"),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"MyOrg")
    ])).issuer_name(
        x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, u"My Test CA")
        ])).not_valid_before(datetime.datetime.now()).not_valid_after(
            datetime.datetime.now() + lifetime).serial_number(int(
                uuid.uuid4())).public_key(public_key).add_extension(
                    x509.BasicConstraints(ca=True, path_length=None),
                    critical=True).add_extension(x509.NameConstraints(
                        permitted_subtrees=(x509.DNSName("*.test.org"),
                                            x509.RFC822Name("*@*.test.org"),
                                            x509.RFC822Name("*@test.org")),
                        excluded_subtrees=(),
                    ),
                                                 critical=True).sign(
                                                     private_key=private_key,
                                                     algorithm=hashes.SHA256(),
                                                     backend=default_backend())

with open("ca-name-restrict.pem", "wb") as f:
    f.write(
        private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.BestAvailableEncryption(
                b"password")))
Exemple #11
0
 def extension_type(self) -> x509.NameConstraints:
     return x509.NameConstraints(permitted_subtrees=self._permitted,
                                 excluded_subtrees=self._excluded)
Exemple #12
0
def generate_csr(name,
                 common_name=None,
                 company_name=None,
                 street_address=None,
                 city=None,
                 state=None,
                 postal_code=None,
                 country_code=None,
                 email_address=None,
                 sans=None,
                 key_content=None,
                 key_size=4096,
                 output_path=None,
                 is_ca=False):
    """Generate a CSR for specified parameters

    if a private key is given, it will be used to generate CSR, else a new one will be created

    :param name: name of file generated (without extension)
    :param common_name: common name
    :param company_name: company name
    :param street_address: company street address
    :param city: company city
    :param state: company state
    :param postal_code: company postal code
    :param country_code: company country
    :param email_address: contact email
    :param sans: list of SANs to be covered
    :param key_content: optional private key content to generate CSR
    :type key_content: byte
    :param key_size: size of private key to generate CSR, if no key in input
    :param output_path: local path where to generate files
    :param is_ca: True if the requested certificate is for a CA
    :return: tuple(key_content, csr_path) with content of private key and path to csr file
    :rtype: tuple(bytes, pathlib.Path)
    """
    output_path = output_path or os.path.curdir
    if not isinstance(output_path, util.Path):
        output_path = util.Path(str(output_path))
    output_path.mkdir(parents=True, exist_ok=True)

    # use existing private key
    if key_content is not None:
        key = serialization.load_pem_private_key(
            data=key_content,
            password=None,
            backend=default_backend(),
        )
        logger.info("Using existing private key.")
    # Generate our key
    else:
        key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=key_size,
            backend=default_backend(),
        )
        # get private key content as we never want to write it on disk for security reasons
        key_content = key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        )
        logger.info("New private key created.")

    name_attributes = []

    if common_name is not None:
        name_attributes.append(x509.NameAttribute(NameOID.COMMON_NAME, u"%s" % common_name))
    if company_name is not None:
        name_attributes.append(x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"%s" % company_name))
    if street_address is not None:
        name_attributes.append(x509.NameAttribute(NameOID.STREET_ADDRESS, u"%s" % street_address))
    if postal_code is not None:
        name_attributes.append(x509.NameAttribute(NameOID.POSTAL_CODE, u"%s" % postal_code))
    if state is not None:
        name_attributes.append(x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"%s" % state))
    if city is not None:
        name_attributes.append(x509.NameAttribute(NameOID.LOCALITY_NAME, u"%s" % city))
    if country_code is not None:
        name_attributes.append(x509.NameAttribute(NameOID.COUNTRY_NAME, u"%s" % country_code))
    if email_address is not None:
        name_attributes.append(x509.NameAttribute(NameOID.EMAIL_ADDRESS, u"%s" % email_address))

    # Generate a CSR
    builder = x509.CertificateSigningRequestBuilder()
    builder = builder.subject_name(x509.Name(name_attributes))
    builder = builder.add_extension(
        x509.SubjectAlternativeName([x509.DNSName(u"%s" % alt_domain) for alt_domain in sans or []]),
        critical=False
    )
    if is_ca:
        builder = builder.add_extension(
            x509.BasicConstraints(ca=True, path_length=0), critical=True
        )
        builder = builder.add_extension(
            x509.KeyUsage(
                digital_signature=True,
                content_commitment=False,
                key_encipherment=True,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=True,
                crl_sign=False,
                encipher_only=False,
                decipher_only=False
                ),
            critical=True,
        )
        if sans:
            builder = builder.add_extension(
                x509.NameConstraints([x509.DNSName(u"%s" % alt_domain) for alt_domain in sans], []), critical=True
            )

    # Sign the CSR with our private key.
    csr = builder.sign(key, hashes.SHA256(), default_backend())

    # Write our CSR out to disk.
    csr_path = output_path.joinpath("%s.csr" % name)
    csr_path.write_bytes(csr.public_bytes(serialization.Encoding.PEM))
    logger.info("Csr %s created." % csr_path.name)

    return key_content, csr_path