From 07ff7da6d41fb3843e9bfba277125b269989ac7c 2020-06-11 15:14:24 From: Branko Majic Date: 2020-06-11 15:14:24 Subject: [PATCH] GC-37: Added support for showing key algorithms to the status command: - Updated functional test for the status command to include a variety of key algorithms and to test for their representation in the output. - Added unit tests. - Updated the status command to extract key algorithm information from the issued certificates and output the information. --- diff --git a/functional_tests/test_status.py b/functional_tests/test_status.py index 2f29bcd65d6f58d426bfc08cfdc756ad5ca36958..926bb2a9cc2e5ddf4858abfc651e69777fd2f271 100644 --- a/functional_tests/test_status.py +++ b/functional_tests/test_status.py @@ -76,13 +76,13 @@ def test_status_on_initialised_directory(tmpdir): run_command('gimmecert', 'init', '-d', '3', '-b', 'My Project') - run_command('gimmecert', 'server', 'myserver1') + run_command('gimmecert', 'server', 'myserver1', '-k', 'rsa:1024') run_command('gimmecert', 'server', 'myserver2', 'myservice.example.com', 'myotherservice.example.com') run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myserver3.key.pem", "-subj", "/CN=myserver3", "-out", "myserver3.csr.pem") run_command('gimmecert', 'server', '--csr', 'myserver3.csr.pem', 'myserver3') - run_command('gimmecert', 'client', 'myclient1') + run_command('gimmecert', 'client', 'myclient1', '-k', 'rsa:1024') run_command('gimmecert', 'client', 'myclient2') run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myclient3.key.pem", "-subj", "/CN=myclient3", "-out", "myclient3.csr.pem") @@ -108,14 +108,18 @@ def test_status_on_initialised_directory(tmpdir): assert "Client certificates" in stdout # John first has a look at information about the CA - # hierarchy. Hierarchy tree is presented using indentation. Each - # CA is listed with its full subject DN, as well as not before and - # not after dates. In addition, the final CA in chain is marked as - # end entity issuing CA. + # hierarchy. First thing he can see is information about the + # default key algorithm in use. This is followed by the hierarchy + # tree presented using indentation. Each CA is listed with its + # full subject DN, as well as not before and not after dates. In + # addition, the final CA in chain is marked as end entity issuing + # CA. + index_default_key_algorithm = stdout_lines.index("Default key algorithm: 2048-bit RSA") # Should not raise index_ca_1 = stdout_lines.index("CN=My Project Level 1 CA") # Should not raise index_ca_2 = stdout_lines.index("CN=My Project Level 2 CA") # Should not raise index_ca_3 = stdout_lines.index("CN=My Project Level 3 CA [END ENTITY ISSUING CA]") # Should not raise + assert index_default_key_algorithm < index_ca_1 assert index_ca_1 < index_ca_2 assert index_ca_2 < index_ca_3 @@ -135,41 +139,49 @@ def test_status_on_initialised_directory(tmpdir): # John then has a look at server certificates. These are presented # in a list, and for each certificate is listed with subject DN, # not before, not after, and included DNS names. Information for - # each server is followed by paths to private key and certificate. + # each server is followed by key algorithm information, and paths + # to private key and certificate. index_myserver1 = stdout_lines.index("CN=myserver1") # Should not raise index_myserver2 = stdout_lines.index("CN=myserver2") # Should not raise index_myserver3 = stdout_lines.index("CN=myserver3") # Should not raise assert stdout_lines[index_myserver1+1].startswith(" Validity: ") assert stdout_lines[index_myserver1+2] == " DNS: myserver1" - assert stdout_lines[index_myserver1+3] == " Private key: .gimmecert/server/myserver1.key.pem" - assert stdout_lines[index_myserver1+4] == " Certificate: .gimmecert/server/myserver1.cert.pem" + assert stdout_lines[index_myserver1+3] == " Key algorithm: 1024-bit RSA" + assert stdout_lines[index_myserver1+4] == " Private key: .gimmecert/server/myserver1.key.pem" + assert stdout_lines[index_myserver1+5] == " Certificate: .gimmecert/server/myserver1.cert.pem" assert stdout_lines[index_myserver2+1].startswith(" Validity: ") assert stdout_lines[index_myserver2+2] == " DNS: myserver2, myservice.example.com, myotherservice.example.com" - assert stdout_lines[index_myserver2+3] == " Private key: .gimmecert/server/myserver2.key.pem" - assert stdout_lines[index_myserver2+4] == " Certificate: .gimmecert/server/myserver2.cert.pem" + assert stdout_lines[index_myserver2+3] == " Key algorithm: 2048-bit RSA" + assert stdout_lines[index_myserver2+4] == " Private key: .gimmecert/server/myserver2.key.pem" + assert stdout_lines[index_myserver2+5] == " Certificate: .gimmecert/server/myserver2.cert.pem" assert stdout_lines[index_myserver3+1].startswith(" Validity: ") assert stdout_lines[index_myserver3+2] == " DNS: myserver3" - assert stdout_lines[index_myserver3+3] == " CSR: .gimmecert/server/myserver3.csr.pem" - assert stdout_lines[index_myserver3+4] == " Certificate: .gimmecert/server/myserver3.cert.pem" + assert stdout_lines[index_myserver3+3] == " Key algorithm: 2048-bit RSA" + assert stdout_lines[index_myserver3+4] == " CSR: .gimmecert/server/myserver3.csr.pem" + assert stdout_lines[index_myserver3+5] == " Certificate: .gimmecert/server/myserver3.cert.pem" # For client certificates, John can see that for each certificate # he can see its subject DN and validity. Information for each - # server is followed by paths to private key and certificate. + # client is followed by key algorithm and paths to private key and + # certificate. index_myclient1 = stdout_lines.index("CN=myclient1") # Should not raise index_myclient2 = stdout_lines.index("CN=myclient2") # Should not raise index_myclient3 = stdout_lines.index("CN=myclient3") # Should not raise assert stdout_lines[index_myclient1+1].startswith(" Validity: ") - assert stdout_lines[index_myclient1+2] == " Private key: .gimmecert/client/myclient1.key.pem" - assert stdout_lines[index_myclient1+3] == " Certificate: .gimmecert/client/myclient1.cert.pem" + assert stdout_lines[index_myclient1+2] == " Key algorithm: 1024-bit RSA" + assert stdout_lines[index_myclient1+3] == " Private key: .gimmecert/client/myclient1.key.pem" + assert stdout_lines[index_myclient1+4] == " Certificate: .gimmecert/client/myclient1.cert.pem" assert stdout_lines[index_myclient2+1].startswith(" Validity: ") - assert stdout_lines[index_myclient2+2] == " Private key: .gimmecert/client/myclient2.key.pem" - assert stdout_lines[index_myclient2+3] == " Certificate: .gimmecert/client/myclient2.cert.pem" + assert stdout_lines[index_myclient2+2] == " Key algorithm: 2048-bit RSA" + assert stdout_lines[index_myclient2+3] == " Private key: .gimmecert/client/myclient2.key.pem" + assert stdout_lines[index_myclient2+4] == " Certificate: .gimmecert/client/myclient2.cert.pem" assert stdout_lines[index_myclient3+1].startswith(" Validity: ") - assert stdout_lines[index_myclient3+2] == " CSR: .gimmecert/client/myclient3.csr.pem" - assert stdout_lines[index_myclient3+3] == " Certificate: .gimmecert/client/myclient3.cert.pem" + assert stdout_lines[index_myclient3+2] == " Key algorithm: 2048-bit RSA" + assert stdout_lines[index_myclient3+3] == " CSR: .gimmecert/client/myclient3.csr.pem" + assert stdout_lines[index_myclient3+4] == " Certificate: .gimmecert/client/myclient3.cert.pem" diff --git a/gimmecert/commands.py b/gimmecert/commands.py index 9729012a8fe6d9af44e50146875b274f3c105595..aaa7b77e270e7aaa77acfd6821167375b3b23ad7 100644 --- a/gimmecert/commands.py +++ b/gimmecert/commands.py @@ -542,6 +542,11 @@ def status(stdout, stderr, project_directory): ca_hierarchy = gimmecert.storage.read_ca_hierarchy(os.path.join(project_directory, '.gimmecert', 'ca')) + # Derive key specification from the issuing CA certificate. + key_algorithm = gimmecert.crypto.KeyGenerator('rsa', ca_hierarchy[-1][1].public_key().key_size) + print("", file=stdout) # Separator + print("Default key algorithm: %s" % key_algorithm, file=stdout) + for i, (_, certificate) in enumerate(ca_hierarchy, 1): # Separator. print("", file=stdout) @@ -580,6 +585,7 @@ def status(stdout, stderr, project_directory): certificate = gimmecert.storage.read_certificate(os.path.join(project_directory, '.gimmecert', 'server', certificate_file)) private_key_path = os.path.join(project_directory, '.gimmecert', 'server', certificate_file.replace('.cert.pem', '.key.pem')) csr_path = os.path.join(project_directory, '.gimmecert', 'server', certificate_file.replace('.cert.pem', '.csr.pem')) + key_algorithm = str(gimmecert.crypto.KeyGenerator("rsa", certificate.public_key().key_size)) # Separator. print("", file=stdout) @@ -597,6 +603,7 @@ def status(stdout, stderr, project_directory): validity_status), file=stdout) print(" DNS: %s" % ", ".join(gimmecert.utils.get_dns_names(certificate)), file=stdout) + print(" Key algorithm: %s" % key_algorithm, file=stdout) if os.path.exists(private_key_path): print(" Private key: .gimmecert/server/%s" % certificate_file.replace('.cert.pem', '.key.pem'), file=stdout) elif os.path.exists(csr_path): @@ -620,6 +627,7 @@ def status(stdout, stderr, project_directory): certificate = gimmecert.storage.read_certificate(os.path.join(project_directory, '.gimmecert', 'client', certificate_file)) private_key_path = os.path.join(project_directory, '.gimmecert', 'client', certificate_file.replace('.cert.pem', '.key.pem')) csr_path = os.path.join(project_directory, '.gimmecert', 'client', certificate_file.replace('.cert.pem', '.csr.pem')) + key_algorithm = str(gimmecert.crypto.KeyGenerator("rsa", certificate.public_key().key_size)) # Separator. print("", file=stdout) @@ -636,6 +644,7 @@ def status(stdout, stderr, project_directory): certificate.not_valid_after), validity_status), file=stdout) + print(" Key algorithm: %s" % key_algorithm, file=stdout) if os.path.exists(private_key_path): print(" Private key: .gimmecert/client/%s" % certificate_file.replace('.cert.pem', '.key.pem'), file=stdout) elif os.path.exists(csr_path): diff --git a/tests/test_commands.py b/tests/test_commands.py index 52f3aa29fecd19c62d308a4b7a9289944e65fbf2..9239a67fea57254ed55ff44d58ee0516101d347d 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -626,12 +626,16 @@ def test_status_reports_uninitialised_directory(tmpdir): assert "CA hierarchy has not been initialised in current directory." in stdout -def test_status_reports_ca_hierarchy_information(tmpdir): +@pytest.mark.parametrize('ca_key_specification,ca_key_representation', [ + (('rsa', 1024), '1024-bit RSA'), + (('rsa', 2048), '2048-bit RSA'), +]) +def test_status_reports_ca_hierarchy_information(tmpdir, ca_key_specification, ca_key_representation): stdout_stream = io.StringIO() stderr_stream = io.StringIO() with freeze_time('2018-01-01 00:15:00'): - gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, 3, ("rsa", 2048)) + gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, 3, ca_key_specification) with freeze_time('2018-06-01 00:15:00'): status_code = gimmecert.commands.status(stdout_stream, stderr_stream, tmpdir.strpath) @@ -644,12 +648,13 @@ def test_status_reports_ca_hierarchy_information(tmpdir): assert stderr == "" assert "CA hierarchy\n------------\n" in stdout + index_ca_key_algorithm = stdout_lines.index("Default key algorithm: %s" % ca_key_representation) # Should not raise index_ca_1 = stdout_lines.index("CN=%s Level 1 CA" % tmpdir.basename) # Should not raise index_ca_2 = stdout_lines.index("CN=%s Level 2 CA" % tmpdir.basename) # Should not raise index_ca_3 = stdout_lines.index("CN=%s Level 3 CA [END ENTITY ISSUING CA]" % tmpdir.basename) # Should not raise full_chain = stdout_lines.index("Full certificate chain: .gimmecert/ca/chain-full.cert.pem") # Shold not raise - assert full_chain > index_ca_3 > index_ca_2 > index_ca_1, "Output ordering for CA section is wrong:\n%s" % stdout + assert full_chain > index_ca_3 > index_ca_2 > index_ca_1 > index_ca_key_algorithm, "Output ordering for CA section is wrong:\n%s" % stdout ca_1_validity = stdout_lines[index_ca_1 + 1] ca_1_certificate_path = stdout_lines[index_ca_1 + 2] @@ -683,7 +688,7 @@ def test_status_reports_server_certificate_information(tmpdir): gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, 3, ("rsa", 2048)) with freeze_time('2018-02-01 00:15:00'): - gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver1', None, None, None) + gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver1', None, None, ("rsa", 1024)) with freeze_time('2018-03-01 00:15:00'): gimmecert.commands.server(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myserver2', ['myservice1.example.com', 'myservice2.example.com'], None, None) @@ -709,31 +714,37 @@ def test_status_reports_server_certificate_information(tmpdir): myserver1_validity = stdout_lines[index_myserver1 + 1] myserver1_dns = stdout_lines[index_myserver1 + 2] - myserver1_private_key_path = stdout_lines[index_myserver1 + 3] - myserver1_certificate_path = stdout_lines[index_myserver1 + 4] + myserver1_key_algorithm = stdout_lines[index_myserver1 + 3] + myserver1_private_key_path = stdout_lines[index_myserver1 + 4] + myserver1_certificate_path = stdout_lines[index_myserver1 + 5] myserver2_validity = stdout_lines[index_myserver2 + 1] myserver2_dns = stdout_lines[index_myserver2 + 2] - myserver2_private_key_path = stdout_lines[index_myserver2 + 3] - myserver2_certificate_path = stdout_lines[index_myserver2 + 4] + myserver2_key_algorithm = stdout_lines[index_myserver2 + 3] + myserver2_private_key_path = stdout_lines[index_myserver2 + 4] + myserver2_certificate_path = stdout_lines[index_myserver2 + 5] myserver3_validity = stdout_lines[index_myserver3 + 1] myserver3_dns = stdout_lines[index_myserver3 + 2] - myserver3_csr_path = stdout_lines[index_myserver3 + 3] - myserver3_certificate_path = stdout_lines[index_myserver3 + 4] + myserver3_key_algorithm = stdout_lines[index_myserver3 + 3] + myserver3_csr_path = stdout_lines[index_myserver3 + 4] + myserver3_certificate_path = stdout_lines[index_myserver3 + 5] assert myserver1_validity == " Validity: 2018-02-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" assert myserver1_dns == " DNS: myserver1" + assert myserver1_key_algorithm == " Key algorithm: 1024-bit RSA" assert myserver1_private_key_path == " Private key: .gimmecert/server/myserver1.key.pem" assert myserver1_certificate_path == " Certificate: .gimmecert/server/myserver1.cert.pem" assert myserver2_validity == " Validity: 2018-03-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" assert myserver2_dns == " DNS: myserver2, myservice1.example.com, myservice2.example.com" + assert myserver2_key_algorithm == " Key algorithm: 2048-bit RSA" assert myserver2_private_key_path == " Private key: .gimmecert/server/myserver2.key.pem" assert myserver2_certificate_path == " Certificate: .gimmecert/server/myserver2.cert.pem" assert myserver3_validity == " Validity: 2018-04-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" assert myserver3_dns == " DNS: myserver3" + assert myserver3_key_algorithm == " Key algorithm: 2048-bit RSA" assert myserver3_csr_path == " CSR: .gimmecert/server/myserver3.csr.pem" assert myserver3_certificate_path == " Certificate: .gimmecert/server/myserver3.cert.pem" @@ -751,7 +762,7 @@ def test_status_reports_client_certificate_information(tmpdir): gimmecert.commands.init(io.StringIO(), io.StringIO(), tmpdir.strpath, tmpdir.basename, 3, ("rsa", 2048)) with freeze_time('2018-02-01 00:15:00'): - gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1', None, None) + gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient1', None, ("rsa", 1024)) with freeze_time('2018-03-01 00:15:00'): gimmecert.commands.client(io.StringIO(), io.StringIO(), tmpdir.strpath, 'myclient2', None, None) @@ -776,26 +787,32 @@ def test_status_reports_client_certificate_information(tmpdir): index_myclient3 = stdout_lines.index("CN=myclient3") # Should not raise myclient1_validity = stdout_lines[index_myclient1 + 1] - myclient1_private_key_path = stdout_lines[index_myclient1 + 2] - myclient1_certificate_path = stdout_lines[index_myclient1 + 3] + myclient1_key_algorithm = stdout_lines[index_myclient1 + 2] + myclient1_private_key_path = stdout_lines[index_myclient1 + 3] + myclient1_certificate_path = stdout_lines[index_myclient1 + 4] myclient2_validity = stdout_lines[index_myclient2 + 1] - myclient2_private_key_path = stdout_lines[index_myclient2 + 2] - myclient2_certificate_path = stdout_lines[index_myclient2 + 3] + myclient2_key_algorithm = stdout_lines[index_myclient2 + 2] + myclient2_private_key_path = stdout_lines[index_myclient2 + 3] + myclient2_certificate_path = stdout_lines[index_myclient2 + 4] myclient3_validity = stdout_lines[index_myclient3 + 1] - myclient3_csr_path = stdout_lines[index_myclient3 + 2] - myclient3_certificate_path = stdout_lines[index_myclient3 + 3] + myclient3_key_algorithm = stdout_lines[index_myclient3 + 2] + myclient3_csr_path = stdout_lines[index_myclient3 + 3] + myclient3_certificate_path = stdout_lines[index_myclient3 + 4] assert myclient1_validity == " Validity: 2018-02-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" + assert myclient1_key_algorithm == " Key algorithm: 1024-bit RSA" assert myclient1_private_key_path == " Private key: .gimmecert/client/myclient1.key.pem" assert myclient1_certificate_path == " Certificate: .gimmecert/client/myclient1.cert.pem" assert myclient2_validity == " Validity: 2018-03-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" + assert myclient2_key_algorithm == " Key algorithm: 2048-bit RSA" assert myclient2_private_key_path == " Private key: .gimmecert/client/myclient2.key.pem" assert myclient2_certificate_path == " Certificate: .gimmecert/client/myclient2.cert.pem" assert myclient3_validity == " Validity: 2018-04-01 00:00:00 UTC - 2019-01-01 00:15:00 UTC" + assert myclient3_key_algorithm == " Key algorithm: 2048-bit RSA" assert myclient3_csr_path == " CSR: .gimmecert/client/myclient3.csr.pem" assert myclient3_certificate_path == " Certificate: .gimmecert/client/myclient3.cert.pem"