def _sign_string(self, string_to_sign): try: # We expect the private key to be the an PKCS8 pem formatted string. pem_bytes = self.credentials.private_key.encode('utf-8') if pem.detect(pem_bytes): _, _, der_bytes = pem.unarmor(pem_bytes) # In PKCS8 the key is wrapped in a container that describes it info = keys.PrivateKeyInfo.load(der_bytes, strict=True) # The unwrapped key is equivalent to pkcs1 contents key = rsa.PrivateKey.load_pkcs1(info.unwrap().dump(), 'DER') else: raise Exception('Not a PEM file') except: message = \ "Failed to import private key from: '%s'. The private key is " \ "corrupted or it is not in PKCS8 PEM format. The private key " \ "was extracted either from 'env' (environment variables), " \ "'shared-credentials-file' (a profile in the shared " \ "credential file, by default under ~/.altus/credentials), or " \ "'auth-config-file' (a file containing the credentials whose " \ "location was supplied on the command line.)" % \ self.credentials.method LOG.debug(message, exc_info=True) raise Exception(message) # We sign the hash. signature = rsa.sign(string_to_sign.encode('utf-8'), key, 'SHA-256') return urlsafe_b64encode(signature).strip().decode('utf-8')
def _validate_unarmor(self, certs, var_name): """ Takes a list of byte strings or asn1crypto.x509.Certificates objects, validates and loads them while unarmoring any PEM-encoded contents :param certs: A list of byte strings or asn1crypto.x509.Certificate objects :param var_name: A unicode variable name to use in any TypeError exceptions :return: A list of asn1crypto.x509.Certificate objects """ output = [] for cert in certs: if isinstance(cert, x509.Certificate): output.append(cert) else: if not isinstance(cert, byte_cls): raise TypeError(pretty_message( ''' %s must contain only byte strings or asn1crypto.x509.Certificate objects, not %s ''', var_name, type_name(cert) )) if pem.detect(cert): _, _, cert = pem.unarmor(cert) output.append(x509.Certificate.load(cert)) return output
def _sign_string(self, string_to_sign): try: # We expect the private key to be the an PKCS8 pem formatted string. pem_bytes = self.credentials.private_key.encode('utf-8') if pem.detect(pem_bytes): _, _, der_bytes = pem.unarmor(pem_bytes) # In PKCS8 the key is wrapped in a container that describes it info = keys.PrivateKeyInfo.load(der_bytes, strict=True) # The unwrapped key is equivalent to pkcs1 contents key = rsa.PrivateKey.load_pkcs1(info.unwrap().dump(), 'DER') else: raise Exception('Not a PEM file') except: message = \ "Failed to import private key from: '%s'. The private key is " \ "corrupted or it is not in PKCS8 PEM format. The private key " \ "was extracted either from 'env' (environment variables), " \ "'shared-credentials-file' (a profile in the shared " \ "credential file, by default under ~/.ccs/credentials), or " \ "'auth-config-file' (a file containing the credentials whose " \ "location was supplied on the command line.)" % \ self.credentials.method LOG.debug(message, exc_info=True) raise Exception(message) # We sign the hash. signature = rsa.sign(string_to_sign.encode('utf-8'), key, 'SHA-256') return urlsafe_b64encode(signature).strip().decode('utf-8')
def prepend(self, cert): """ Prepends a cert to the path. This should be the issuer of the previously prepended cert. :param cert: An asn1crypto.x509.Certificate object or a byte string :return: The current ValidationPath object, for chaining """ if not isinstance(cert, x509.Certificate): if not isinstance(cert, byte_cls): raise TypeError(pretty_message( ''' cert must be a byte string or an asn1crypto.x509.Certificate object, not %s ''', type_name(cert) )) if pem.detect(cert): _, _, cert = pem.unarmor(cert) cert = x509.Certificate.load(cert) if cert.issuer_serial in self._cert_hashes: raise DuplicateCertificateError() self._cert_hashes.add(cert.issuer_serial) self._certs.insert(0, cert) return self
def _grab_crl(user_agent, url, timeout): """ Fetches a CRL and parses it :param user_agent: A unicode string of the user agent to use when fetching the URL :param url: A unicode string of the URL to fetch the CRL from :param timeout: The number of seconds after which an HTTP request should timeout :return: An asn1crypto.crl.CertificateList object """ if sys.version_info < (3,): url = util.iri_to_uri(url) request = Request(url) request.add_header(b'Accept', b'application/pkix-crl') request.add_header(b'User-Agent', user_agent.encode('iso-8859-1')) response = urlopen(request, None, timeout) data = response.read() if pem.detect(data): _, _, data = pem.unarmor(data) return crl.CertificateList.load(data)
def _load_cert_object(self, *path_components): with open(os.path.join(fixtures_dir, *path_components), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) return cert
def _format_crl(self, name, content, fmt): # type: (str, bytes, str) -> Tuple[str, bytes] """ Forces a CRL to DER or PEM. name: The original name of the downloaded CRL. content: The CRL contents. fmt: 'der' or 'pem'. Returns the updated name and content: (name, content), """ base, ext = os.path.splitext(name) is_pem = pem.detect(content) if fmt == 'pem': ext = '.pem' if not is_pem: content = pem.armor('X509 CRL', content) elif fmt == 'der': ext = '.crl' if is_pem: _, _, content = pem.unarmor(content) return (base + ext, content)
def _raw_sign_string(self, string_to_sign): """ Sign the supplied string using the credentials and return the raw signature. :param string_to_sign: String to sign :return: Raw signature as string """ try: # We expect the private key to be the an PKCS8 pem formatted string. pem_bytes = self.credentials.private_key.encode('utf-8') if pem.detect(pem_bytes): _, _, der_bytes = pem.unarmor(pem_bytes) # In PKCS8 the key is wrapped in a container that describes it info = keys.PrivateKeyInfo.load(der_bytes, strict=True) # Directly unwrap the private key. The asn1crypto library stopped # offering an API call for this in their 1.0.0 release but their # official answer of using a separate native-code-dependent # library to do one line of work is unreasonable. Of course, this # line might break in the future... unwrapped = info['private_key'].parsed # The unwrapped key is equivalent to pkcs1 contents key = rsa.PrivateKey.load_pkcs1(unwrapped.dump(), 'DER') else: raise Exception('Not a PEM file') except Exception: message = self.ERROR_MESSAGE % self.credentials.method LOG.debug(message, exc_info=True) raise Exception(message) # We sign the hash. signature = rsa.sign(string_to_sign.encode('utf-8'), key, 'SHA-256') return signature
def _validate_unarmor(self, certs, var_name): """ Takes a list of byte strings or asn1crypto.x509.Certificates objects, validates and loads them while unarmoring any PEM-encoded contents :param certs: A list of byte strings or asn1crypto.x509.Certificate objects :param var_name: A unicode variable name to use in any TypeError exceptions :return: A list of asn1crypto.x509.Certificate objects """ output = [] for cert in certs: if isinstance(cert, x509.Certificate): output.append(cert) else: if not isinstance(cert, byte_cls): raise TypeError( pretty_message( ''' %s must contain only byte strings or asn1crypto.x509.Certificate objects, not %s ''', var_name, type_name(cert))) if pem.detect(cert): _, _, cert = pem.unarmor(cert) output.append(x509.Certificate.load(cert)) return output
def _grab_crl(user_agent, url, timeout): """ Fetches a CRL and parses it :param user_agent: A unicode string of the user agent to use when fetching the URL :param url: A unicode string of the URL to fetch the CRL from :param timeout: The number of seconds after which an HTTP request should timeout :return: An asn1crypto.crl.CertificateList object """ request = Request(url) request.add_header('Accept', 'application/pkix-crl') request.add_header('User-Agent', user_agent) request.add_header('Host', request.get_host().split(':')[0]) response = urlopen(request, None, timeout) data = response.read() if pem.detect(data): _, _, data = pem.unarmor(data) return crl.CertificateList.load(data)
def load_certs_from_pemder(cert_files): """ A convenience function to load PEM/DER-encoded certificates from files. :param cert_files: An iterable of file names. :return: A generator producing :class:`.asn1crypto.x509.Certificate` objects. """ for ca_chain_file in cert_files: with open(ca_chain_file, 'rb') as f: ca_chain_bytes = f.read() # use the pattern from the asn1crypto docs # to distinguish PEM/DER and read multiple certs # from one PEM file (if necessary) if pem.detect(ca_chain_bytes): pems = pem.unarmor(ca_chain_bytes, multiple=True) for type_name, _, der in pems: if type_name is None or type_name.lower() == 'certificate': yield x509.Certificate.load(der) else: # pragma: nocover logger.debug(f'Skipping PEM block of type {type_name} in ' f'CA chain file.') else: # no need to unarmor, just try to load it immediately yield x509.Certificate.load(ca_chain_bytes)
def prepend(self, cert): """ Prepends a cert to the path. This should be the issuer of the previously prepended cert. :param cert: An asn1crypto.x509.Certificate object or a byte string :return: The current ValidationPath object, for chaining """ if not isinstance(cert, x509.Certificate): if not isinstance(cert, byte_cls): raise TypeError( pretty_message( ''' cert must be a byte string or an asn1crypto.x509.Certificate object, not %s ''', type_name(cert))) if pem.detect(cert): _, _, cert = pem.unarmor(cert) cert = x509.Certificate.load(cert) if cert.issuer_serial in self._cert_hashes: raise DuplicateCertificateError() self._cert_hashes.add(cert.issuer_serial) self._certs.insert(0, cert) return self
def return_cert_from_file(filename): with open(filename, 'rb') as f: der_bytes = f.read() if pem.detect(der_bytes): type_name, headers, der_bytes = pem.unarmor(der_bytes) return x509.Certificate.load(der_bytes) else: raise ValueError("{} doesn't contain DER data".format(filename))
def test_get_path(self): trust_list.clear_cache() certs = trust_list.get_path() with open(certs, 'rb') as f: cert_data = f.read() self.assertEqual(True, pem.detect(cert_data)) self.assertLess(10240, len(cert_data))
def read_pem(pem_data): if not pem.detect(pem_data): return [pem_data] der_bytes_list = [] for _, _, der_bytes in pem.unarmor(pem_data, multiple=True): der_bytes_list.append(der_bytes) return der_bytes_list
def cert2asn(cert, cert_bytes=True): if cert_bytes: cert_bytes = cert.public_bytes(serialization.Encoding.PEM) else: cert_bytes = cert if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) return x509.Certificate.load(cert_bytes)
def load_key(filename: str) -> RSAPrivateKey: with open(filename, 'rb') as f: pembytes = f.read() if pem.detect(pembytes): _, _, raw = pem.unarmor(pembytes) return RSAPrivateKey.load(raw) else: print('bad pem!') sys.exit(1)
def do_run(): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) for password in [None, 'password123']: pem_serialized = asymmetric.dump_private_key(private, password, target_ms=20) private_reloaded = asymmetric.load_private_key(pem_serialized, password) self.assertTrue(pem.detect(pem_serialized)) self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm)
def test_read_existing_verify_result(): pem_str = cert.read_existing(to_pem=True) if pem.detect(pem_str): type_name, headers, pem_str = pem.unarmor(pem_str) x509.Certificate.load(pem_str) der_bytes = cert.read_existing() x509.Certificate.load(der_bytes)
def unwrap_pkcs8(blob): if not pem.detect(blob): return _, _, der_bytes = pem.unarmor(blob) data = EncryptedPrivateKeyInfo.load(der_bytes).native if "encryption_algorithm" not in data: return if "encrypted_data" not in data: return if "algorithm" not in data["encryption_algorithm"]: return if data["encryption_algorithm"]["algorithm"] != "pbes2": sys.stderr.write( "[%s] encryption_algorithm <%s> is not supported currently!\n" % (sys.argv[0], data["encryption_algorithm"]["algorithm"])) return # encryption data encrypted_data = data["encrypted_data"] # KDF params = data["encryption_algorithm"]["parameters"] kdf = params["key_derivation_func"] if kdf["algorithm"] != "pbkdf2": sys.stderr.write( "[%s] kdf algorithm <%s> is not supported currently!\n" % (sys.argv[0], kdf["algorithm"])) return kdf_params = kdf["parameters"] salt = kdf_params["salt"] iterations = kdf_params["iteration_count"] # Cipher cipher_params = params["encryption_scheme"] cipher = cipher_params["algorithm"] iv = cipher_params["parameters"] if cipher == "tripledes_3key": cid = 1 elif cipher == "aes128_cbc": cid = 2 elif cipher == "aes192_cbc": cid = 3 elif cipher == "aes256_cbc": cid = 4 else: sys.stderr.write("[%s] cipher <%s> is not supported currently!\n" % (sys.argv[0], cipher)) return sys.stdout.write("$PEM$1$%d$%s$%s$%s$%d$%s\n" % (cid, salt.encode("hex"), iterations, iv.encode("hex"), len(encrypted_data), encrypted_data.encode("hex")))
def test_read_existing_verify_result(): LOGGER.info('Read certificate and make sanity/format check') pem_str = cert.read_existing(to_pem=True) if pem.detect(pem_str): type_name, headers, pem_str = pem.unarmor(pem_str) x509.Certificate.load(pem_str) der_bytes = cert.read_existing() x509.Certificate.load(der_bytes)
def do_run(): private = asymmetric.load_private_key( os.path.join(fixtures_dir, 'keys/test.key')) for password in [None, 'password123']: pem_serialized = asymmetric.dump_private_key(private, password, target_ms=20) private_reloaded = asymmetric.load_private_key( pem_serialized, password) self.assertTrue(pem.detect(pem_serialized)) self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm)
def test_fetch_crl(self): with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: file_bytes = f.read() if pem.detect(file_bytes): _, _, file_bytes = pem.unarmor(file_bytes) intermediate = x509.Certificate.load(file_bytes) crls = crl_client.fetch(intermediate, timeout=3) context = ValidationContext(crls=crls) registry = context.certificate_registry path = registry.build_paths(intermediate)[0] verify_crl(intermediate, path, context)
def test_coverage(self): with Now(utcdatetime(2000, 1, 1)): anchor, cert = self.issue_anchor() reissued = cert.issue(anchor, cert.asn_cert) self.assertEqual(Certificate.objects.issued().count(), 1) self.assertEqual(Certificate.objects.revoked().count(), 0) self.assertEqual(Certificate.objects.revokable().count(), 1) self.assertEqual(Certificate.objects.expired().count(), 0) self.assertTrue(pem.detect(cert.pem())) self.assertFalse(cert.is_revoked()) self.assertEqual(reissued, None) self.assertEqual(Entity.objects.get(pk=anchor.pk).get_issuer(), anchor) self.assertTrue(isinstance(cert.public_key_info(), keys.PublicKeyInfo))
def test_fetch_ocsp(self): with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) intermediate = x509.Certificate.load(cert_bytes) registry = CertificateRegistry() path = registry.build_paths(intermediate)[0] issuer = path.find_issuer(intermediate) ocsp_response = ocsp_client.fetch(intermediate, issuer, timeout=3) context = ValidationContext(ocsps=[ocsp_response]) verify_ocsp_response(intermediate, path, context)
def decode_cert(cert_path): # Open certificate and deserialize with open(str(cert_path),'rb') as rf: pem_obj = rf.read() if pem.detect(pem_obj): type, _, _ = pem.unarmor(pem_obj) # Determine type of certificate if type == "PKCS7": pkcs7 = crypto.load_pkcs7_data(crypto.FILETYPE_PEM,pem_obj) certs = get_certificates(pkcs7) count = 0 for cert in certs: pem_cert = x509.load_pem_x509_certificate(cert, default_backend()) # Print cert issued to colorize('green') print('Issued to: (Chain ' + str(count) + ')') count += 1 colorize_edit('reset') print(pem_cert.subject.rfc4514_string()) print("Serial No: " + str(pem_cert.serial_number)) print("Fingerprint: " + str(base64.b64encode(pem_cert.fingerprint(hashes.SHA256())), 'utf-8')) print("Valid from: " + str(pem_cert.not_valid_before)) print("Valid to: " + str(pem_cert.not_valid_after)) print("Signature hash algorithm: " + pem_cert.signature_hash_algorithm.name) # Print issued by colorize('green') print('Issued by: ') colorize_edit('reset') print(pem_cert.issuer.rfc4514_string()) print('\n') elif type == "CERTIFICATE": cert = x509.load_pem_x509_certificate(pem_obj, default_backend()) # Print cert issued to colorize('green') print('Issued to: ') colorize_edit('reset') print(cert.subject.rfc4514_string()) print("Serial No: " + str(cert.serial_number)) print("Fingerprint: " + str(base64.b64encode(cert.fingerprint(hashes.SHA256())), 'utf-8')) print("Valid from: " + str(cert.not_valid_before)) print("Valid to: " + str(cert.not_valid_after)) print("Signature hash algorithm: " + cert.signature_hash_algorithm.name) # Print issued by colorize('green') print('Issued by: ') colorize_edit('reset') print(cert.issuer.rfc4514_string()) else: raise TypeError('Not a PEM or PKCS7 file.')
def store_request(buf, overwrite=False, address="", user=""): """ Store CSR for later processing """ if not buf: raise ValueError("No signing request supplied") if pem.detect(buf): header, _, der_bytes = pem.unarmor(buf) csr = CertificationRequest.load(der_bytes) else: csr = CertificationRequest.load(buf) buf = pem_armor_csr(csr) common_name = csr["certification_request_info"]["subject"].native[ "common_name"] if not re.match(const.RE_COMMON_NAME, common_name): raise ValueError("Invalid common name %s" % repr(common_name)) request_path = os.path.join(config.REQUESTS_DIR, common_name + ".pem") # If there is cert, check if it's the same if os.path.exists(request_path) and not overwrite: if open(request_path, "rb").read() == buf: raise errors.RequestExists("Request already exists") else: raise errors.DuplicateCommonNameError( "Another request with same common name already exists") else: with open(request_path + ".part", "wb") as fh: fh.write(buf) os.rename(request_path + ".part", request_path) attach_csr = buf, "application/x-pem-file", common_name + ".csr" mailer.send("request-stored.md", attachments=(attach_csr, ), common_name=common_name) setxattr(request_path, "user.request.address", address) setxattr(request_path, "user.request.user", user) try: hostname, aliaslist, ipaddrlist = socket.gethostbyaddr(address) except (socket.herror, OSError): # Failed to resolve hostname or resolved to multiple pass else: setxattr(request_path, "user.request.hostname", hostname) return request_path, csr, common_name
def __init__(self, end_entity_cert, intermediate_certs=None, validation_context=None): """ :param end_entity_cert: An asn1crypto.x509.Certificate object or a byte string of the DER or PEM-encoded X.509 end-entity certificate to validate :param intermediate_certs: None or a list of asn1crypto.x509.Certificate objects or a byte string of a DER or PEM-encoded X.509 certificate. Used in constructing certificate paths for validation. :param validation_context: A certvalidator.context.ValidationContext() object that controls validation options """ if not isinstance(end_entity_cert, Certificate): if not isinstance(end_entity_cert, byte_cls): raise TypeError( pretty_message( ''' end_entity_cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(end_entity_cert))) if pem.detect(end_entity_cert): _, _, end_entity_cert = pem.unarmor(end_entity_cert) end_entity_cert = Certificate.load(end_entity_cert) if validation_context is None: validation_context = ValidationContext() if not isinstance(validation_context, ValidationContext): raise TypeError( pretty_message( ''' validation_context must be an instance of certvalidator.context.ValidationContext, not %s ''', type_name(validation_context))) if intermediate_certs is not None: certificate_registry = validation_context.certificate_registry for intermediate_cert in intermediate_certs: certificate_registry.add_other_cert(intermediate_cert) self._context = validation_context self._certificate = end_entity_cert
def __init__(self, end_entity_cert, intermediate_certs=None, validation_context=None): """ :param end_entity_cert: An asn1crypto.x509.Certificate object or a byte string of the DER or PEM-encoded X.509 end-entity certificate to validate :param intermediate_certs: None or a list of asn1crypto.x509.Certificate objects or a byte string of a DER or PEM-encoded X.509 certificate. Used in constructing certificate paths for validation. :param validation_context: A certvalidator.context.ValidationContext() object that controls validation options """ if not isinstance(end_entity_cert, Certificate): if not isinstance(end_entity_cert, byte_cls): raise TypeError(pretty_message( ''' end_entity_cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(end_entity_cert) )) if pem.detect(end_entity_cert): _, _, end_entity_cert = pem.unarmor(end_entity_cert) end_entity_cert = Certificate.load(end_entity_cert) if validation_context is None: validation_context = ValidationContext() if not isinstance(validation_context, ValidationContext): raise TypeError(pretty_message( ''' validation_context must be an instance of certvalidator.context.ValidationContext, not %s ''', type_name(validation_context) )) if intermediate_certs is not None: certificate_registry = validation_context.certificate_registry for intermediate_cert in intermediate_certs: certificate_registry.add_other_cert(intermediate_cert) self._context = validation_context self._certificate = end_entity_cert
def build_paths(self, end_entity_cert): """ Builds a list of ValidationPath objects from a certificate in the operating system trust store to the end-entity certificate :param end_entity_cert: A byte string of a DER or PEM-encoded X.509 certificate, or an instance of asn1crypto.x509.Certificate :return: A list of certvalidator.path.ValidationPath objects that represent the possible paths from the end-entity certificate to one of the CA certs. """ if not isinstance(end_entity_cert, byte_cls) and not isinstance(end_entity_cert, x509.Certificate): raise TypeError(pretty_message( ''' end_entity_cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(end_entity_cert) )) if isinstance(end_entity_cert, byte_cls): if pem.detect(end_entity_cert): _, _, end_entity_cert = pem.unarmor(end_entity_cert) end_entity_cert = x509.Certificate.load(end_entity_cert) path = ValidationPath(end_entity_cert) paths = [] failed_paths = [] self._walk_issuers(path, paths, failed_paths) if len(paths) == 0: cert_name = end_entity_cert.subject.human_friendly missing_issuer_name = failed_paths[0].first.issuer.human_friendly raise PathBuildingError(pretty_message( ''' Unable to build a validation path for the certificate "%s" - no issuer matching "%s" was found ''', cert_name, missing_issuer_name )) return paths
def pem_to_der(cert: bytes, return_multiple: bool = True): """Convert a given certificate or list to PEM format.""" # initialize the certificate array cert_list = [] # If certificate is in DER then un-armour it if pem.detect(cert): for _, _, der_bytes in pem.unarmor(cert, multiple=True): cert_list.append(der_bytes) else: cert_list.append(cert) # return multiple if return_multiple is set else first element if return_multiple: return cert_list return cert_list.pop()
def build_paths(self, end_entity_cert): """ Builds a list of ValidationPath objects from a certificate in the operating system trust store to the end-entity certificate :param end_entity_cert: A byte string of a DER or PEM-encoded X.509 certificate, or an instance of asn1crypto.x509.Certificate :return: A list of pyhanko_certvalidator.path.ValidationPath objects that represent the possible paths from the end-entity certificate to one of the CA certs. """ if not isinstance(end_entity_cert, byte_cls) and not isinstance( end_entity_cert, x509.Certificate): raise TypeError( pretty_message( ''' end_entity_cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(end_entity_cert))) if isinstance(end_entity_cert, byte_cls): if pem.detect(end_entity_cert): _, _, end_entity_cert = pem.unarmor(end_entity_cert) end_entity_cert = x509.Certificate.load(end_entity_cert) path = ValidationPath(end_entity_cert) paths = [] failed_paths = [] self._walk_issuers(path, paths, failed_paths) if len(paths) == 0: cert_name = end_entity_cert.subject.human_friendly missing_issuer_name = failed_paths[0].first.issuer.human_friendly raise PathBuildingError( pretty_message( ''' Unable to build a validation path for the certificate "%s" - no issuer matching "%s" was found ''', cert_name, missing_issuer_name)) return paths
def add_other_cert(self, cert): """ Allows adding an "other" cert that is obtained from doing revocation check via OCSP or CRL, or some other method :param cert: An asn1crypto.x509.Certificate object or a byte string of a DER or PEM-encoded certificate :return: A boolean indicating if the certificate was added - will return False if the certificate was already present """ if not isinstance(cert, x509.Certificate): if not isinstance(cert, byte_cls): raise TypeError(pretty_message( ''' cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(cert) )) if pem.detect(cert): _, _, cert = pem.unarmor(cert) cert = x509.Certificate.load(cert) hashable = cert.subject.hashable if hashable not in self._subject_map: self._subject_map[hashable] = [] # Don't add the cert if we already have it else: serial_number = cert.serial_number for existing_cert in self._subject_map[hashable]: if existing_cert.serial_number == serial_number: return False self._subject_map[hashable].append(cert) if cert.key_identifier: self._key_identifier_map[cert.key_identifier] = cert else: self._key_identifier_map[cert.public_key.sha1] = cert return True
def load_private_key_from_pemder(key_file, passphrase: Optional[bytes]) \ -> keys.PrivateKeyInfo: """ A convenience function to load PEM/DER-encoded keys from files. :param key_file: File to read the key from. :param passphrase: Key passphrase. :return: A private key encoded as an unencrypted PKCS#8 PrivateKeyInfo object. """ with open(key_file, 'rb') as f: key_bytes = f.read() load_fun = (serialization.load_pem_private_key if pem.detect(key_bytes) else serialization.load_der_private_key) return _translate_pyca_cryptography_key_to_asn1( load_fun(key_bytes, password=passphrase))
def parse_cert(byte_data): if not isinstance(byte_data, bytes): raise TypeError("byte_data must be a byte string") if byte_data[0] == 'M': byte_data = base64.b64decode(byte_data) if pem.detect(byte_data): file_type, headers, byte_data = pem.unarmor(byte_data) if file_type != 'CERTIFICATE': raise TypeError( "CERTIFICATE expected, but got {}".format(file_type)) x509cert = x509.Certificate.load(byte_data) return x509cert
def add_other_cert(self, cert): """ Allows adding an "other" cert that is obtained from doing revocation check via OCSP or CRL, or some other method :param cert: An asn1crypto.x509.Certificate object or a byte string of a DER or PEM-encoded certificate :return: A boolean indicating if the certificate was added - will return False if the certificate was already present """ if not isinstance(cert, x509.Certificate): if not isinstance(cert, byte_cls): raise TypeError( pretty_message( ''' cert must be a byte string or an instance of asn1crypto.x509.Certificate, not %s ''', type_name(cert))) if pem.detect(cert): _, _, cert = pem.unarmor(cert) cert = x509.Certificate.load(cert) hashable = cert.subject.hashable if hashable not in self._subject_map: self._subject_map[hashable] = [] # Don't add the cert if we already have it else: serial_number = cert.serial_number for existing_cert in self._subject_map[hashable]: if existing_cert.serial_number == serial_number: return False self._subject_map[hashable].append(cert) if cert.key_identifier: self._key_identifier_map[cert.key_identifier] = cert else: self._key_identifier_map[cert.public_key.sha1] = cert return True
def decode(byte_string): try: if pem.detect(byte_string): type_name, headers, decoded_bytes = pem.unarmor(byte_string) byte_string = decoded_bytes key = OneAsymmetricKey.load(byte_string) if key['privateKeyAlgorithm']['algorithm'].native != 'Curve25519' and key['privateKeyAlgorithm']['algorithm'].native != 'Ed25519': raise NotACurve25519Key key = PrivateKey.load(key['privateKey'].native) if len(key.native) != 32: raise NotA256BitCurve25519Key except Exception as e: raise FailedToParseCurve25519Key return key.native
def unwrap_pkcs8(blob): if not pem.detect(blob): return _, _, der_bytes = pem.unarmor(blob) data = EncryptedPrivateKeyInfo.load(der_bytes).native if "encryption_algorithm" not in data: return if "encrypted_data" not in data: return if "algorithm" not in data["encryption_algorithm"]: return if data["encryption_algorithm"]["algorithm"] != "pbes2": sys.stderr.write("[%s] encryption_algorithm <%s> is not supported currently!\n" % (sys.argv[0], data["encryption_algorithm"]["algorithm"])) return # encryption data encrypted_data = data["encrypted_data"] # KDF params = data["encryption_algorithm"]["parameters"] kdf = params["key_derivation_func"] if kdf["algorithm"] != "pbkdf2": sys.stderr.write("[%s] kdf algorithm <%s> is not supported currently!\n" % (sys.argv[0], kdf["algorithm"])) return kdf_params = kdf["parameters"] salt = kdf_params["salt"] iterations = kdf_params["iteration_count"] # Cipher cipher_params = params["encryption_scheme"] cipher = cipher_params["algorithm"] iv = cipher_params["parameters"] if cipher != "tripledes_3key": sys.stderr.write("[%s] cipher <%s> is not supported currently!\n" % (sys.argv[0], cipher)) return sys.stdout.write("$PEM$1$1$%s$%s$%s$%d$%s\n" % (salt.encode("hex"), iterations, iv.encode("hex"), len(encrypted_data), encrypted_data.encode("hex")))
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'codex.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual([ b'\xaa+\x03\x14\xafd.\x13\x0e\xd6\x92%\xe3\xff*\xba\xd7=b0', b"\xfcq\x7f\x98='\xcc\xb3D\xfbK\x85\xf0\x81\x8f\xab\xcb\xf0\x9b\x14" ], [item.subject.sha1 for item in path])
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'mozilla.org.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open( os.path.join(fixtures_dir, 'digicert-sha2-secure-server-ca.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual([ b"\x10_\xa6z\x80\x08\x9d\xb5'\x9f5\xce\x83\x0bC\x88\x9e\xa3\xc7\r", b'I\xac\x03\xf8\xf3Km\xca)V)\xf2I\x9a\x98\xbe\x98\xdc.\x81' ], [item.subject.sha1 for item in path])
def test_revocation(self): with Now(utcdatetime(2000, 1, 1)): ca, _ = self.issue_ca() crl = self.new_crl(ca) crl_url = crl.url() # Retired revoked certificate. entity, cert = self.issue_entity('Test', ca) cert.revoked_at = now() + timedelta(1) cert.state = Certificate.RETIRED cert.save() # Nonsensical revoked reservation. cert = Certificate.objects.reserve(entity, utcdatetime(2000, 2, 1), utcdatetime(2000, 5, 1)) cert.revoked_at = utcdatetime(2000, 2, 1) cert.save() # Normal revoked certificate. with Now(utcdatetime(2000, 3, 1)): cert = ca.issue_cert(entity, self.keys['entity'][0], expires_at=timedelta(days=90)) with Now(utcdatetime(2000, 3, 2)): cert.revoke() with Now(utcdatetime(2000, 3, 2)): tasks.refresh_crls() crl = CRL.objects.get(pk=crl.pk) asn_crl = crl.asn_crl() self.assertEqual(crl.url(), crl_url) self.assertEqual(Certificate.objects.revokable().count(), 2) self.assertEqual(Certificate.objects.issued().revoked().count(), 1) self.assertEqual(ca.issued_certificate_set.count(), 3) self.assertEqual(crl.revoked_certificate_set.count(), 1) self.assertEqual( [rc['user_certificate'] for rc in asn_crl['tbs_cert_list']['revoked_certificates'].native], [cert.serial] ) self.assertTrue(pem.detect(crl.pem()))
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'codex.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual( [ b'\xaa+\x03\x14\xafd.\x13\x0e\xd6\x92%\xe3\xff*\xba\xd7=b0', b"\xfcq\x7f\x98='\xcc\xb3D\xfbK\x85\xf0\x81\x8f\xab\xcb\xf0\x9b\x14" ], [item.subject.sha1 for item in path] )
def detect(self, relative_path, is_pem): with open(os.path.join(fixtures_dir, relative_path), 'rb') as f: byte_string = f.read() self.assertEqual(is_pem, pem.detect(byte_string))
def test_get_path(self): certs = trust_list.get_path() with open(certs, "rb") as f: cert_data = f.read() self.assertEqual(True, pem.detect(cert_data)) self.assertLess(10240, len(cert_data))