Changeset - 7b789c8b5199
[Not reviewed]
0 3 0
Branko Majic (branko) - 6 years ago 2018-04-14 00:49:50
branko@majic.rs
GC-22: Updated renew command to replace existing private key with CSR if passed-in:

- Added functional test which covers renewal of server and client
certificates by generating new private key when previous certificate
was issued using custom CSR.
- Replace the CSR with generate private key when renewing certificate
in case where previous certificate was issued with CSR.
- Added unit tests covering new functionality.
3 files changed with 174 insertions and 0 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_csr.py
Show inline comments
 
@@ -315,3 +315,100 @@ def test_renew_certificate_originally_issued_with_private_key_using_csr(tmpdir):
 
    assert client_new_certificate != client_old_certificate
 
    assert client_stored_csr == client_csr
 
    assert client_new_certificate_public_key == client_csr_public_key
 

	
 

	
 
def test_renew_certificate_originally_issued_with_csr_using_private_key(tmpdir):
 
    # John has an existing project where he has generated a server and
 
    # client private key with corresponding CSR.
 
    tmpdir.chdir()
 
    run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myserver.key.pem",
 
                "-subj", "/CN=myserver", "-out", "mycustomserver.csr.pem")
 
    run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myclient.key.pem",
 
                "-subj", "/CN=myclient", "-out", "mycustomclient.csr.pem")
 

	
 
    # He wants to grab some certificates for those, so he goes ahead
 
    # and initialises the CA hierarchy.
 
    tmpdir.chdir()
 
    run_command("gimmecert", "init")
 

	
 
    # He proceeds to issue a server and client certificate using the
 
    # CSRs.
 
    run_command("gimmecert", "server", "--csr", "mycustomserver.csr.pem", "myserver")
 
    run_command("gimmecert", "client", "--csr", "mycustomserver.csr.pem", "myclient")
 

	
 
    # John has a look at generated artefacts.
 
    server_old_certificate = tmpdir.join(".gimmecert", "server", "myserver.cert.pem").read()
 
    server_old_certificate_public_key, _, _ = run_command("openssl", "x509", "-noout", "-pubkey", "-in", ".gimmecert/server/myserver.cert.pem")
 

	
 
    client_old_certificate = tmpdir.join(".gimmecert", "client", "myclient.cert.pem").read()
 
    client_old_certificate_public_key, _, _ = run_command("openssl", "x509", "-noout", "-pubkey", "-in", ".gimmecert/client/myclient.cert.pem")
 

	
 
    # John accidentally removes the generated private keys.
 
    tmpdir.join('myserver.key.pem').remove()
 
    tmpdir.join('myclient.key.pem').remove()
 

	
 
    # He realises that the issued certificates are now useless to him,
 
    # and decides to renew the certificates and let Gimmecert generate
 
    # private keys for him.
 

	
 
    # He goes ahead and renews the server certificate first,
 
    # requesting a new private key along the way.
 
    stdout, stderr, exit_code = run_command("gimmecert", "renew", "--new-private-key", "server", "myserver")
 

	
 
    # No errors are shown, and John is informed about generated
 
    # artefacts, and that the CSR has been replaced with a generated
 
    # private key.
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert "Generated new private key and renewed certificate for server myserver." in stdout
 
    assert "CSR used for issuance of previous certificate has been removed, and a private key has been generated in its place." in stdout
 
    assert ".gimmecert/server/myserver.key.pem" in stdout
 
    assert ".gimmecert/server/myserver.cert.pem" in stdout
 
    assert ".gimmecert/server/myserver.csr.pem" not in stdout
 

	
 
    # John has a look at generated artefacts.
 
    server_generated_private_key_public_key, _, _ = run_command("openssl", "rsa", "-pubout", "-in", ".gimmecert/server/myserver.key.pem")
 

	
 
    server_new_certificate = tmpdir.join(".gimmecert", "server", "myserver.cert.pem").read()
 
    server_new_certificate_public_key, _, _ = run_command("openssl", "x509", "-noout", "-pubkey", "-in", ".gimmecert/server/myserver.cert.pem")
 

	
 
    # John notices that, for start, the CSR has indeed been removed
 
    # from the filesystem, that the content of the certificate has
 
    # changed, that the old public key is not the same as the new one,
 
    # and that public key from the certificate matches with the
 
    # private key.
 
    assert not tmpdir.join(".gimmecert", "server", "myserver.csr.pem").check()
 
    assert server_new_certificate != server_old_certificate
 
    assert server_old_certificate_public_key != server_generated_private_key_public_key
 
    assert server_new_certificate_public_key == server_generated_private_key_public_key
 

	
 
    # He goes ahead and renews the client certificate first,
 
    # requesting a new private key along the way.
 
    stdout, stderr, exit_code = run_command("gimmecert", "renew", "--new-private-key", "client", "myclient")
 

	
 
    # No errors are shown, and John is informed about generated
 
    # artefacts, and that the CSR has been replaced with a generated
 
    # private key.
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert "Generated new private key and renewed certificate for client myclient." in stdout
 
    assert "CSR used for issuance of previous certificate has been removed, and a private key has been generated in its place." in stdout
 
    assert ".gimmecert/client/myclient.key.pem" in stdout
 
    assert ".gimmecert/client/myclient.cert.pem" in stdout
 
    assert ".gimmecert/client/myclient.csr.pem" not in stdout
 

	
 
    # John has a look at generated artefacts.
 
    client_generated_private_key_public_key, _, _ = run_command("openssl", "rsa", "-pubout", "-in", ".gimmecert/client/myclient.key.pem")
 

	
 
    client_new_certificate = tmpdir.join(".gimmecert", "client", "myclient.cert.pem").read()
 
    client_new_certificate_public_key, _, _ = run_command("openssl", "x509", "-noout", "-pubkey", "-in", ".gimmecert/client/myclient.cert.pem")
 

	
 
    # John notices that, for start, the CSR has indeed been removed
 
    # from the filesystem, that the content of the certificate has
 
    # changed, that the old public key is not the same as the new one,
 
    # and that public key from the certificate matches with the
 
    # private key.
 
    assert not tmpdir.join(".gimmecert", "client", "myclient.csr.pem").check()
 
    assert client_new_certificate != client_old_certificate
 
    assert client_old_certificate_public_key != client_generated_private_key_public_key
 
    assert client_new_certificate_public_key == client_generated_private_key_public_key
