Changeset - 7a2919409da2
[Not reviewed]
0 9 0
Branko Majic (branko) - 6 years ago 2018-03-04 14:08:39
branko@majic.rs
GC-15: Implemented functionality for issuing a server certificate:

- Added functional test covering the user scenario.
- Updated CLI implementation to show user message about issued server
artifacts.
- Implemented functionality in the server command.
- Fixed a small typo in docstring for issue_certificate function.
- Implemented high-level crypto function for issuing server
certificates.
- Implemented additional storage functions for reading an entire CA
hierarchy, individual private keys, and individual certificates.
- Implemented the necessary unit tests covering newly implemented
code.
9 files changed with 429 insertions and 3 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_server.py
Show inline comments
 
@@ -59,3 +59,79 @@ def test_server_command_requires_initialised_hierarchy(tmpdir):
 
    assert stdout == ""
 
    assert stderr == "CA hierarchy must be initialised prior to issuing server certificates. Run the gimmecert init command first.\n"
 
    assert exit_code != 0
 

	
 

	
 
def test_server_command_issues_server_certificate(tmpdir):
 
    # John is about to issue a server certificate. He switches to his
 
    # project directory, and initialises the CA hierarchy there.
 
    tmpdir.chdir()
 
    run_command("gimmecert", "init")
 

	
 
    # He then runs command for issuing a server certificate.
 
    stdout, stderr, exit_code = run_command('gimmecert', 'server', 'myserver')
 

	
 
    # John notices that the command has run without an error, and that
 
    # it has printed out path to the private key and certificate.
 
    assert stderr == ""
 
    assert exit_code == 0
 
    assert "Server certificate issued." in stdout
 
    assert ".gimmecert/server/myserver.key.pem" in stdout
 
    assert ".gimmecert/server/myserver.cert.pem" in stdout
 

	
 
    # John has a look at the generated private key using the OpenSSL
 
    # CLI.
 
    stdout, stderr, exit_code = run_command('openssl', 'rsa', '-noout', '-text', '-in', '.gimmecert/server/myserver.key.pem')
 

	
 
    # No errors are reported, and John is able to see some details
 
    # about the generated key.
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert "Private-Key: (2048 bit)" in stdout
 

	
 
    # John then has a look at the generated certificate file.
 
    stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-text', '-in', '.gimmecert/server/myserver.cert.pem')
 

	
 
    # Once again, there are no errors, and he can see some details
 
    # about the certificate.
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert 'Certificate:' in stdout
 

	
 
    # He notices that the certificate includes the provided entity
 
    # name as DNS subject alternative name.
 
    assert "DNS:myserver\n" in stdout
 

	
 
    # John has a quick look at issuer and subject DN stored in
 
    # certificate.
 
    issuer_dn, _, _ = run_command('openssl', 'x509', '-noout', '-issuer', '-in', '.gimmecert/server/myserver.cert.pem')
 
    subject_dn, _, _ = run_command('openssl', 'x509', '-noout', '-subject', '-in', '.gimmecert/server/myserver.cert.pem')
 
    issuer_dn = issuer_dn.replace('issuer=', '', 1).rstrip().replace(' /CN=', 'CN = ', 1)  # OpenSSL 1.0 vs 1.1 formatting
 
    subject_dn = subject_dn.replace('subject=', '', 1).rstrip().replace(' /CN=', 'CN = ', 1)  # OpenSSL 1.0 vs 1.1 formatting
 

	
 
    # He notices the issuer DN is as expected based on the directory
 
    # name, and that server certificate subject DN simply has CN field
 
    # with the name he provided earlier.
 
    assert issuer_dn == "CN = %s Level 1 CA" % tmpdir.basename
 
    assert subject_dn == "CN = myserver"
 

	
 
    # John takes a look at certificate purpose, since he wants to
 
    # ensure it is a proper TLS server certificate.
 
    stdout, stderr, exit_code = run_command('openssl', 'x509', '-noout', '-purpose', '-in', '.gimmecert/server/myserver.cert.pem')
 

	
 
    # He verifies that the provided certificate has correct purpose.
 
    assert "SSL server : Yes" in stdout
 
    assert "SSL server CA : No" in stdout
 
    assert "SSL client : No" in stdout
 
    assert "SSL client CA : No" in stdout
 

	
 
    # Finally, he decides to check if the certificate can be verified
 
    # using the CA certificate chain.
 
    _, _, error_code = run_command(
 
        "openssl", "verify",
 
        "-CAfile",
 
        ".gimmecert/ca/chain-full.cert.pem",
 
        ".gimmecert/server/myserver.cert.pem"
 
    )
 

	
 
    # He is happy to see that verification succeeds.
 
    assert error_code == 0
