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)
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, )
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)
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)
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
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'])
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)
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
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
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")))
def extension_type(self) -> x509.NameConstraints: return x509.NameConstraints(permitted_subtrees=self._permitted, excluded_subtrees=self._excluded)
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