gimmecert/commands.py
Show inline comments
 
@@ -430,6 +430,13 @@ def renew(stdout, stderr, project_directory, entity_type, entity_name, generate_
 
    else:
 
        private_key_replaced_with_csr = False
 

	
 
    # Replace CSR with private key.
 
    if generate_new_private_key and os.path.exists(csr_path):
 
        os.remove(csr_path)
 
        csr_replaced_with_private_key = True
 
    else:
 
        csr_replaced_with_private_key = False
 

	
 
    # Type of artefacts reported depending on whether the private key
 
    # or CSR are present.
 
    if generate_new_private_key:
 
@@ -440,6 +447,9 @@ def renew(stdout, stderr, project_directory, entity_type, entity_name, generate_
 
    if private_key_replaced_with_csr:
 
        print("Private key used for issuance of previous certificate has been removed, and replaced with the passed-in CSR.", file=stdout)
 

	
 
    if csr_replaced_with_private_key:
 
        print("CSR used for issuance of previous certificate has been removed, and a private key has been generated in its place.", file=stdout)
 

	
 
    # Output information about private key or CSR path.
 
    if os.path.exists(csr_path):
 
        print("{entity_type_titled} CSR: .gimmecert/{entity_type}/{entity_name}.csr.pem"
tests/test_commands.py
Show inline comments
 
@@ -1314,6 +1314,7 @@ def test_renew_replaces_server_private_key_with_csr(tmpdir):
 

	
 
    custom_csr_file = tmpdir.join("mycustom.csr.pem")
 
    csr_file = tmpdir.join(".gimmecert", "server", "myserver.csr.pem")
 
    certificate_file = tmpdir.join(".gimmecert", "server", "myserver.cert.pem")
 
    private_key_file = tmpdir.join(".gimmecert", "server", "myserver.key.pem")
 

	
 
    custom_csr_private_key = gimmecert.crypto.generate_private_key()
 
@@ -1332,8 +1333,14 @@ def test_renew_replaces_server_private_key_with_csr(tmpdir):
 

	
 
    csr_file_content = csr_file.read()
 

	
 
    csr = gimmecert.storage.read_csr(csr_file.strpath)
 
    csr_public_numbers = csr.public_key().public_numbers()
 
    certificate = gimmecert.storage.read_certificate(certificate_file.strpath)
 
    certificate_public_numbers = certificate.public_key().public_numbers()
 

	
 
    assert csr_file_content == custom_csr_file_content
 
    assert not private_key_file.check()
 
    assert certificate_public_numbers == csr_public_numbers
 

	
 

	
 
def test_renew_raises_exception_if_both_new_private_key_generation_and_csr_are_passed_in(tmpdir):
 
@@ -1352,3 +1359,63 @@ def test_renew_raises_exception_if_both_new_private_key_generation_and_csr_are_p
 

	
 
    print(e_info.value)
 
    assert str(e_info.value) == "Only one of the following two parameters should be specified: generate_new_private_key, custom_csr_path."
 

	
 

	
 
def test_renew_reports_success_and_paths_to_server_artifacts_with_private_key_when_replacing_csr(tmpdir):
 
    depth = 1
 

	
 
    custom_csr_file = tmpdir.join("mycustom.csr.pem")
 

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

	
 
    custom_private_key = gimmecert.crypto.generate_private_key()
 
    custom_csr = gimmecert.crypto.generate_csr("mytest", custom_private_key)
 
    gimmecert.storage.write_csr(custom_csr, custom_csr_file.strpath)
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 
    gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver', None, False, custom_csr_file.strpath)
 

	
 
    status_code = gimmecert.commands.renew(stdout_stream, stderr_stream, tmpdir.strpath, 'server', 'myserver', True, None)
 

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

	
 
    assert status_code == gimmecert.commands.ExitCode.SUCCESS
 
    assert "Generated new private key and renewed certificate for server myserver." in stdout
 
    assert "removed" in stdout
 
    assert "generated" in stdout
 
    assert ".gimmecert/server/myserver.key.pem" in stdout
 
    assert ".gimmecert/server/myserver.cert.pem" in stdout
 
    assert ".gimmecert/server/myserver.csr.pem" not in stdout
 
    assert stderr == ""
 

	
 

	
 
def test_renew_replaces_server_csr_with_private_key(tmpdir):
 
    depth = 1
 

	
 
    custom_csr_file = tmpdir.join("mycustom.csr.pem")
 
    csr_file = tmpdir.join(".gimmecert", "server", "myserver.csr.pem")
 
    certificate_file = tmpdir.join(".gimmecert", "server", "myserver.cert.pem")
 
    private_key_file = tmpdir.join(".gimmecert", "server", "myserver.key.pem")
 

	
 
    custom_csr_private_key = gimmecert.crypto.generate_private_key()
 
    custom_csr = gimmecert.crypto.generate_csr("mycustom", custom_csr_private_key)
 
    gimmecert.storage.write_csr(custom_csr, custom_csr_file.strpath)
 

	
 
    gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, depth)
 
    gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver', None, False, custom_csr_file.strpath)
 

	
 
    assert csr_file.check(file=1)
 

	
 
    gimmecert.commands.renew(io.StringIO(), io.StringIO(), tmpdir.strpath, 'server', 'myserver', True, None)
 

	
 
    assert private_key_file.check(file=1)
 

	
 
    private_key = gimmecert.storage.read_private_key(private_key_file.strpath)
 
    private_key_public_numbers = private_key.public_key().public_numbers()
 
    certificate = gimmecert.storage.read_certificate(certificate_file.strpath)
 
    certificate_public_numbers = certificate.public_key().public_numbers()
 

	
 
    assert not csr_file.check()
 
    assert certificate_public_numbers == private_key_public_numbers
0 comments (0 inline, 0 general)