gimmecert/cli.py
Show inline comments
 
@@ -92,6 +92,8 @@ def setup_server_subcommand_parser(parser, subparsers):
 
        if status is False:
 
            print(message, file=sys.stderr)
 
            exit(ERROR_GENERIC)
 
        else:
 
            print(message)
 

	
 
    subparser.set_defaults(func=server_wrapper)
 

	
gimmecert/commands.py
Show inline comments
 
@@ -85,7 +85,23 @@ def server(project_directory, entity_name):
 
    :rtype: (bool, str)
 
    """
 

	
 
    private_key_path = os.path.join('.gimmecert', 'server', '%s.key.pem' % entity_name)
 
    certificate_path = os.path.join('.gimmecert', 'server', '%s.cert.pem' % entity_name)
 

	
 
    if not gimmecert.storage.is_initialised(project_directory):
 
        return False, "CA hierarchy must be initialised prior to issuing server certificates. Run the gimmecert init command first."
 

	
 
    return True, ""
 
    message = """Server certificate issued.\n
 
    Server private key: .gimmecert/server/%s.key.pem
 
    Server certificate: .gimmecert/server/%s.cert.pem
 
""" % (entity_name, entity_name)
 

	
 
    ca_hierarchy = gimmecert.storage.read_ca_hierarchy(os.path.join(project_directory, '.gimmecert', 'ca'))
 
    issuer_private_key, issuer_certificate = ca_hierarchy[-1]
 
    private_key = gimmecert.crypto.generate_private_key()
 
    certificate = gimmecert.crypto.issue_server_certificate(entity_name, private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    gimmecert.storage.write_private_key(private_key, private_key_path)
 
    gimmecert.storage.write_certificate(certificate, certificate_path)
 

	
 
    return True, message
gimmecert/crypto.py
Show inline comments
 
@@ -98,7 +98,7 @@ def issue_certificate(issuer_dn, subject_dn, signing_key, public_key, not_before
 
    :type signing_key: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey
 

	
 
    :param public_key: Public key belonging to entity associated with passed-in subject_dn. Used as part of certificate to denote its owner.
 
    :type cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey:
 
    :type public_key: cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey
 

	
 
    :param not_before: Beginning of certifiate validity.
 
    :type datetime.datetime.:
 
@@ -176,3 +176,55 @@ def generate_ca_hierarchy(base_name, depth):
 
        issuer_dn, issuer_private_key = dn, private_key
 

	
 
    return hierarchy
 

	
 

	
 
def issue_server_certificate(name, public_key, issuer_private_key, issuer_certificate):
 
    """
 
    Issues a server certificate. The resulting certificate will use
 
    the passed-in name for subject DN, as well as DNS subject
 
    alternative name.
 

	
 
    The server certificate key usages and extended key usages are set
 
    to comply with requirements for using such certificates as TLS
 
    server certificates.
 

	
 
    :param name: Name of the server end entity. Name will be part of subject DN CN field.
 
    :type name: str
 

	
 
    :param public_key: Public key of the server end entity.
 
    :type public_key: cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey
 

	
 
    :param issuer_private_key: Private key of the issuer to use for signing the server certificate structure.
 
    :type issuer_private_key: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey
 

	
 
    :param issuer_certificate: Certificate of certificate issuer. Naming and validity constraints will be applied based on its content.
 
    :type issuer_certificate: cryptography.x509.Certificate
 

	
 
    :returns: Server certificate issued by designated issuer.
 
    :rtype: cryptography.x509.Certificate
 
    """
 

	
 
    dn = get_dn(name)
 
    not_before, not_after = get_validity_range()
 
    extensions = [
 
        (cryptography.x509.BasicConstraints(ca=False, path_length=None), True),
 
        (
 
            cryptography.x509.KeyUsage(
 
                digital_signature=True,
 
                key_encipherment=True,
 
                content_commitment=False,
 
                data_encipherment=False,
 
                key_agreement=False,
 
                key_cert_sign=False,
 
                crl_sign=False,
 
                encipher_only=False,
 
                decipher_only=False
 
            ), True
 
        ),
 
        (cryptography.x509.ExtendedKeyUsage([cryptography.x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]), True),
 
        (cryptography.x509.SubjectAlternativeName([cryptography.x509.DNSName('myserver')]), False)
 
    ]
 

	
 
    certificate = issue_certificate(issuer_certificate.issuer, dn, issuer_private_key, public_key, not_before, not_after, extensions)
 

	
 
    return certificate
gimmecert/storage.py
Show inline comments
 
@@ -43,6 +43,7 @@ def initialise_storage(project_directory):
 

	
 
    os.mkdir(os.path.join(project_directory, '.gimmecert'))
 
    os.mkdir(os.path.join(project_directory, '.gimmecert', 'ca'))
 
    os.mkdir(os.path.join(project_directory, '.gimmecert', 'server'))
 

	
 

	
 
def write_private_key(private_key, path):
 
@@ -121,3 +122,73 @@ def is_initialised(project_directory):
 
        return True
 

	
 
    return False
 

	
 

	
 
def read_ca_hierarchy(ca_directory):
 
    """
 
    Reads an entirye CA hierarchy from the directory, and returns the
 
    CA private key/certificate pairs in hierarchy order.
 

	
 
    Only private key and certificate files that conform to naming
 
    pattern 'levelN.key.pem' and 'levelN.cert.pem' will be read.
 

	
 
    :param ca_directory: Path to directory containing the CA artifacts (private keys and certificates).
 
    :type ca_directory: str
 

	
 
    :returns: List of private key/certificate pairs, starting with the level 1 CA and moving down the chain to leaf CA.
 
    :rtype: list[(cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey, cryptography.x509.Certificate)]
 
    """
 

	
 
    ca_hierarchy = []
 

	
 
    level = 1
 
    while os.path.exists(os.path.join(ca_directory, "level%d.key.pem" % level)) and os.path.exists(os.path.join(ca_directory, "level%d.cert.pem" % level)):
 
        private_key = read_private_key(os.path.join(ca_directory, 'level%d.key.pem' % level))
 
        certificate = read_certificate(os.path.join(ca_directory, 'level%d.cert.pem' % level))
 
        ca_hierarchy.append((private_key, certificate))
 
        level = level + 1
 

	
 
    return ca_hierarchy
 

	
 

	
 
def read_private_key(private_key_path):
 
    """
 
    Reads RSA private key from the designated path. The key should be
 
    provided in OpenSSL-style PEM format, unencrypted.
 

	
 
    :param private_key_path: Path to private key to read.
 
    :type private_key_path: str
 

	
 
    :returns: Private key object read from the specified file.
 
    :rtype: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey
 
    """
 

	
 
    with open(private_key_path, 'rb') as private_key_file:
 
        private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key(
 
            private_key_file.read(),
 
            None,  # no password
 
            cryptography.hazmat.backends.default_backend()
 
        )
 

	
 
    return private_key
 

	
 

	
 
def read_certificate(certificate_path):
 
    """
 
    Reads X.509 certificate from the designated file path. The
 
    certificate is expected to be provided in OpenSSL-style PEM
 
    format.
 

	
 
    :param certificate_path: Path to certificate file.
 
    :type certificate_path: str
 

	
 
    :returns: Certificate object read from the specified file.
 
    :rtype: cryptography.x509.Certificate
 
    """
 
    with open(certificate_path, 'rb') as certificate_file:
 
        certificate = cryptography.x509.load_pem_x509_certificate(
 
            certificate_file.read(),
 
            cryptography.hazmat.backends.default_backend()
 
        )
 

	
 
    return certificate
tests/test_cli.py
Show inline comments
 
@@ -277,3 +277,13 @@ def test_server_command_invoked_with_correct_parameters(mock_server, tmpdir):
 
    gimmecert.cli.main()
 

	
 
    mock_server.assert_called_once_with(tmpdir.strpath, 'myserver')
 

	
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'server', 'myserver'])
 
def test_server_command_exists_with_error_if_hierarchy_not_initialised(tmpdir):
 
    tmpdir.chdir()
 

	
 
    with pytest.raises(SystemExit) as e_info:
 
        gimmecert.cli.main()
 

	
 
    assert e_info.value.code != 0
tests/test_commands.py
Show inline comments
 
@@ -25,7 +25,8 @@ import gimmecert.commands
 

	
 
def test_init_sets_up_directory_structure(tmpdir):
 
    base_dir = tmpdir.join('.gimmecert')
 
    ca_dir = tmpdir.join('.gimmecert')
 
    ca_dir = tmpdir.join('.gimmecert', 'ca')
 
    server_dir = tmpdir.join('.gimmecert', 'server')
 
    depth = 1
 

	
 
    tmpdir.chdir()
 
@@ -34,6 +35,7 @@ def test_init_sets_up_directory_structure(tmpdir):
 

	
 
    assert os.path.exists(base_dir.strpath)
 
    assert os.path.exists(ca_dir.strpath)
 
    assert os.path.exists(server_dir.strpath)
 

	
 

	
 
def test_init_generates_single_ca_artifact_for_depth_1(tmpdir):
 
@@ -153,3 +155,50 @@ def test_server_reports_error_if_directory_is_not_initialised(tmpdir):
 

	
 
    assert status is False
 
    assert "must be initialised" in message
 

	
 

	
 
def test_server_reports_paths_to_generated_artifacts(tmpdir):
 
    depth = 1
 

	
 
    tmpdir.chdir()
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    status, message = gimmecert.commands.server(tmpdir.strpath, 'myserver')
 

	
 
    assert status is True
 
    assert ".gimmecert/server/myserver.key.pem" in message
 
    assert ".gimmecert/server/myserver.cert.pem" in message
 

	
 

	
 
def test_server_outputs_private_key_to_file(tmpdir):
 
    depth = 1
 
    private_key_file = tmpdir.join('.gimmecert', 'server', 'myserver.key.pem')
 

	
 
    tmpdir.chdir()
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.server(tmpdir.strpath, 'myserver')
 

	
 
    assert private_key_file.check(file=1)
 

	
 
    private_key_file_content = private_key_file.read()
 

	
 
    assert private_key_file_content.startswith('-----BEGIN RSA PRIVATE KEY')
 
    assert private_key_file_content.endswith('END RSA PRIVATE KEY-----\n')
 

	
 

	
 
def test_server_outputs_certificate_to_file(tmpdir):
 
    depth = 1
 
    certificate_file = tmpdir.join('.gimmecert', 'server', 'myserver.cert.pem')
 

	
 
    tmpdir.chdir()
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.server(tmpdir.strpath, 'myserver')
 

	
 
    assert certificate_file.check(file=1)
 

	
 
    certificate_file_content = certificate_file.read()
 

	
 
    assert certificate_file_content.startswith('-----BEGIN CERTIFICATE')
 
    assert certificate_file_content.endswith('END CERTIFICATE-----\n')
tests/test_crypto.py
Show inline comments
 
@@ -258,3 +258,82 @@ def test_generate_ca_hierarchy_produces_certificates_with_ca_basic_constraints()
 
        assert critical is True
 
        assert value.ca is True
 
        assert value.path_length is None
 

	
 

	
 
def test_issue_server_certificate_returns_certificate():
 
    ca_hierarchy = gimmecert.crypto.generate_ca_hierarchy('My Project', 1)
 
    issuer_private_key, issuer_certificate = ca_hierarchy[0]
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 

	
 
    certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    assert isinstance(certificate, cryptography.x509.Certificate)
 

	
 

	
 
def test_issue_server_certificate_sets_correct_extensions():
 
    ca_hierarchy = gimmecert.crypto.generate_ca_hierarchy('My Project', 1)
 
    issuer_private_key, issuer_certificate = ca_hierarchy[0]
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 

	
 
    expected_basic_constraints = cryptography.x509.BasicConstraints(ca=False, path_length=None)
 
    expected_key_usage = cryptography.x509.KeyUsage(
 
        digital_signature=True,
 
        key_encipherment=True,
 
        content_commitment=False,
 
        data_encipherment=False,
 
        key_agreement=False,
 
        key_cert_sign=False,
 
        crl_sign=False,
 
        encipher_only=False,
 
        decipher_only=False
 
    )
 
    expected_extended_key_usage = cryptography.x509.ExtendedKeyUsage(
 
        [
 
            cryptography.x509.oid.ExtendedKeyUsageOID.SERVER_AUTH
 
        ]
 
    )
 
    expected_subject_alternative_name = cryptography.x509.SubjectAlternativeName(
 
        [
 
            cryptography.x509.DNSName('myserver')
 
        ]
 
    )
 

	
 
    certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    assert len(certificate.extensions) == 4
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.BasicConstraints).critical is True
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.BasicConstraints).value == expected_basic_constraints
 

	
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.KeyUsage).critical is True
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.KeyUsage).value == expected_key_usage
 

	
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.ExtendedKeyUsage).critical is True
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.ExtendedKeyUsage).value == expected_extended_key_usage
 

	
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.SubjectAlternativeName).critical is False
 
    assert certificate.extensions.get_extension_for_class(cryptography.x509.SubjectAlternativeName).value == expected_subject_alternative_name
 

	
 

	
 
def test_issue_server_certificate_has_correct_issuer_and_subject():
 
    ca_hierarchy = gimmecert.crypto.generate_ca_hierarchy('My Project', 1)
 
    issuer_private_key, issuer_certificate = ca_hierarchy[0]
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 

	
 
    certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    assert certificate.issuer == issuer_certificate.subject
 
    assert certificate.subject == gimmecert.crypto.get_dn('myserver')
 

	
 

	
 
def test_issue_server_certificate_has_correct_public_key():
 
    ca_hierarchy = gimmecert.crypto.generate_ca_hierarchy('My Project', 1)
 
    issuer_private_key, issuer_certificate = ca_hierarchy[0]
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 

	
 
    certificate = gimmecert.crypto.issue_server_certificate('myserver', private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    assert certificate.public_key().public_numbers() == private_key.public_key().public_numbers()
tests/test_storage.py
Show inline comments
 
@@ -20,6 +20,9 @@
 

	
 
import os
 

	
 
import cryptography
 

	
 
import gimmecert.commands
 
import gimmecert.crypto
 
import gimmecert.storage
 
import gimmecert.utils
 
@@ -32,6 +35,7 @@ def test_initialise_storage(tmpdir):
 

	
 
    assert os.path.exists(tmpdir.join('.gimmecert').strpath)
 
    assert os.path.exists(tmpdir.join('.gimmecert', 'ca').strpath)
 
    assert os.path.exists(tmpdir.join('.gimmecert', 'server').strpath)
 

	
 

	
 
def test_write_private_key(tmpdir):
 
@@ -96,3 +100,70 @@ def test_is_initialised_returns_false_if_directory_is_not_initialised(tmpdir):
 
    tmpdir.chdir()
 

	
 
    assert gimmecert.storage.is_initialised(tmpdir.strpath) is False
 

	
 

	
 
def test_read_ca_hierarchy_returns_list_of_ca_private_key_and_certificate_pairs_for_single_ca(tmpdir):
 
    tmpdir.chdir()
 
    gimmecert.commands.init(tmpdir.strpath, 'My Project', 1)
 

	
 
    ca_hierarchy = gimmecert.storage.read_ca_hierarchy(tmpdir.join('.gimmecert', 'ca').strpath)
 

	
 
    assert len(ca_hierarchy) == 1
 

	
 
    private_key, certificate = ca_hierarchy[0]
 

	
 
    assert isinstance(private_key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert isinstance(certificate, cryptography.x509.Certificate)
 

	
 

	
 
def test_read_private_key_returns_private_key(tmpdir):
 
    private_key_path = tmpdir.join('private.key.pem').strpath
 
    private_key = gimmecert.crypto.generate_private_key()
 
    gimmecert.storage.write_private_key(private_key, private_key_path)
 

	
 
    my_private_key = gimmecert.storage.read_private_key(private_key_path)
 

	
 
    assert isinstance(my_private_key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert my_private_key.public_key().public_numbers() == private_key.public_key().public_numbers()  # Can't compare private keys directly.
 

	
 

	
 
def test_read_certificate_returns_certificate(tmpdir):
 
    certificate_path = tmpdir.join('certificate.cert.pem').strpath
 
    dn = gimmecert.crypto.get_dn('mycertificate')
 
    not_before, not_after = gimmecert.crypto.get_validity_range()
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    certificate = gimmecert.crypto.issue_certificate(dn, dn, private_key, private_key.public_key(), not_before, not_after)
 
    gimmecert.storage.write_certificate(certificate, certificate_path)
 

	
 
    my_certificate = gimmecert.storage.read_certificate(certificate_path)
 

	
 
    assert isinstance(my_certificate, cryptography.x509.Certificate)
 
    assert my_certificate == certificate
 

	
 

	
 
def test_read_ca_hierarchy_returns_list_of_ca_private_key_and_certificate_pairs_in_hierarchy_order_for_multiple_cas(tmpdir):
 
    tmpdir.chdir()
 
    gimmecert.commands.init(tmpdir.strpath, 'My Project', 4)
 

	
 
    ca_hierarchy = gimmecert.storage.read_ca_hierarchy(tmpdir.join('.gimmecert', 'ca').strpath)
 

	
 
    assert len(ca_hierarchy) == 4
 

	
 
    private_key_1, certificate_1 = ca_hierarchy[0]
 
    private_key_2, certificate_2 = ca_hierarchy[1]
 
    private_key_3, certificate_3 = ca_hierarchy[2]
 
    private_key_4, certificate_4 = ca_hierarchy[3]
 

	
 
    assert isinstance(private_key_1, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert isinstance(certificate_1, cryptography.x509.Certificate)
 
    assert certificate_1.subject == gimmecert.crypto.get_dn("My Project Level 1 CA")
 
    assert isinstance(private_key_2, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert isinstance(certificate_2, cryptography.x509.Certificate)
 
    assert certificate_2.subject == gimmecert.crypto.get_dn("My Project Level 2 CA")
 
    assert isinstance(private_key_3, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert isinstance(certificate_3, cryptography.x509.Certificate)
 
    assert certificate_3.subject == gimmecert.crypto.get_dn("My Project Level 3 CA")
 
    assert isinstance(private_key_4, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey)
 
    assert isinstance(certificate_4, cryptography.x509.Certificate)
 
    assert certificate_4.subject == gimmecert.crypto.get_dn("My Project Level 4 CA")
0 comments (0 inline, 0 general)