Changeset - 647208f5016b
[Not reviewed]
0 9 0
Branko Majic (branko) - 6 years ago 2018-04-11 21:56:44
branko@majic.rs
GC-22: Implemented issuance of client certificates using passed-in CSR:

- Added functional test covering the issuance of client certificate
using CSR.
- Updated default value for the CSR client command option to be
explicitly None.
- Updated the client command to take in the CSR as an argument and
process it accordingly.
- Added helpers for generating CSR, as well as writing and reading
them to/from file.
- Updated existing tests to use new signature for the client
command.
- Added new unit tests for implemented functionality.
9 files changed with 329 insertions and 29 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_csr.py
Show inline comments
 
@@ -53,3 +53,47 @@ def test_commands_report_csr_option_as_available():
 
    # John notcies the option for passing-in a CSR.
 
    assert " --csr " in stdout
 
    assert " -c " in stdout
 

	
 

	
 
def test_client_certificate_issuance_by_passing_csr_as_file(tmpdir):
 
    # John is working on a project where he has already generated
 
    # client private key.
 
    tmpdir.chdir()
 
    run_command("openssl", "genrsa", "-out", "myclient1.key.pem", "2048")
 

	
 
    # However, he still needs to have a CA as a trustpoint, so he goes
 
    # ahead and initialises Gimmecert for this purpose.
 
    run_command("gimmecert", "init")
 

	
 
    # Before issuing the certificate, he goes ahead and generates a
 
    # CSR for the client private key
 
    run_command("openssl", "req", "-new", "-key", "myclient1.key.pem", "-subj", "/CN=myclient1", "-out", "myclient1.csr.pem")
 

	
 
    # John issues client certificate using CSR.
 
    stdout, stderr, exit_code = run_command("gimmecert", "client", "--csr", "myclient1.csr.pem", "myclient1")
 

	
 
    # The operation is successful, and he is presented with
 
    # information about generated artefacts.
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert ".gimmecert/client/myclient1.cert.pem" in stdout
 
    assert ".gimmecert/client/myclient1.csr.pem" in stdout
 

	
 
    # John also notices that there is no mention of a private key.
 
    assert ".gimmecert/client/myclient1.key.pem" not in stdout
 

	
 
    # John notices that the content of stored CSR is identical to the
 
    # one he provided.
 
    with open("myclient1.csr.pem", "r") as original_csr_file, open(".gimmecert/client/myclient1.csr.pem", "r") as stored_csr_file:
 
        original_csr = original_csr_file.read()
 
        stored_csr = stored_csr_file.read()
 

	
 
        assert original_csr == stored_csr
 

	
 
    # John then quickly has a look at the public key associated with
 
    # the private key, and public key stored in certificate.
 
    public_key, _, _ = run_command("openssl", "rsa", "-pubout", "-in", "myclient1.key.pem")
 
    certificate_public_key, _, _ = run_command("openssl", "x509", "-pubkey", "-noout", "-in", ".gimmecert/client/myclient1.cert.pem")
 

	
 
    # To his delight, they are identical.
 
    assert certificate_public_key == public_key
gimmecert/cli.py
Show inline comments
 
@@ -125,13 +125,13 @@ def setup_server_subcommand_parser(parser, subparsers):
 
