def get_cert_days(self, cert_filename=None, cert_content=None, now=None): ''' Return the days the certificate in cert_filename remains valid and -1 if the file was not found. If cert_filename contains more than one certificate, only the first one will be considered. If now is not specified, datetime.datetime.now() is used. ''' if cert_filename is not None: cert_content = None if os.path.exists(cert_filename): cert_content = read_file(cert_filename) else: cert_content = to_bytes(cert_content) if cert_content is None: return -1 try: cert = cryptography.x509.load_pem_x509_certificate( cert_content, _cryptography_backend) except Exception as e: if cert_filename is None: raise BackendException( 'Cannot parse certificate: {0}'.format(e)) raise BackendException('Cannot parse certificate {0}: {1}'.format( cert_filename, e)) if now is None: now = datetime.datetime.now() return (cert.not_valid_after - now).days
def create_mac_key(self, alg, key): '''Create a MAC key.''' if alg == 'HS256': hashalg = cryptography.hazmat.primitives.hashes.SHA256 hashbytes = 32 elif alg == 'HS384': hashalg = cryptography.hazmat.primitives.hashes.SHA384 hashbytes = 48 elif alg == 'HS512': hashalg = cryptography.hazmat.primitives.hashes.SHA512 hashbytes = 64 else: raise BackendException( 'Unsupported MAC key algorithm for cryptography backend: {0}'. format(alg)) key_bytes = base64.urlsafe_b64decode(key) if len(key_bytes) < hashbytes: raise BackendException( '{0} key must be at least {1} bytes long (after Base64 decoding)' .format(alg, hashbytes)) return { 'mac_obj': lambda: cryptography.hazmat.primitives.hmac.HMAC( key_bytes, hashalg(), _cryptography_backend), 'type': 'hmac', 'alg': alg, 'jwk': { 'kty': 'oct', 'k': key, }, }
def create_mac_key(self, alg, key): '''Create a MAC key.''' if alg == 'HS256': hashalg = 'sha256' hashbytes = 32 elif alg == 'HS384': hashalg = 'sha384' hashbytes = 48 elif alg == 'HS512': hashalg = 'sha512' hashbytes = 64 else: raise BackendException( 'Unsupported MAC key algorithm for OpenSSL backend: {0}'. format(alg)) key_bytes = base64.urlsafe_b64decode(key) if len(key_bytes) < hashbytes: raise BackendException( '{0} key must be at least {1} bytes long (after Base64 decoding)' .format(alg, hashbytes)) return { 'type': 'hmac', 'alg': alg, 'jwk': { 'kty': 'oct', 'k': key, }, 'hash': hashalg, }
def get_csr_identifiers(self, csr_filename=None, csr_content=None): ''' Return a set of requested identifiers (CN and SANs) for the CSR. Each identifier is a pair (type, identifier), where type is either 'dns' or 'ip'. ''' identifiers = set([]) if csr_content is None: csr_content = read_file(csr_filename) else: csr_content = to_bytes(csr_content) csr = cryptography.x509.load_pem_x509_csr(csr_content, _cryptography_backend) for sub in csr.subject: if sub.oid == cryptography.x509.oid.NameOID.COMMON_NAME: identifiers.add(('dns', sub.value)) for extension in csr.extensions: if extension.oid == cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME: for name in extension.value: if isinstance(name, cryptography.x509.DNSName): identifiers.add(('dns', name.value)) elif isinstance(name, cryptography.x509.IPAddress): identifiers.add(('ip', name.value.compressed)) else: raise BackendException( 'Found unsupported SAN identifier {0}'.format( name)) return identifiers
def create_chain_matcher(self, criterium): ''' Given a Criterium object, creates a ChainMatcher object. ''' raise BackendException( 'Alternate chain matching can only be used with the "cryptography" backend.' )
def get_cert_days(self, cert_filename=None, cert_content=None, now=None): ''' Return the days the certificate in cert_filename remains valid and -1 if the file was not found. If cert_filename contains more than one certificate, only the first one will be considered. If now is not specified, datetime.datetime.now() is used. ''' filename = cert_filename data = None if cert_content is not None: filename = '-' data = cert_content.encode('utf-8') cert_filename_suffix = '' elif cert_filename is not None: if not os.path.exists(cert_filename): return -1 cert_filename_suffix = ' in {0}'.format(cert_filename) else: return -1 openssl_cert_cmd = [ self.openssl_binary, "x509", "-in", filename, "-noout", "-text" ] dummy, out, dummy = self.module.run_command( openssl_cert_cmd, data=data, check_rc=True, binary_data=True, environ_update=_OPENSSL_ENVIRONMENT_UPDATE) try: not_after_str = re.search( r"\s+Not After\s*:\s+(.*)", to_text(out, errors='surrogate_or_strict')).group(1) not_after = datetime.datetime.strptime(not_after_str, '%b %d %H:%M:%S %Y %Z') except AttributeError: raise BackendException( "No 'Not after' date found{0}".format(cert_filename_suffix)) except ValueError: raise BackendException( "Failed to parse 'Not after' date{0}".format( cert_filename_suffix)) if now is None: now = datetime.datetime.now() return (not_after - now).days
def sign(self, payload64, protected64, key_data): sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8') if key_data['type'] == 'hmac': hex_key = to_native( binascii.hexlify(base64.urlsafe_b64decode( key_data['jwk']['k']))) cmd_postfix = [ "-mac", "hmac", "-macopt", "hexkey:{0}".format(hex_key), "-binary" ] else: cmd_postfix = ["-sign", key_data['key_file']] openssl_sign_cmd = [ self.openssl_binary, "dgst", "-{0}".format(key_data['hash']) ] + cmd_postfix dummy, out, dummy = self.module.run_command( openssl_sign_cmd, data=sign_payload, check_rc=True, binary_data=True, environ_update=_OPENSSL_ENVIRONMENT_UPDATE) if key_data['type'] == 'ec': dummy, der_out, dummy = self.module.run_command( [self.openssl_binary, "asn1parse", "-inform", "DER"], data=out, binary_data=True, environ_update=_OPENSSL_ENVIRONMENT_UPDATE) expected_len = 2 * key_data['point_size'] sig = re.findall( r"prim:\s+INTEGER\s+:([0-9A-F]{1,%s})\n" % expected_len, to_text(der_out, errors='surrogate_or_strict')) if len(sig) != 2: raise BackendException( "failed to generate Elliptic Curve signature; cannot parse DER output: {0}" .format(to_text(der_out, errors='surrogate_or_strict'))) sig[0] = (expected_len - len(sig[0])) * '0' + sig[0] sig[1] = (expected_len - len(sig[1])) * '0' + sig[1] out = binascii.unhexlify(sig[0]) + binascii.unhexlify(sig[1]) return { "protected": protected64, "payload": payload64, "signature": nopad_b64(to_bytes(out)), }
def get_csr_identifiers(self, csr_filename=None, csr_content=None): ''' Return a set of requested identifiers (CN and SANs) for the CSR. Each identifier is a pair (type, identifier), where type is either 'dns' or 'ip'. ''' filename = csr_filename data = None if csr_content is not None: filename = '-' data = csr_content.encode('utf-8') openssl_csr_cmd = [ self.openssl_binary, "req", "-in", filename, "-noout", "-text" ] dummy, out, dummy = self.module.run_command( openssl_csr_cmd, data=data, check_rc=True, binary_data=True, environ_update=_OPENSSL_ENVIRONMENT_UPDATE) identifiers = set([]) common_name = re.search(r"Subject:.* CN\s?=\s?([^\s,;/]+)", to_text(out, errors='surrogate_or_strict')) if common_name is not None: identifiers.add(('dns', common_name.group(1))) subject_alt_names = re.search( r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n", to_text(out, errors='surrogate_or_strict'), re.MULTILINE | re.DOTALL) if subject_alt_names is not None: for san in subject_alt_names.group(1).split(", "): if san.lower().startswith("dns:"): identifiers.add(('dns', san[4:])) elif san.lower().startswith("ip:"): identifiers.add(('ip', self._normalize_ip(san[3:]))) elif san.lower().startswith("ip address:"): identifiers.add(('ip', self._normalize_ip(san[11:]))) else: raise BackendException( 'Found unsupported SAN identifier "{0}"'.format(san)) return identifiers
def get_cert_days(self, cert_filename=None, cert_content=None, now=None): raise BackendException('Not implemented in fake backend')
def get_csr_identifiers(self, csr_filename=None, csr_content=None): raise BackendException('Not implemented in fake backend')
def create_mac_key(self, alg, key): raise BackendException('Not implemented in fake backend')
def sign(self, payload64, protected64, key_data): raise BackendException('Not implemented in fake backend')
def parse_key(self, key_file=None, key_content=None, passphrase=None): raise BackendException('Not implemented in fake backend')
def create_chain_matcher(self, criterium): raise BackendException('Not implemented in fake backend')