def as_view(cls, responder_key, responder_cert, **kwargs): priv_err_msg = '%s: Could not read private key.' % responder_key pub_err_msg = '%s: Could not read public key.' % responder_cert # Preload the responder key and certificate for faster access. try: with open(responder_key, 'rb') as stream: responder_key = stream.read() except: raise ImproperlyConfigured(priv_err_msg) try: # try to load responder key and cert with oscrypto, to make sure they are actually usable load_private_key(responder_key) except: raise ImproperlyConfigured(priv_err_msg) if os.path.exists(responder_cert): with open(responder_cert, 'rb') as stream: responder_cert = stream.read() if not responder_cert: raise ImproperlyConfigured(pub_err_msg) try: load_certificate(responder_cert) except: raise ImproperlyConfigured(pub_err_msg) return super(OCSPView, cls).as_view(responder_key=responder_key, responder_cert=responder_cert, **kwargs)
def test_cert_get(self) -> None: """Test getting OCSP responses.""" priv_path, _cert_path, ocsp_cert = self.ca.generate_ocsp_key() self.ocsp_private_key = asymmetric.load_private_key(ca_storage.path(priv_path)) url = reverse( "django_ca:ocsp-cert-get", kwargs={ "serial": self.ca.serial, "data": base64.b64encode(req1).decode("utf-8"), }, ) response = self.client.get(url) self.assertEqual(response.status_code, HTTPStatus.OK) # URL config sets expires to 3600 self.assertOCSP( response, requested=[self.cert], nonce=req1_asn1_nonce, ocsp_cert=ocsp_cert, expires=3600 ) priv_path, _cert_path, ocsp_cert = self.ca.generate_ocsp_key(key_size=1024) self.ocsp_private_key = asymmetric.load_private_key(ca_storage.path(priv_path)) response = self.client.get(url) self.assertEqual(response.status_code, HTTPStatus.OK) # URL config sets expires to 3600 self.assertOCSP( response, requested=[self.cert], nonce=req1_asn1_nonce, ocsp_cert=ocsp_cert, expires=3600 )
def test_cert_get(self): ca = self.cas['child'] cert = self.certs['child-cert'] priv_path, cert_path, ocsp_cert = ca.generate_ocsp_key() self.ocsp_private_key = asymmetric.load_private_key( ca_storage.path(priv_path)) url = reverse('django_ca:ocsp-cert-get', kwargs={ 'serial': self.cas['child'].serial, 'data': base64.b64encode(req1).decode('utf-8'), }) response = self.client.get(url) self.assertEqual(response.status_code, 200) # URL config sets expires to 3600 self.assertOCSP(response, requested=[cert], nonce=req1_nonce, ocsp_cert=ocsp_cert, expires=3600) priv_path, cert_path, ocsp_cert = ca.generate_ocsp_key(key_size=1024) self.ocsp_private_key = asymmetric.load_private_key( ca_storage.path(priv_path)) response = self.client.get(url) self.assertEqual(response.status_code, 200) # URL config sets expires to 3600 self.assertOCSP(response, requested=[cert], nonce=req1_nonce, ocsp_cert=ocsp_cert, expires=3600)
def test_dump_private_openssl(self): private = asymmetric.load_private_key( os.path.join(fixtures_dir, 'keys/test.key')) pem_serialized = asymmetric.dump_openssl_private_key( private, 'password123') private_reloaded = asymmetric.load_private_key(pem_serialized, 'password123') self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm)
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 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_dsa_generate(self): public, private = asymmetric.generate_pair('dsa', bit_size=1024) self.assertEqual('dsa', public.algorithm) self.assertEqual(1024, public.bit_size) original_data = b'This is data to sign' signature = asymmetric.dsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha1') raw_public = asymmetric.dump_public_key(public) asymmetric.load_public_key(raw_public) raw_private = asymmetric.dump_private_key(private, None) asymmetric.load_private_key(raw_private, None)
def test_ec_generate(self): public, private = asymmetric.generate_pair('ec', curve='secp256r1') self.assertEqual('ec', public.algorithm) self.assertEqual('secp256r1', public.asn1.curve[1]) original_data = b'This is data to sign' signature = asymmetric.ecdsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.ecdsa_verify(public, signature, original_data, 'sha1') raw_public = asymmetric.dump_public_key(public) asymmetric.load_public_key(raw_public) raw_private = asymmetric.dump_private_key(private, None) asymmetric.load_private_key(raw_private, None)
def test_rsa_private_oaep_decrypt(self): original_data = b'This is the message to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) with open(os.path.join(fixtures_dir, 'rsa_public_encrypted_oaep'), 'rb') as f: plaintext = asymmetric.rsa_oaep_decrypt(private, f.read()) self.assertEqual(original_data, plaintext)
def test_demo_plugin(requests_mock): with_plugin_cfg = CertomancerConfig.from_file('tests/data/with-plugin.yml', 'tests/data') arch = with_plugin_cfg.get_pki_arch(ArchLabel('testing-ca')) illusionist.Illusionist(pki_arch=arch).register(requests_mock) importlib.import_module('example_plugin.encrypt_echo') # make the endpoint encrypt something endpoint = 'http://test.test/testing-ca/plugin/encrypt-echo/test-endpoint' payload = b'test test test' response = requests.post(endpoint, data=payload) # decrypt it env_data = cms.ContentInfo.load(response.content)['content'] key = arch.key_set.get_private_key(KeyLabel('signer1')) ktri = env_data['recipient_infos'][0].chosen encrypted_key = ktri['encrypted_key'].native decrypted_key = asymmetric.rsa_pkcs1v15_decrypt( asymmetric.load_private_key(key.dump()), encrypted_key) eci = env_data['encrypted_content_info'] cea = eci['content_encryption_algorithm'] assert cea['algorithm'].native == 'aes256_cbc' iv = cea['parameters'].native encrypted_content_bytes = eci['encrypted_content'].native decrypted_payload = symmetric.aes_cbc_pkcs7_decrypt( decrypted_key, encrypted_content_bytes, iv) assert decrypted_payload == payload
def test_build_signed_request(self): issuer_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, "test.crt")) subject_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, "test-inter.crt")) requestor_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, "test-third.crt")) requestor_key = asymmetric.load_private_key(os.path.join(fixtures_dir, "test-third.key")) builder = OCSPRequestBuilder(subject_cert, issuer_cert) ocsp_request = builder.build(requestor_key, requestor_cert, [subject_cert, issuer_cert]) der_bytes = ocsp_request.dump() new_request = asn1crypto.ocsp.OCSPRequest.load(der_bytes) tbs_request = new_request["tbs_request"] signature = new_request["optional_signature"] self.assertEqual("sha256", signature["signature_algorithm"].hash_algo) self.assertEqual("rsassa_pkcs1v15", signature["signature_algorithm"].signature_algo) self.assertEqual(3, len(signature["certs"])) self.assertEqual("v1", tbs_request["version"].native) self.assertEqual(requestor_cert.asn1.subject, tbs_request["requestor_name"].chosen) self.assertEqual(1, len(tbs_request["request_list"])) request = tbs_request["request_list"][0] self.assertEqual("sha1", request["req_cert"]["hash_algorithm"]["algorithm"].native) self.assertEqual(issuer_cert.asn1.subject.sha1, request["req_cert"]["issuer_name_hash"].native) self.assertEqual(issuer_cert.asn1.public_key.sha1, request["req_cert"]["issuer_key_hash"].native) self.assertEqual(subject_cert.asn1.serial_number, request["req_cert"]["serial_number"].native) self.assertEqual(0, len(request["single_request_extensions"])) self.assertEqual(1, len(tbs_request["request_extensions"])) extn = tbs_request["request_extensions"][0] self.assertEqual("nonce", extn["extn_id"].native) self.assertEqual(16, len(extn["extn_value"].parsed.native))
def frompem(cls, issuer_cert: StrOrBytes, responder_cert: StrOrBytes, responder_key: StrOrBytes, validate_func: ValidateFunc, cert_retrieve_func: CertRetrieveFunc = None, next_update_days: int = 7): """ Create a new OCSPResponder instance from filepaths or bytes. :param issuer_cert: Path to or Bytes of the issuer certificate. :param responder_cert: Path to or Bytes of the certificate of the OCSP responder with the `OCSP Signing` extension. :param responder_key: Path to or Bytes of the private key belonging to the responder cert. :param validate_func: A function that - given a certificate serial - will return the appropriate :class:`CertificateStatus` and - depending on the status - a revocation datetime. :param cert_retrieve_func: A function that - given a certificate serial - will return the corresponding certificate as a string. :param next_update_days: The ``nextUpdate`` value that will be written into the response. Default: 7 days. """ # Certs and keys _issuer_cert = asymmetric.load_certificate(issuer_cert) _responder_cert = asymmetric.load_certificate(responder_cert) _responder_key = asymmetric.load_private_key(responder_key) return cls(_issuer_cert, _responder_cert, _responder_key, validate_func=validate_func, cert_retrieve_func=cert_retrieve_func, next_update_days=next_update_days)
def test_build_revoked_no_reason(self): issuer_key = asymmetric.load_private_key( os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test-inter.crt')) revoked_time = datetime(2015, 9, 1, 12, 0, 0, tzinfo=timezone.utc) builder = OCSPResponseBuilder('successful', [{ 'certificate': subject_cert, 'certificate_status': 'revoked', 'revocation_date': revoked_time }]) ocsp_response = builder.build(issuer_key, issuer_cert) der_bytes = ocsp_response.dump() new_response = asn1crypto.ocsp.OCSPResponse.load(der_bytes) basic_response = new_response['response_bytes']['response'].parsed response_data = basic_response['tbs_response_data'] cert_response = response_data['responses'][0] self.assertEqual('revoked', cert_response['cert_status'].name) self.assertEqual( revoked_time, cert_response['cert_status'].chosen['revocation_time'].native) self.assertEqual( 'unspecified', cert_response['cert_status'].chosen['revocation_reason'].native)
def test_private_key_ec_attributes(self): private_key = asymmetric.load_private_key( os.path.join(fixtures_dir, 'keys/test-ec-named.key')) self.assertEqual(256, private_key.bit_size) self.assertEqual(32, private_key.byte_size) self.assertEqual('secp256r1', private_key.curve) self.assertEqual('ec', private_key.algorithm)
def test_build_no_certificate(self): issuer_key = asymmetric.load_private_key( os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test-inter.crt')) with self.assertRaisesRegexp( ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert, 'good') builder.certificate = None builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp( ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert, 'good') builder.certificate_status = None builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp( ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert) builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp( ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', None, 'good') builder.build(issuer_key, issuer_cert)
def __init__(self, issuer_cert: str, responder_cert: str, responder_key: str, fault: str, next_update_seconds: int): """ Create a new OCSPResponder instance. :param issuer_cert: Path to the issuer certificate. :param responder_cert: Path to the certificate of the OCSP responder with the `OCSP Signing` extension. :param responder_key: Path to the private key belonging to the responder cert. :param validate_func: A function that - given a certificate serial - will return the appropriate :class:`CertificateStatus` and - depending on the status - a revocation datetime. :param cert_retrieve_func: A function that - given a certificate serial - will return the corresponding certificate as a string. :param next_update_seconds: The ``nextUpdate`` value that will be written into the response. Default: 9 hours. """ # Certs and keys self._issuer_cert = asymmetric.load_certificate(issuer_cert) self._responder_cert = asymmetric.load_certificate(responder_cert) self._responder_key = asymmetric.load_private_key(responder_key) # Next update self._next_update_seconds = next_update_seconds self._fault = fault
def setUp(self) -> None: # pylint: disable=invalid-name,missing-function-docstring super().setUp() # used for verifying signatures key_path = os.path.join(settings.FIXTURES_DIR, ocsp_profile["key_filename"]) self.ocsp_private_key = asymmetric.load_private_key(key_path)
def test_demo_plugin(): with_plugin_cfg = CertomancerConfig.from_file('tests/data/with-plugin.yml', 'tests/data') with_plugin_app = Animator(AnimatorArchStore(with_plugin_cfg.pki_archs), with_web_ui=False) client = Client(with_plugin_app, Response) # make the endpoint encrypt something endpoint = '/testing-ca/plugin/encrypt-echo/test-endpoint' payload = b'test test test' response = client.post(endpoint, data=payload) # decrypt it env_data = cms.ContentInfo.load(response.data)['content'] arch = with_plugin_cfg.get_pki_arch(ArchLabel('testing-ca')) key = arch.key_set.get_private_key(KeyLabel('signer1')) ktri = env_data['recipient_infos'][0].chosen encrypted_key = ktri['encrypted_key'].native decrypted_key = asymmetric.rsa_pkcs1v15_decrypt( asymmetric.load_private_key(key.dump()), encrypted_key) eci = env_data['encrypted_content_info'] cea = eci['content_encryption_algorithm'] assert cea['algorithm'].native == 'aes256_cbc' iv = cea['parameters'].native encrypted_content_bytes = eci['encrypted_content'].native decrypted_payload = symmetric.aes_cbc_pkcs7_decrypt( decrypted_key, encrypted_content_bytes, iv) assert decrypted_payload == payload
def load_key(key_str, key_pass): """ Function to load password protected key file in p12 or pem format""" try: # First try to parse as a p12 file key, cert, _ = asymmetric.load_pkcs12(key_str, key_pass) except ValueError as e: # If it fails due to invalid password raise error here if e.args[0] == 'Password provided is invalid': raise AS2Exception('Password not valid for Private Key.') # if not try to parse as a pem file key, cert = None, None for kc in split_pem(key_str): try: cert = asymmetric.load_certificate(kc) except (ValueError, TypeError): try: key = asymmetric.load_private_key(kc, key_pass) except OSError: raise AS2Exception( 'Invalid Private Key or password is not correct.') if not key or not cert: raise AS2Exception( 'Invalid Private key file or Public key not included.') return key, cert
def test_build_good_response(self): issuer_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test-inter.crt')) builder = OCSPResponseBuilder('successful', subject_cert, 'good') ocsp_response = builder.build(issuer_key, issuer_cert) der_bytes = ocsp_response.dump() new_response = asn1crypto.ocsp.OCSPResponse.load(der_bytes) basic_response = new_response['response_bytes']['response'].parsed response_data = basic_response['tbs_response_data'] self.assertEqual('sha256', basic_response['signature_algorithm'].hash_algo) self.assertEqual('rsassa_pkcs1v15', basic_response['signature_algorithm'].signature_algo) self.assertEqual('v1', response_data['version'].native) self.assertEqual('by_key', response_data['responder_id'].name) self.assertEqual( issuer_cert.asn1.public_key.sha1, response_data['responder_id'].chosen.native ) self.assertGreaterEqual(datetime.now(timezone.utc), response_data['produced_at'].native) self.assertEqual(1, len(response_data['responses'])) self.assertEqual(0, len(response_data['response_extensions'])) cert_response = response_data['responses'][0] self.assertEqual('sha1', cert_response['cert_id']['hash_algorithm']['algorithm'].native) self.assertEqual(issuer_cert.asn1.subject.sha1, cert_response['cert_id']['issuer_name_hash'].native) self.assertEqual(issuer_cert.asn1.public_key.sha1, cert_response['cert_id']['issuer_key_hash'].native) self.assertEqual(subject_cert.asn1.serial_number, cert_response['cert_id']['serial_number'].native) self.assertEqual('good', cert_response['cert_status'].name) self.assertGreaterEqual(datetime.now(timezone.utc), cert_response['this_update'].native) self.assertGreaterEqual(set(), cert_response.critical_extensions)
def self_enroll(skip_notify=False): assert os.getuid() == 0 and os.getgid( ) == 0, "Can self-enroll only as root" from certidude import const, config common_name = const.FQDN os.umask(0o0177) try: path, buf, cert, signed, expires = get_signed(common_name) self_public_key = asymmetric.load_public_key(path) private_key = asymmetric.load_private_key(config.SELF_KEY_PATH) except FileNotFoundError: # certificate or private key not found click.echo("Generating private key for frontend: %s" % config.SELF_KEY_PATH) with open(config.SELF_KEY_PATH, 'wb') as fh: if public_key.algorithm == "ec": self_public_key, private_key = asymmetric.generate_pair( "ec", curve=public_key.curve) elif public_key.algorithm == "rsa": self_public_key, private_key = asymmetric.generate_pair( "rsa", bit_size=public_key.bit_size) else: raise NotImplemented( "CA certificate public key algorithm %s not supported" % public_key.algorithm) fh.write(asymmetric.dump_private_key(private_key, None)) else: now = datetime.utcnow() if now + timedelta(days=1) < expires: click.echo( "Certificate %s still valid, delete to self-enroll again" % path) return builder = CSRBuilder({"common_name": common_name}, self_public_key) request = builder.build(private_key) pid = os.fork() if not pid: from certidude import authority, config from certidude.common import drop_privileges drop_privileges() assert os.getuid() != 0 and os.getgid() != 0 path = os.path.join(config.REQUESTS_DIR, common_name + ".pem") click.echo("Writing certificate signing request for frontend: %s" % path) with open(path, "wb") as fh: fh.write( pem_armor_csr(request)) # Write CSR with certidude permissions authority.sign(common_name, skip_notify=skip_notify, skip_push=True, overwrite=True, profile=config.PROFILES["srv"]) click.echo("Frontend certificate signed") sys.exit(0) else: os.waitpid(pid, 0) os.system("systemctl reload nginx")
def test_ec_public_key_attr(self): private = asymmetric.load_private_key( os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key( os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) computed_public = private.public_key self.assertEqual(public.asn1.dump(), computed_public.asn1.dump())
def test_ec_fingerprints(self): private = asymmetric.load_private_key( os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key( os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint)
def test_ecdsa_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) signature = asymmetric.ecdsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.ecdsa_verify(public, signature, original_data, 'sha1')
def do_run(): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha256') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha256')
def test_rsa_raw_sign(self): original_data = b'This is data to sign!' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'raw') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw')
def setUpClass(cls): super(OCSPTestView, cls).setUpClass() logging.disable(logging.CRITICAL) cls.client = Client() cls.ocsp_cert = cls.load_cert(ca=cls.ca, x509=ocsp_pubkey) # used for verifying signatures cls.ocsp_private_key = asymmetric.load_private_key(ocsp_key_path)
def test_build_revoked_response(self): issuer_key = asymmetric.load_private_key( os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate( os.path.join(fixtures_dir, 'test-inter.crt')) revoked_time = datetime(2015, 9, 1, 12, 0, 0, tzinfo=timezone.utc) builder = OCSPResponseBuilder( 'successful', [{ 'certificate': subject_cert, 'certificate_status': 'key_compromise', 'revocation_date': revoked_time }]) ocsp_response = builder.build(issuer_key, issuer_cert) der_bytes = ocsp_response.dump() new_response = asn1crypto.ocsp.OCSPResponse.load(der_bytes) basic_response = new_response['response_bytes']['response'].parsed response_data = basic_response['tbs_response_data'] self.assertEqual('sha256', basic_response['signature_algorithm'].hash_algo) self.assertEqual('rsassa_pkcs1v15', basic_response['signature_algorithm'].signature_algo) self.assertEqual('v1', response_data['version'].native) self.assertEqual('by_key', response_data['responder_id'].name) self.assertEqual(issuer_cert.asn1.public_key.sha1, response_data['responder_id'].chosen.native) self.assertGreaterEqual(datetime.now(timezone.utc), response_data['produced_at'].native) self.assertEqual(1, len(response_data['responses'])) self.assertEqual(0, len(response_data['response_extensions'])) cert_response = response_data['responses'][0] self.assertEqual( 'sha1', cert_response['cert_id']['hash_algorithm']['algorithm'].native) self.assertEqual(issuer_cert.asn1.subject.sha1, cert_response['cert_id']['issuer_name_hash'].native) self.assertEqual(issuer_cert.asn1.public_key.sha1, cert_response['cert_id']['issuer_key_hash'].native) self.assertEqual(subject_cert.asn1.serial_number, cert_response['cert_id']['serial_number'].native) self.assertEqual('revoked', cert_response['cert_status'].name) self.assertEqual( revoked_time, cert_response['cert_status'].chosen['revocation_time'].native) self.assertEqual( 'key_compromise', cert_response['cert_status'].chosen['revocation_reason'].native) self.assertGreaterEqual(datetime.now(timezone.utc), cert_response['this_update'].native) self.assertGreaterEqual(set(), cert_response.critical_extensions)
def setUp(self): # pylint: disable=invalid-name,missing-function-docstring super().setUp() self.client = Client() # we want to be anonymous # used for verifying signatures key_path = os.path.join(settings.FIXTURES_DIR, ocsp_profile['key_filename']) self.ocsp_private_key = asymmetric.load_private_key(key_path)
def self_enroll(): assert os.getuid() == 0 and os.getgid( ) == 0, "Can self-enroll only as root" from certidude import const common_name = const.FQDN directory = os.path.join("/var/lib/certidude", const.FQDN) self_key_path = os.path.join(directory, "self_key.pem") try: path, buf, cert, signed, expires = get_signed(common_name) self_public_key = asymmetric.load_public_key(path) private_key = asymmetric.load_private_key(self_key_path) except FileNotFoundError: # certificate or private key not found with open(self_key_path, 'wb') as fh: if public_key.algorithm == "ec": self_public_key, private_key = asymmetric.generate_pair( "ec", curve=public_key.curve) elif public_key.algorithm == "rsa": self_public_key, private_key = asymmetric.generate_pair( "rsa", bit_size=public_key.bit_size) else: NotImplemented fh.write(asymmetric.dump_private_key(private_key, None)) else: now = datetime.utcnow() if now + timedelta(days=1) < expires: click.echo( "Certificate %s still valid, delete to self-enroll again" % path) return builder = CSRBuilder({"common_name": common_name}, self_public_key) request = builder.build(private_key) pid = os.fork() if not pid: from certidude import authority from certidude.common import drop_privileges drop_privileges() assert os.getuid() != 0 and os.getgid() != 0 path = os.path.join(directory, "requests", common_name + ".pem") click.echo("Writing request to %s" % path) with open(path, "wb") as fh: fh.write( pem_armor_csr(request)) # Write CSR with certidude permissions authority.sign(common_name, skip_push=True, overwrite=True, profile=config.PROFILES["srv"]) sys.exit(0) else: os.waitpid(pid, 0) if os.path.exists("/etc/systemd"): os.system("systemctl reload nginx") else: os.system("service nginx reload")
def test_rsa_oaep_encrypt(self): original_data = b'This is data to encrypt' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) ciphertext = asymmetric.rsa_oaep_encrypt(public, original_data) self.assertIsInstance(ciphertext, byte_cls) plaintext = asymmetric.rsa_oaep_decrypt(private, ciphertext) self.assertEqual(original_data, plaintext)
def setUpClass(cls): super(OCSPViewTestMixin, cls).setUpClass() logging.disable(logging.CRITICAL) cls.client = Client() cls.ocsp_cert = cls.load_cert(ca=cls.ca, x509=ocsp_pubkey) # used for verifying signatures cls.ocsp_private_key = asymmetric.load_private_key( force_text(settings.OCSP_KEY_PATH))
def setUp(self): super(OCSPViewTestMixin, self).setUp() self.client = Client() # used for verifying signatures ocsp_key_path = os.path.join(settings.FIXTURES_DIR, ocsp_profile['key_filename']) self.ocsp_private_key = asymmetric.load_private_key( force_text(ocsp_key_path))
def _read_key(self, path): # type: (str) -> str try: privkey = asymmetric.load_private_key(path) except Exception as e: raise CommandError("Error reading private key at {}: {}".format(path, e)) key_pem = asymmetric.dump_private_key(privkey, None, 'pem') return key_pem
def test_build_basic_crl(self): root_private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, "root.key"), "password123") root_certificate = asymmetric.load_certificate(os.path.join(fixtures_dir, "root.crt")) builder = CertificateListBuilder("http://crl.example.com", root_certificate, 50000) revoked_at = datetime(2015, 8, 1, 12, 0, 0, tzinfo=timezone.utc) builder.add_certificate(29232181, revoked_at, "key_compromise") certificate_list = builder.build(root_private_key) der_bytes = certificate_list.dump() new_cert_list = crl.CertificateList.load(der_bytes) tbs_cert_list = new_cert_list["tbs_cert_list"] revoked_certificates = tbs_cert_list["revoked_certificates"] now = datetime.now(timezone.utc) self.assertEqual("v3", tbs_cert_list["version"].native) self.assertEqual("rsassa_pkcs1v15", tbs_cert_list["signature"].signature_algo) self.assertEqual("sha256", tbs_cert_list["signature"].hash_algo) self.assertEqual(root_certificate.asn1.subject, tbs_cert_list["issuer"]) self.assertEqual(root_certificate.asn1.subject.sha256, tbs_cert_list["issuer"].sha256) self.assertGreaterEqual(now, tbs_cert_list["this_update"].native) self.assertLess(now, tbs_cert_list["next_update"].native) self.assertEqual(set(["issuing_distribution_point"]), new_cert_list.critical_extensions) self.assertEqual(1, len(revoked_certificates)) revoked_cert = revoked_certificates[0] self.assertEqual(29232181, revoked_cert["user_certificate"].native) self.assertEqual(revoked_at, revoked_cert["revocation_date"].native) self.assertEqual(set(), revoked_cert.critical_extensions) self.assertEqual("key_compromise", revoked_cert.crl_reason_value.native) self.assertEqual(None, revoked_cert.invalidity_date_value) self.assertEqual(None, revoked_cert.certificate_issuer_value) self.assertEqual(None, revoked_cert.issuer_name) self.assertEqual(None, new_cert_list.issuer_alt_name_value) self.assertEqual(50000, new_cert_list.crl_number_value.native) self.assertEqual(None, new_cert_list.delta_crl_indicator_value) self.assertEqual("full_name", new_cert_list.issuing_distribution_point_value["distribution_point"].name) self.assertEqual( "uniform_resource_identifier", new_cert_list.issuing_distribution_point_value["distribution_point"].chosen[0].name, ) self.assertEqual( "http://crl.example.com", new_cert_list.issuing_distribution_point_value["distribution_point"].chosen[0].native, ) self.assertEqual(root_certificate.asn1.key_identifier, new_cert_list.authority_key_identifier) self.assertEqual(None, new_cert_list.freshest_crl_value) self.assertEqual(None, new_cert_list.authority_information_access_value)
def test_build_delta_crl(self): root_certificate = asymmetric.load_certificate(os.path.join(fixtures_dir, "root.crt")) crl_issuer_private_key = asymmetric.load_private_key( os.path.join(fixtures_dir, "crl_issuer.key"), "password123" ) crl_issuer_certificate = asymmetric.load_certificate(os.path.join(fixtures_dir, "crl_issuer.crt")) builder = CertificateListBuilder("http://crl.example.com/delta", crl_issuer_certificate, 50001) builder.certificate_issuer = root_certificate builder.issuer_certificate_url = "http://download.example.com/crl_issuer" builder.delta_of = 50000 certificate_list = builder.build(crl_issuer_private_key) der_bytes = certificate_list.dump() new_cert_list = crl.CertificateList.load(der_bytes) tbs_cert_list = new_cert_list["tbs_cert_list"] revoked_certificates = tbs_cert_list["revoked_certificates"] now = datetime.now(timezone.utc) self.assertEqual("v3", tbs_cert_list["version"].native) self.assertEqual("rsassa_pkcs1v15", tbs_cert_list["signature"].signature_algo) self.assertEqual("sha256", tbs_cert_list["signature"].hash_algo) self.assertEqual(crl_issuer_certificate.asn1.subject, tbs_cert_list["issuer"]) self.assertEqual(crl_issuer_certificate.asn1.subject.sha256, tbs_cert_list["issuer"].sha256) self.assertGreaterEqual(now, tbs_cert_list["this_update"].native) self.assertLess(now, tbs_cert_list["next_update"].native) self.assertEqual(set(["issuing_distribution_point", "delta_crl_indicator"]), new_cert_list.critical_extensions) self.assertEqual(0, len(revoked_certificates)) self.assertEqual(None, new_cert_list.issuer_alt_name_value) self.assertEqual(50001, new_cert_list.crl_number_value.native) self.assertEqual(50000, new_cert_list.delta_crl_indicator_value.native) self.assertEqual("full_name", new_cert_list.issuing_distribution_point_value["distribution_point"].name) self.assertEqual( "uniform_resource_identifier", new_cert_list.issuing_distribution_point_value["distribution_point"].chosen[0].name, ) self.assertEqual( "http://crl.example.com/delta", new_cert_list.issuing_distribution_point_value["distribution_point"].chosen[0].native, ) self.assertEqual(crl_issuer_certificate.asn1.key_identifier, new_cert_list.authority_key_identifier) self.assertEqual([], new_cert_list.delta_crl_distribution_points) self.assertEqual(["http://download.example.com/crl_issuer"], new_cert_list.issuer_cert_urls)
def test_build_revoked_no_reason(self): issuer_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test-inter.crt')) revoked_time = datetime(2015, 9, 1, 12, 0, 0, tzinfo=timezone.utc) builder = OCSPResponseBuilder('successful', subject_cert, 'revoked', revoked_time) ocsp_response = builder.build(issuer_key, issuer_cert) der_bytes = ocsp_response.dump() new_response = asn1crypto.ocsp.OCSPResponse.load(der_bytes) basic_response = new_response['response_bytes']['response'].parsed response_data = basic_response['tbs_response_data'] cert_response = response_data['responses'][0] self.assertEqual('revoked', cert_response['cert_status'].name) self.assertEqual(revoked_time, cert_response['cert_status'].chosen['revocation_time'].native) self.assertEqual('unspecified', cert_response['cert_status'].chosen['revocation_reason'].native)
def test_build_no_certificate(self): issuer_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'test.key')) issuer_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test.crt')) subject_cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test-inter.crt')) with self.assertRaisesRegexp(ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert, 'good') builder.certificate = None ocsp_response = builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp(ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert, 'good') builder.certificate_status = None ocsp_response = builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp(ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', subject_cert) ocsp_response = builder.build(issuer_key, issuer_cert) with self.assertRaisesRegexp(ValueError, 'must be set if the response_status is "successful"'): builder = OCSPResponseBuilder('successful', None, 'good') ocsp_response = builder.build(issuer_key, issuer_cert)
def __init__(self, issuer_cert: str, responder_cert: str, responder_key: str, validate_func: ValidateFunc, cert_retrieve_func: CertRetrieveFunc, next_update_days: int = 7): """ Create a new OCSPResponder instance. :param issuer_cert: Path to the issuer certificate. :param responder_cert: Path to the certificate of the OCSP responder with the `OCSP Signing` extension. :param responder_key: Path to the private key belonging to the responder cert. :param validate_func: A function that - given a certificate serial - will return the appropriate :class:`CertificateStatus` and - depending on the status - a revocation datetime. :param cert_retrieve_func: A function that - given a certificate serial - will return the corresponding certificate as a string. :param next_update_days: The ``nextUpdate`` value that will be written into the response. Default: 7 days. """ # Certs and keys self._issuer_cert = asymmetric.load_certificate(issuer_cert) self._responder_cert = asymmetric.load_certificate(responder_cert) self._responder_key = asymmetric.load_private_key(responder_key) # Functions self._validate = validate_func self._cert_retrieve = cert_retrieve_func # Next update self._next_update_days = next_update_days # Bottle self._app = Bottle() # Initialize routing self._route()
def test_dump_private_openssl(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) pem_serialized = asymmetric.dump_openssl_private_key(private, 'password123') private_reloaded = asymmetric.load_private_key(pem_serialized, 'password123') self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm)
def test_private_key_attributes(self): private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) self.assertEqual(2048, private_key.bit_size) self.assertEqual(256, private_key.byte_size) self.assertEqual('rsa', private_key.algorithm)
def test_private_key_ec_attributes(self): private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) self.assertEqual(256, private_key.bit_size) self.assertEqual(32, private_key.byte_size) self.assertEqual('secp256r1', private_key.curve) self.assertEqual('ec', private_key.algorithm)
# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os from oscrypto import asymmetric from certbuilder import CertificateBuilder fixtures_dir = os.path.join(os.path.dirname(__file__), '..', 'tests', 'fixtures') root_ca_private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'test.key')) root_ca_certificate = asymmetric.load_certificate(os.path.join(fixtures_dir, 'test.crt')) root_ocsp_public_key, root_ocsp_private_key = asymmetric.generate_pair('rsa', bit_size=2048) with open(os.path.join(fixtures_dir, 'test-ocsp.key'), 'wb') as f: f.write(asymmetric.dump_private_key(root_ocsp_private_key, 'password', target_ms=20)) builder = CertificateBuilder( { 'country_name': 'US', 'state_or_province_name': 'Massachusetts', 'locality_name': 'Newbury', 'organization_name': 'Codex Non Sufficit LC', 'organization_unit_name': 'Testing', 'common_name': 'CodexNS OCSP Responder', }, root_ocsp_public_key )
def build(self, issuer_private_key): """ Validates the certificate list information, constructs the ASN.1 structure and then signs it :param issuer_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key of the CRL issuer :return: An asn1crypto.crl.CertificateList object of the newly signed CRL """ is_oscrypto = isinstance(issuer_private_key, asymmetric.PrivateKey) if not isinstance(issuer_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' issuer_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(issuer_private_key) )) if self._this_update is None: self._this_update = datetime.now(timezone.utc) if self._next_update is None: self._next_update = self._this_update + timedelta(days=7) signature_algo = issuer_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) def _make_extension(name, value): return { 'extn_id': name, 'critical': self._determine_critical(name), 'extn_value': value } extensions = [] for name in sorted(self._special_extensions): value = getattr(self, '_%s' % name) if value is not None: extensions.append(_make_extension(name, value)) for name in sorted(self._other_extensions.keys()): extensions.append(_make_extension(name, self._other_extensions[name])) # For an indirect CRL we need to set the first if self._certificate_issuer and len(self._revoked_certificates) > 0: self._revoked_certificates[0]['crl_entry_extensions'].append({ 'extn_id': 'certificate_issuer', 'critical': True, 'extn_value': x509.GeneralNames([ x509.GeneralName( name='directory_name', value=self._certificate_issuer.subject ) ]) }) tbs_cert_list = crl.TbsCertList({ 'version': 'v3', 'signature': { 'algorithm': signature_algorithm_id }, 'issuer': self._issuer.subject, 'this_update': x509.Time(name='utc_time', value=self._this_update), 'next_update': x509.Time(name='utc_time', value=self._next_update), 'revoked_certificates': crl.RevokedCertificates(self._revoked_certificates), 'crl_extensions': extensions }) if issuer_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif issuer_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif issuer_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: issuer_private_key = asymmetric.load_private_key(issuer_private_key) signature = sign_func(issuer_private_key, tbs_cert_list.dump(), self._hash_algo) return crl.CertificateList({ 'tbs_cert_list': tbs_cert_list, 'signature_algorithm': { 'algorithm': signature_algorithm_id }, 'signature': signature })
def build(self, responder_private_key=None, responder_certificate=None): """ Validates the request information, constructs the ASN.1 structure and signs it. The responder_private_key and responder_certificate parameters are only required if the response_status is "successful". :param responder_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the response with :param responder_certificate: An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate object of the certificate associated with the private key :return: An asn1crypto.ocsp.OCSPResponse object of the response """ if self._response_status != 'successful': return ocsp.OCSPResponse({ 'response_status': self._response_status }) is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey) if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' responder_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(responder_private_key) )) cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate) if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto: raise TypeError(_pretty_message( ''' responder_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(responder_certificate) )) if cert_is_oscrypto: responder_certificate = responder_certificate.asn1 if self._certificate is None: raise ValueError(_pretty_message( ''' certificate must be set if the response_status is "successful" ''' )) if self._certificate_status is None: raise ValueError(_pretty_message( ''' certificate_status must be set if the response_status is "successful" ''' )) def _make_extension(name, value): return { 'extn_id': name, 'critical': False, 'extn_value': value } response_data_extensions = [] single_response_extensions = [] for name, value in self._response_data_extensions.items(): response_data_extensions.append(_make_extension(name, value)) if self._nonce: response_data_extensions.append( _make_extension('nonce', self._nonce) ) if not response_data_extensions: response_data_extensions = None for name, value in self._single_response_extensions.items(): single_response_extensions.append(_make_extension(name, value)) if self._certificate_issuer: single_response_extensions.append( _make_extension( 'certificate_issuer', [ x509.GeneralName( name='directory_name', value=self._certificate_issuer.subject ) ] ) ) if not single_response_extensions: single_response_extensions = None responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo) if self._certificate_status == 'good': cert_status = ocsp.CertStatus( name='good', value=core.Null() ) elif self._certificate_status == 'unknown': cert_status = ocsp.CertStatus( name='unknown', value=core.Null() ) else: status = self._certificate_status reason = status if status != 'revoked' else 'unspecified' cert_status = ocsp.CertStatus( name='revoked', value={ 'revocation_time': self._revocation_date, 'revocation_reason': reason, } ) issuer = self._certificate_issuer if self._certificate_issuer else responder_certificate if issuer.subject != self._certificate.issuer: raise ValueError(_pretty_message( ''' responder_certificate does not appear to be the issuer for the certificate. Perhaps set the .certificate_issuer attribute? ''' )) produced_at = datetime.now(timezone.utc) if self._this_update is None: self._this_update = produced_at if self._next_update is None: self._next_update = self._this_update + timedelta(days=7) response_data = ocsp.ResponseData({ 'responder_id': ocsp.ResponderId(name='by_key', value=responder_key_hash), 'produced_at': produced_at, 'responses': [ { 'cert_id': { 'hash_algorithm': { 'algorithm': self._key_hash_algo }, 'issuer_name_hash': getattr(self._certificate.issuer, self._key_hash_algo), 'issuer_key_hash': getattr(issuer.public_key, self._key_hash_algo), 'serial_number': self._certificate.serial_number, }, 'cert_status': cert_status, 'this_update': self._this_update, 'next_update': self._next_update, 'single_extensions': single_response_extensions } ], 'response_extensions': response_data_extensions }) signature_algo = responder_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) if responder_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif responder_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif responder_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: responder_private_key = asymmetric.load_private_key(responder_private_key) signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo) certs = None if self._certificate_issuer: certs = [responder_certificate] return ocsp.OCSPResponse({ 'response_status': self._response_status, 'response_bytes': { 'response_type': 'basic_ocsp_response', 'response': { 'tbs_response_data': response_data, 'signature_algorithm': {'algorithm': signature_algorithm_id}, 'signature': signature_bytes, 'certs': certs } } })
def build(self, signing_private_key): """ Validates the certificate information, constructs an X.509 certificate and then signs it :param signing_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the request with. This should be the private key that matches the public key. :return: An asn1crypto.csr.CertificationRequest object of the request """ is_oscrypto = isinstance(signing_private_key, asymmetric.PrivateKey) if not isinstance(signing_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' signing_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(signing_private_key) )) signature_algo = signing_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) def _make_extension(name, value): return { 'extn_id': name, 'critical': self._determine_critical(name), 'extn_value': value } extensions = [] for name in sorted(self._special_extensions): value = getattr(self, '_%s' % name) if value is not None: extensions.append(_make_extension(name, value)) for name in sorted(self._other_extensions.keys()): extensions.append(_make_extension(name, self._other_extensions[name])) attributes = [] if extensions: attributes.append({ 'type': 'extension_request', 'values': [extensions] }) certification_request_info = csr.CertificationRequestInfo({ 'version': 'v1', 'subject': self._subject, 'subject_pk_info': self._subject_public_key, 'attributes': attributes }) if signing_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif signing_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif signing_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: signing_private_key = asymmetric.load_private_key(signing_private_key) signature = sign_func(signing_private_key, certification_request_info.dump(), self._hash_algo) return csr.CertificationRequest({ 'certification_request_info': certification_request_info, 'signature_algorithm': { 'algorithm': signature_algorithm_id, }, 'signature': signature })
def build(self, signing_private_key): """ Validates the certificate information, constructs an X.509 certificate and then signs it :param signing_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the request with. This should be the private key that matches the public key. :return: An asn1crypto.csr.CertificationRequest object of the request """ is_oscrypto = isinstance(signing_private_key, asymmetric.PrivateKey) if not isinstance(signing_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError( _pretty_message( """ signing_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s """, _type_name(signing_private_key), ) ) signature_algo = signing_private_key.algorithm if signature_algo == "ec": signature_algo = "ecdsa" signature_algorithm_id = "%s_%s" % (self._hash_algo, signature_algo) def _make_extension(name, value): return {"extn_id": name, "critical": self._determine_critical(name), "extn_value": value} extensions = [] for name in sorted(self._special_extensions): value = getattr(self, "_%s" % name) if value is not None: extensions.append(_make_extension(name, value)) for name in sorted(self._other_extensions.keys()): extensions.append(_make_extension(name, self._other_extensions[name])) attributes = [] if extensions: attributes.append({"type": "extension_request", "values": [extensions]}) certification_request_info = csr.CertificationRequestInfo( { "version": "v1", "subject": self._subject, "subject_pk_info": self._subject_public_key, "attributes": attributes, } ) if signing_private_key.algorithm == "rsa": sign_func = asymmetric.rsa_pkcs1v15_sign elif signing_private_key.algorithm == "dsa": sign_func = asymmetric.dsa_sign elif signing_private_key.algorithm == "ec": sign_func = asymmetric.ecdsa_sign if not is_oscrypto: signing_private_key = asymmetric.load_private_key(signing_private_key) signature = sign_func(signing_private_key, certification_request_info.dump(), self._hash_algo) return csr.CertificationRequest( { "certification_request_info": certification_request_info, "signature_algorithm": {"algorithm": signature_algorithm_id}, "signature": signature, } )
def build(self, requestor_private_key=None, requestor_certificate=None, other_certificates=None): """ Validates the request information, constructs the ASN.1 structure and then optionally signs it. The requestor_private_key, requestor_certificate and other_certificates params are all optional and only necessary if the request needs to be signed. Signing a request is uncommon for OCSP requests related to web TLS connections. :param requestor_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the request with :param requestor_certificate: An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate object of the certificate associated with the private key :param other_certificates: A list of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate objects that may be useful for the OCSP server to verify the request signature. Intermediate certificates would be specified here. :return: An asn1crypto.ocsp.OCSPRequest object of the request """ def _make_extension(name, value): return { 'extn_id': name, 'critical': False, 'extn_value': value } tbs_request_extensions = [] request_extensions = [] has_nonce = False for name, value in self._tbs_request_extensions.items(): if name == 'nonce': has_nonce = True tbs_request_extensions.append(_make_extension(name, value)) if self._nonce and not has_nonce: tbs_request_extensions.append( _make_extension('nonce', util.rand_bytes(16)) ) if not tbs_request_extensions: tbs_request_extensions = None for name, value in self._request_extensions.items(): request_extensions.append(_make_extension(name, value)) if not request_extensions: request_extensions = None tbs_request = ocsp.TBSRequest({ 'request_list': [ { 'req_cert': { 'hash_algorithm': { 'algorithm': self._key_hash_algo }, 'issuer_name_hash': getattr(self._certificate.issuer, self._key_hash_algo), 'issuer_key_hash': getattr(self._issuer.public_key, self._key_hash_algo), 'serial_number': self._certificate.serial_number, }, 'single_request_extensions': request_extensions } ], 'request_extensions': tbs_request_extensions }) signature = None if requestor_private_key or requestor_certificate or other_certificates: is_oscrypto = isinstance(requestor_private_key, asymmetric.PrivateKey) if not isinstance(requestor_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' requestor_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(requestor_private_key) )) cert_is_oscrypto = isinstance(requestor_certificate, asymmetric.Certificate) if not isinstance(requestor_certificate, x509.Certificate) and not cert_is_oscrypto: raise TypeError(_pretty_message( ''' requestor_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(requestor_certificate) )) if other_certificates is not None and not isinstance(other_certificates, list): raise TypeError(_pretty_message( ''' other_certificates must be a list of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate objects, not %s ''', _type_name(other_certificates) )) if cert_is_oscrypto: requestor_certificate = requestor_certificate.asn1 tbs_request['requestor_name'] = x509.GeneralName( name='directory_name', value=requestor_certificate.subject ) certificates = [requestor_certificate] for other_certificate in other_certificates: other_cert_is_oscrypto = isinstance(other_certificate, asymmetric.Certificate) if not isinstance(other_certificate, x509.Certificate) and not other_cert_is_oscrypto: raise TypeError(_pretty_message( ''' other_certificate must be an instance of asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate, not %s ''', _type_name(other_certificate) )) if other_cert_is_oscrypto: other_certificate = other_certificate.asn1 certificates.append(other_certificate) signature_algo = requestor_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) if requestor_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif requestor_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif requestor_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: requestor_private_key = asymmetric.load_private_key(requestor_private_key) signature_bytes = sign_func(requestor_private_key, tbs_request.dump(), self._hash_algo) signature = ocsp.Signature({ 'signature_algorithm': {'algorithm': signature_algorithm_id}, 'signature': signature_bytes, 'certs': certificates }) return ocsp.OCSPRequest({ 'tbs_request': tbs_request, 'optional_signature': signature })
def load_private(name): return asymmetric.load_private_key(path.join( certs_dir, '{}.key'.format(name) ))
def build(self, signing_private_key): """ Validates the certificate information, constructs the ASN.1 structure and then signs it :param signing_private_key: An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey object for the private key to sign the certificate with. If the key is self-signed, this should be the private key that matches the public key, otherwise it needs to be the issuer's private key. :return: An asn1crypto.x509.Certificate object of the newly signed certificate """ is_oscrypto = isinstance(signing_private_key, asymmetric.PrivateKey) if not isinstance(signing_private_key, keys.PrivateKeyInfo) and not is_oscrypto: raise TypeError(_pretty_message( ''' signing_private_key must be an instance of asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey, not %s ''', _type_name(signing_private_key) )) if self._self_signed is not True and self._issuer is None: raise ValueError(_pretty_message( ''' Certificate must be self-signed, or an issuer must be specified ''' )) if self._self_signed: self._issuer = self._subject if self._serial_number is None: time_part = int_to_bytes(int(time.time())) random_part = util.rand_bytes(4) self._serial_number = int_from_bytes(time_part + random_part) if self._begin_date is None: self._begin_date = datetime.now(timezone.utc) if self._end_date is None: self._end_date = self._begin_date + timedelta(365) if not self.ca: for ca_only_extension in set(['policy_mappings', 'policy_constraints', 'inhibit_any_policy']): if ca_only_extension in self._other_extensions: raise ValueError(_pretty_message( ''' Extension %s is only valid for CA certificates ''', ca_only_extension )) signature_algo = signing_private_key.algorithm if signature_algo == 'ec': signature_algo = 'ecdsa' signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) # RFC 3280 4.1.2.5 def _make_validity_time(dt): if dt < datetime(2050, 1, 1, tzinfo=timezone.utc): value = x509.Time(name='utc_time', value=dt) else: value = x509.Time(name='general_time', value=dt) return value def _make_extension(name, value): return { 'extn_id': name, 'critical': self._determine_critical(name), 'extn_value': value } extensions = [] for name in sorted(self._special_extensions): value = getattr(self, '_%s' % name) if name == 'ocsp_no_check': value = core.Null() if value else None if value is not None: extensions.append(_make_extension(name, value)) for name in sorted(self._other_extensions.keys()): extensions.append(_make_extension(name, self._other_extensions[name])) tbs_cert = x509.TbsCertificate({ 'version': 'v3', 'serial_number': self._serial_number, 'signature': { 'algorithm': signature_algorithm_id }, 'issuer': self._issuer, 'validity': { 'not_before': _make_validity_time(self._begin_date), 'not_after': _make_validity_time(self._end_date), }, 'subject': self._subject, 'subject_public_key_info': self._subject_public_key, 'extensions': extensions }) if signing_private_key.algorithm == 'rsa': sign_func = asymmetric.rsa_pkcs1v15_sign elif signing_private_key.algorithm == 'dsa': sign_func = asymmetric.dsa_sign elif signing_private_key.algorithm == 'ec': sign_func = asymmetric.ecdsa_sign if not is_oscrypto: signing_private_key = asymmetric.load_private_key(signing_private_key) signature = sign_func(signing_private_key, tbs_cert.dump(), self._hash_algo) return x509.Certificate({ 'tbs_certificate': tbs_cert, 'signature_algorithm': { 'algorithm': signature_algorithm_id }, 'signature_value': signature })
def get_ocsp_response(self, data): try: ocsp_request = asn1crypto.ocsp.OCSPRequest.load(data) tbs_request = ocsp_request['tbs_request'] request_list = tbs_request['request_list'] if len(request_list) != 1: log.error('Received OCSP request with multiple sub requests') raise NotImplemented('Combined requests not yet supported') single_request = request_list[0] # TODO: Support more than one request req_cert = single_request['req_cert'] serial = serial_from_int(req_cert['serial_number'].native) except Exception as e: log.exception('Error parsing OCSP request: %s', e) return self.fail('malformed_request') # Get CA and certificate ca = CertificateAuthority.objects.get(serial=self.ca) try: cert = Certificate.objects.filter(ca=ca).get(serial=serial) except Certificate.DoesNotExist: log.warn('OCSP request for unknown cert received.') return self.fail('internal_error') # load ca cert and responder key/cert ca_cert = load_certificate(force_bytes(ca.pub)) responder_key = load_private_key(self.responder_key) responder_cert = load_certificate(self.responder_cert) builder = OCSPResponseBuilder( response_status='successful', # ResponseStatus.successful.value, certificate=load_certificate(force_bytes(cert.pub)), certificate_status=cert.ocsp_status, revocation_date=cert.revoked_date, ) # Parse extensions for extension in tbs_request['request_extensions']: extn_id = extension['extn_id'].native critical = extension['critical'].native value = extension['extn_value'].parsed # This variable tracks whether any unknown extensions were encountered unknown = False # Handle nonce extension if extn_id == 'nonce': builder.nonce = value.native # That's all we know else: # pragma: no cover unknown = True # If an unknown critical extension is encountered (which should not # usually happen, according to RFC 6960 4.1.2), we should throw our # hands up in despair and run. if unknown is True and critical is True: # pragma: no cover log.warning('Could not parse unknown critical extension: %r', dict(extension.native)) return self._fail('internal_error') # If it's an unknown non-critical extension, we can safely ignore it. elif unknown is True: # pragma: no cover log.info('Ignored unknown non-critical extension: %r', dict(extension.native)) builder.certificate_issuer = ca_cert builder.next_update = datetime.utcnow() + timedelta(seconds=self.expires) return builder.build(responder_key, responder_cert)
def as_view(cls, **kwargs): kwargs['responder_key'] = load_private_key(kwargs['responder_key']) return super(OCSPView, cls).as_view(**kwargs)