def setup_client_subcommand_parser(parser, subparsers):
 
    subparser = subparsers.add_parser('client', description='Issue client certificate.')
 
    subparser.add_argument('entity_name', help='Name of the client entity.')
 
    subparser.add_argument('--csr', '-c', type=str, help='''Do not generate client private key locally, and use the passed-in \
 
    subparser.add_argument('--csr', '-c', type=str, default=None, help='''Do not generate client private key locally, and use the passed-in \
 
    certificate signing request (CSR) instead.''')
 

	
 
    def client_wrapper(args):
 
        project_directory = os.getcwd()
 

	
 
        return client(sys.stdout, sys.stderr, project_directory, args.entity_name)
 
        return client(sys.stdout, sys.stderr, project_directory, args.entity_name, args.csr)
 

	
 
    subparser.set_defaults(func=client_wrapper)
 

	
gimmecert/commands.py
Show inline comments
 
@@ -209,10 +209,17 @@ def usage(stdout, stderr, parser):
 
    return ExitCode.SUCCESS
 

	
 

	
 
def client(stdout, stderr, project_directory, entity_name):
 
def client(stdout, stderr, project_directory, entity_name, custom_csr_path):
 
    """
 
    Generates a client private key and issues a client certificate
 
    using the CA hierarchy initialised within the specified directory.
 
    Issues a client certificate using the CA hierarchy initialised
 
    within the specified directory.
 

	
 
    If custom CSR path is not passed-in, a private key will be
 
    generated and stored.
 

	
 
    If custom CSR is passed-in, no private key will be generated, and
 
    the CSR will be stored instead. Only the public key will be used
 
    from the CSR - no naming information is taken from it.
 

	
 
    :param stdout: Output stream where the informative messages should be written-out.
 
    :type stdout: io.IOBase
 
@@ -226,32 +233,61 @@ def client(stdout, stderr, project_directory, entity_name):
 
    :param entity_name: Name of the client entity. Name will be used in subject DN.
 
    :type entity_name: str
 

	
 
    :param custom_csr_path: Path to custom certificate signing request to use for issuing client certificate. Set to None or "" to generate private key.
 
    :type custom_csr_path: str or None
 

	
 
    :returns: Status code, one from gimmecert.commands.ExitCode.
 
    :rtype: int
 
    """
 

	
 
    # Set-up paths where we will output artefacts.
 
    private_key_path = os.path.join(project_directory, '.gimmecert', 'client', '%s.key.pem' % entity_name)
 
    certificate_path = os.path.join(project_directory, '.gimmecert', 'client', '%s.cert.pem' % entity_name)
 
    csr_path = os.path.join(project_directory, '.gimmecert', 'client', '%s.csr.pem' % entity_name)
 

	
 
    # Ensure hierarchy is initialised.
 
    if not gimmecert.storage.is_initialised(project_directory):
 
        print("CA hierarchy must be initialised prior to issuing client certificates. Run the gimmecert init command first.", file=stderr)
 
        return ExitCode.ERROR_NOT_INITIALISED
 

	
 
    if os.path.exists(private_key_path) or os.path.exists(certificate_path):
 
    # Ensure artefacts do not exist already.
 
    if os.path.exists(private_key_path) or os.path.exists(certificate_path) or os.path.exists(csr_path):
 
        print("Refusing to overwrite existing data. Certificate has already been issued for client %s." % entity_name, file=stderr)
 
        return ExitCode.ERROR_CERTIFICATE_ALREADY_ISSUED
 

	
 
    # Grab the issuing CA private key and certificate.
 
    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_client_certificate(entity_name, private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    gimmecert.storage.write_private_key(private_key, private_key_path)
 
    # Either read public key from CSR, or generate a new private key.
 
    if custom_csr_path:
 
        csr = gimmecert.storage.read_csr(custom_csr_path)
 
        public_key = csr.public_key()
 
    else:
 
        private_key = gimmecert.crypto.generate_private_key()
 
        public_key = private_key.public_key()
 

	
 
    # Issue certificate using the passed-in information and
 
    # appropriate public key.
 
    certificate = gimmecert.crypto.issue_client_certificate(entity_name, public_key, issuer_private_key, issuer_certificate)
 

	
 
    # Output CSR or private key depending on what was provided.
 
    if custom_csr_path:
 
        gimmecert.storage.write_csr(csr, csr_path)
 
    else:
 
        gimmecert.storage.write_private_key(private_key, private_key_path)
 

	
 
    gimmecert.storage.write_certificate(certificate, certificate_path)
 

	
 
    print("""Client certificate issued.\n
 
    Client private key: .gimmecert/client/%s.key.pem\n
 
    Client certificate: .gimmecert/client/%s.cert.pem""" % (entity_name, entity_name), file=stdout)
 
    # Show user information about generated artefacts.
 
    print("Client certificate issued.", file=stdout)
 

	
 
    if custom_csr_path:
 
        print("Client CSR: .gimmecert/client/%s.csr.pem" % entity_name, file=stdout)
 
    else:
 
        print("Client private key: .gimmecert/client/%s.key.pem" % entity_name, file=stdout)
 

	
 
    print("Client certificate: .gimmecert/client/%s.cert.pem" % entity_name, file=stdout)
 

	
 
    return ExitCode.SUCCESS
 

	
gimmecert/crypto.py
Show inline comments
 
@@ -342,3 +342,34 @@ def renew_certificate(old_certificate, public_key, issuer_private_key, issuer_ce
 
                                        [(e.value, e.critical) for e in old_certificate.extensions])
 

	
 
    return new_certificate
 

	
 

	
 
def generate_csr(name, private_key):
 
    """
 
    Generates certificate signing request.
 

	
 
    :param name: Name of the end entity. If string, passed-in name is treated as value for CN in subject DN.
 
    :type name: str or cryptography.x509.Name
 

	
 
    :param private_key: Private key of end entity to use for signing the CSR.
 
    :type private_key: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey
 

	
 
    :returns: Certificate signing request with specified naming signed with passed-in private key.
 
    :rtype: cryptography.x509.CertificateSigningRequest
 
    """
 

	
 
    if isinstance(name, cryptography.x509.Name):
 
        subject_dn = name
 
    else:
 
        subject_dn = get_dn(name)
 

	
 
    builder = cryptography.x509.CertificateSigningRequestBuilder()
 
    builder = builder.subject_name(subject_dn)
 

	
 
    csr = builder.sign(
 
        private_key,
 
        cryptography.hazmat.primitives.hashes.SHA256(),
 
        cryptography.hazmat.backends.default_backend()
 
    )
 

	
 
    return csr
gimmecert/storage.py
Show inline comments
 
@@ -194,3 +194,43 @@ def read_certificate(certificate_path):
 
        )
 

	
 
    return certificate
 

	
 

	
 
def write_csr(csr, path):
 
    """
 
    Writes the passed-in certificate signing request to designated
 
    path in OpenSSL-style PEM format.
 

	
 
    :param certificate: CSR that should be writtent-out.
 
    :type certificate: cryptography.x509.CertificateSigningRequest
 

	
 
    :param path: File path where the CSR should be written.
 
    :type path: str
 
    """
 

	
 
    csr_pem = csr.public_bytes(encoding=cryptography.hazmat.primitives.serialization.Encoding.PEM)
 

	
 
    with open(path, 'wb') as csr_file:
 
        csr_file.write(csr_pem)
 

	
 

	
 
def read_csr(csr_path):
 
    """
 
    Reads X.509 certificate signing request from the designated file
 
    path. The CSR is expected to be provided in OpenSSL-style PEM
 
    format.
 

	
 
    :param csr_path: Path to CSR file.
 
    :type csr_path: str
 

	
 
    :returns: CSR object read from the specified file.
 
    :rtype: cryptography.x509.CertificateSigningRequest
 
    """
 

	
 
    with open(csr_path, 'rb') as csr_file:
 
        csr = cryptography.x509.load_pem_x509_csr(
 
            csr_file.read(),
 
            cryptography.hazmat.backends.default_backend()
 
        )
 

	
 
    return csr
tests/test_cli.py
Show inline comments
 
@@ -495,7 +495,7 @@ def test_client_command_invoked_with_correct_parameters(mock_client, tmpdir):
 

	
 
    gimmecert.cli.main()
 

	
 
    mock_client.assert_called_once_with(sys.stdout, sys.stderr, tmpdir.strpath, 'myclient')
 
    mock_client.assert_called_once_with(sys.stdout, sys.stderr, tmpdir.strpath, 'myclient', None)
 

	
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'server', '--update-dns-names', 'myserver', 'service.local'])
tests/test_commands.py
Show inline comments
 
@@ -326,7 +326,7 @@ def test_client_reports_error_if_directory_is_not_initialised(tmpdir):
 
    stdout_stream = io.StringIO()
 
    stderr_stream = io.StringIO()
 

	
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient')
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient', None)
 

	
 
    stdout = stdout_stream.getvalue()
 
    stderr = stderr_stream.getvalue()
 
@@ -337,7 +337,7 @@ def test_client_reports_error_if_directory_is_not_initialised(tmpdir):
 

	
 

	
 
def test_client_returns_status_code(tmpdir):
 
    status_code = gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    status_code = gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    assert isinstance(status_code, int)
 

	
 
@@ -350,7 +350,7 @@ def test_client_reports_success_and_paths_to_generated_artifacts(tmpdir):
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient')
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient', None)
 

	
 
    stdout = stdout_stream.getvalue()
 
    stderr = stderr_stream.getvalue()
 
@@ -359,18 +359,21 @@ def test_client_reports_success_and_paths_to_generated_artifacts(tmpdir):
 
    assert "certificate issued" in stdout
 
    assert ".gimmecert/client/myclient.cert.pem" in stdout
 
    assert ".gimmecert/client/myclient.key.pem" in stdout
 
    assert ".gimmecert/client/myclient.csr.pem" not in stdout
 
    assert stderr == ""
 

	
 

	
 
def test_client_outputs_private_key_to_file(tmpdir):
 
def test_client_outputs_private_key_to_file_without_csr(tmpdir):
 
    depth = 1
 
    private_key_file = tmpdir.join('.gimmecert', 'client', 'myclient.key.pem')
 
    csr_file = tmpdir.join('.gimmecert', 'client', 'myclient.csr.pem')
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    assert private_key_file.check(file=1)
 
    assert not csr_file.check()
 

	
 
    private_key_file_content = private_key_file.read()
 

	
 
@@ -384,7 +387,7 @@ def test_client_outputs_certificate_to_file(tmpdir):
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    assert certificate_file.check(file=1)
 

	
 
@@ -402,12 +405,12 @@ def test_client_errors_out_if_certificate_already_issued(tmpdir):
 

	
 
    # Previous run.
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 
    existing_private_key = tmpdir.join('.gimmecert', 'client', 'myclient.key.pem').read()
 
    certificate = tmpdir.join('.gimmecert', 'client', 'myclient.cert.pem').read()
 

	
 
    # New run.
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient')
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient', None)
 

	
 
    stdout = stdout_stream.getvalue()
 
    stderr = stderr_stream.getvalue()
 
@@ -554,7 +557,7 @@ def test_renew_reports_success_and_paths_to_client_artifacts(tmpdir):
 
    stderr_stream = io.StringIO()
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    status_code = gimmecert.commands.renew(stdout_stream, stderr_stream, tmpdir.strpath, 'client', 'myclient', False)
 

	
 
@@ -589,7 +592,7 @@ def test_renew_keeps_client_private_key(tmpdir):
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 
    private_key_after_issuance = private_key_file.read()
 

	
 
    gimmecert.commands.renew(io.StringIO(), io.StringIO(), tmpdir.strpath, 'client', 'myclient', False)
 
@@ -621,7 +624,7 @@ def test_renew_replaces_client_certificate(tmpdir):
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 
    certificate_after_issuance = certificate_file.read()
 

	
 
    gimmecert.commands.renew(io.StringIO(), io.StringIO(), tmpdir.strpath, 'client', 'myclient', False)
 
@@ -793,10 +796,10 @@ def test_status_reports_client_certificate_information(tmpdir):
 
        gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    with freeze_time('2018-02-01 00:15:00'):
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1')
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1', None)
 

	
 
    with freeze_time('2018-03-01 00:15:00'):
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient2')
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient2', None)
 

	
 
    status_code = gimmecert.commands.status(stdout_stream, stderr_stream, tmpdir.strpath)
 

	
 
@@ -838,8 +841,8 @@ def test_status_reports_no_server_certificates_were_issued(tmpdir):
 
    # Just create some sample data, but no server certificates.
 
    with freeze_time('2018-01-01 00:15:00'):
 
        gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1')
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient2')
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1', None)
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient2', None)
 

	
 
    status_code = gimmecert.commands.status(stdout_stream, stderr_stream, tmpdir.strpath)
 

	
 
@@ -903,7 +906,7 @@ def test_certificate_marked_as_not_valid_or_expired_as_appropriate(tmpdir, subje
 
    with freeze_time(issuance_date):
 
        gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, "My Project", depth)
 
        gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver', None)
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient')
 
        gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    # Move to specific date in future/past for different validity checks.
 
    with freeze_time(status_date):
 
@@ -920,3 +923,95 @@ def test_certificate_marked_as_not_valid_or_expired_as_appropriate(tmpdir, subje
 
    validity = stdout_lines[index_dn + 1]
 

	
 
    assert validity.endswith(validity_status)
 

	
 

	
 
def test_client_reports_success_and_paths_to_generated_artifacts_with_csr(tmpdir):
 
    depth = 1
 
    custom_csr_file = tmpdir.join('mycustom.csr.pem')
 

	
 
    stdout_stream = io.StringIO()
 
    stderr_stream = io.StringIO()
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    custom_csr = gimmecert.crypto.generate_csr('blah', private_key)
 
    gimmecert.storage.write_csr(custom_csr, custom_csr_file.strpath)
 

	
 
    status_code = gimmecert.commands.client(stdout_stream, stderr_stream, tmpdir.strpath, 'myclient', custom_csr_file.strpath)
 

	
 
    stdout = stdout_stream.getvalue()
 
    stderr = stderr_stream.getvalue()
 

	
 
    assert status_code == gimmecert.commands.ExitCode.SUCCESS
 
    assert "certificate issued" in stdout
 
    assert ".gimmecert/client/myclient.cert.pem" in stdout
 
    assert ".gimmecert/client/myclient.csr.pem" in stdout
 
    assert ".gimmecert/client/myclient.key.pem" not in stdout
 
    assert stderr == ""
 

	
 

	
 
def test_client_outputs_passed_in_csr_to_file_without_private_key(tmpdir):
 
    depth = 1
 

	
 
    private_key_file = tmpdir.join('.gimmecert', 'client', 'myclient.key.pem')
 
    csr_file = tmpdir.join('.gimmecert', 'client', 'myclient.csr.pem')
 
    custom_csr_file = tmpdir.join('mycustom.csr.pem')
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    csr = gimmecert.crypto.generate_csr('mycustomcsr', private_key)
 
    gimmecert.storage.write_csr(csr, custom_csr_file.strpath)
 
    custom_csr_file_content = custom_csr_file.read()
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', custom_csr_file.strpath)
 

	
 
    assert csr_file.check(file=1)
 
    assert not private_key_file.check()
 

	
 
    csr_file_content = csr_file.read()
 

	
 
    assert csr_file_content == custom_csr_file_content
 

	
 

	
 
def test_client_uses_correct_public_key_without_csr(tmpdir):
 
    depth = 1
 

	
 
    private_key_file = tmpdir.join('.gimmecert', 'client', 'myclient.key.pem')
 
    certificate_file = tmpdir.join('.gimmecert', 'client', 'myclient.cert.pem')
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', None)
 

	
 
    private_key = gimmecert.storage.read_private_key(private_key_file.strpath)
 
    certificate = gimmecert.storage.read_certificate(certificate_file.strpath)
 

	
 
    private_key_public_numbers = private_key.public_key().public_numbers()
 
    certificate_public_numbers = certificate.public_key().public_numbers()
 

	
 
    assert certificate_public_numbers == private_key_public_numbers
 

	
 

	
 
def test_client_uses_correct_public_key_but_no_naming_with_csr(tmpdir):
 
    depth = 1
 

	
 
    custom_csr_file = tmpdir.join('customcsr.pem')
 
    certificate_file = tmpdir.join('.gimmecert', 'client', 'myclient.cert.pem')
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    csr = gimmecert.crypto.generate_csr('mycustomcsr', private_key)
 
    gimmecert.storage.write_csr(csr, custom_csr_file.strpath)
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 

	
 
    gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient', custom_csr_file.strpath)
 

	
 
    certificate = gimmecert.storage.read_certificate(certificate_file.strpath)
 

	
 
    csr_public_numbers = csr.public_key().public_numbers()
 
    certificate_public_numbers = certificate.public_key().public_numbers()
 

	
 
    assert certificate_public_numbers == csr_public_numbers
 
    assert csr.subject != certificate.subject
tests/test_crypto.py
Show inline comments
 
@@ -589,3 +589,28 @@ def test_renew_certificate_not_after_does_not_exceed_ca_validity():
 
        certificate = gimmecert.crypto.renew_certificate(old_certificate, private_key.public_key(), issuer_private_key, issuer_certificate)
 

	
 
    assert certificate.not_valid_after == issuer_certificate.not_valid_after
 

	
 

	
 
def test_generate_csr_returns_csr_with_passed_in_dn():
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    subject_dn = gimmecert.crypto.get_dn('testcsr')
 

	
 
    csr = gimmecert.crypto.generate_csr(subject_dn, private_key)
 

	
 
    assert isinstance(csr, cryptography.x509.CertificateSigningRequest)
 
    assert csr.public_key().public_numbers() == private_key.public_key().public_numbers()
 
    assert csr.subject == subject_dn
 

	
 

	
 
def test_generate_csr_returns_csr_with_passed_in_name():
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    name = 'testcsr'
 

	
 
    expected_subject_dn = gimmecert.crypto.get_dn('testcsr')
 

	
 
    csr = gimmecert.crypto.generate_csr(name, private_key)
 

	
 
    assert csr.public_key().public_numbers() == private_key.public_key().public_numbers()
 
    assert csr.subject == expected_subject_dn
tests/test_storage.py
Show inline comments
 
@@ -169,3 +169,32 @@ def test_read_ca_hierarchy_returns_list_of_ca_private_key_and_certificate_pairs_
 
    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")
 

	
 

	
 
def test_write_csr(tmpdir):
 
    csr_file = tmpdir.join('test.csr.pem')
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    csr = gimmecert.crypto.generate_csr('test', private_key)
 

	
 
    gimmecert.storage.write_csr(csr, csr_file.strpath)
 

	
 
    csr_file_content = csr_file.read()
 

	
 
    assert os.path.exists(csr_file.strpath)
 
    assert csr_file_content.startswith('-----BEGIN CERTIFICATE REQUEST-----')
 
    assert csr_file_content.endswith('-----END CERTIFICATE REQUEST-----\n')
 

	
 

	
 
def test_read_csr(tmpdir):
 
    csr_file = tmpdir.join('mycsr.csr.pem')
 

	
 
    private_key = gimmecert.crypto.generate_private_key()
 
    original_csr = gimmecert.crypto.generate_csr('mycsr', private_key)
 

	
 
    gimmecert.storage.write_csr(original_csr, csr_file.strpath)
 

	
 
    csr = gimmecert.storage.read_csr(csr_file.strpath)
 

	
 
    assert isinstance(csr, cryptography.x509.CertificateSigningRequest)
 
    assert csr == original_csr
0 comments (0 inline, 0 general)