From 2b2f43a4612ec0979637bb1da3cd831f0894d6ed Mon Sep 17 00:00:00 2001 From: Grant Baker Date: Thu, 27 Apr 2023 12:52:34 -0700 Subject: [PATCH 1/2] This prints out the hash of the CSR to disk for both the aggregator and collaborator. The user then compares and approves this hash with the hash printed out of the file to validate the CSR. In addition, a warning message is pritned if certify is run in silent mode. Fixes securefederatedai#692 Signed-off-by: Grant Baker --- openfl/cryptography/io.py | 18 ++++++++++++++++++ openfl/interface/aggregator.py | 7 +++++++ openfl/interface/collaborator.py | 7 +++++++ 3 files changed, 32 insertions(+) diff --git a/openfl/cryptography/io.py b/openfl/cryptography/io.py index b9ffce917f..ec7473fa10 100644 --- a/openfl/cryptography/io.py +++ b/openfl/cryptography/io.py @@ -111,3 +111,21 @@ def read_csr(path: Path) -> Tuple[CertificateSigningRequest, str]: # TODO: replace assert with exception / sys.exit assert (isinstance(csr, x509.CertificateSigningRequest)) return csr, hasher.hexdigest() + + +def get_csr_hash(certificate: CertificateSigningRequest) -> str: + """ + Get hash of cryptography certificate. + + Args: + certificate : Cryptography CSR object + + Returns: + Hash of cryptography certificate / csr + """ + hasher = sha384() + encoded_bytes = certificate.public_bytes( + encoding=serialization.Encoding.PEM, + ) + hasher.update(encoded_bytes) + return hasher.hexdigest() diff --git a/openfl/interface/aggregator.py b/openfl/interface/aggregator.py index 8aadaab73a..02f920b229 100644 --- a/openfl/interface/aggregator.py +++ b/openfl/interface/aggregator.py @@ -71,6 +71,7 @@ def generate_cert_request(fqdn): from openfl.cryptography.participant import generate_csr from openfl.cryptography.io import write_crt from openfl.cryptography.io import write_key + from openfl.cryptography.io import get_csr_hash from openfl.interface.cli_helper import CERT_DIR if fqdn is None: @@ -91,6 +92,10 @@ def generate_cert_request(fqdn): echo(' Writing AGGREGATOR certificate key pair to: ' + style( f'{CERT_DIR}/server', fg='green')) + # Print csr hash before writing csr to disk + csr_hash = get_csr_hash(server_csr) + echo('The CSR Hash ' + style(f'{csr_hash}', fg='red')) + # Write aggregator csr and key to disk write_crt(server_csr, CERT_DIR / 'server' / f'{file_name}.csr') write_key(server_private_key, CERT_DIR / 'server' / f'{file_name}.key') @@ -175,6 +180,7 @@ def certify(fqdn, silent): if silent: + echo(' Warning: manual check of certificate hashes is bypassed in silent mode.') echo(' Signing AGGREGATOR certificate') signed_agg_cert = sign_certificate(csr, signing_key, signing_crt.subject) write_crt(signed_agg_cert, crt_path_absolute_path) @@ -183,6 +189,7 @@ def certify(fqdn, silent): if confirm('Do you want to sign this certificate?'): + echo('Make sure the two hashes above are the same.') echo(' Signing AGGREGATOR certificate') signed_agg_cert = sign_certificate(csr, signing_key, signing_crt.subject) write_crt(signed_agg_cert, crt_path_absolute_path) diff --git a/openfl/interface/collaborator.py b/openfl/interface/collaborator.py index a0c25b73d8..bc20ce0d7e 100644 --- a/openfl/interface/collaborator.py +++ b/openfl/interface/collaborator.py @@ -135,6 +135,7 @@ def generate_cert_request(collaborator_name, data_path, silent, skip_package): from openfl.cryptography.participant import generate_csr from openfl.cryptography.io import write_crt from openfl.cryptography.io import write_key + from openfl.cryptography.io import get_csr_hash from openfl.interface.cli_helper import CERT_DIR common_name = f'{collaborator_name}'.lower() @@ -152,6 +153,10 @@ def generate_cert_request(collaborator_name, data_path, silent, skip_package): echo(' Moving COLLABORATOR certificate to: ' + style( f'{CERT_DIR}/{file_name}', fg='green')) + # Print csr hash before writing csr to disk + csr_hash = get_csr_hash(client_csr) + echo('The CSR Hash ' + style(f'{csr_hash}', fg='red')) + # Write collaborator csr and key to disk write_crt(client_csr, CERT_DIR / 'client' / f'{file_name}.csr') write_key(client_private_key, CERT_DIR / 'client' / f'{file_name}.key') @@ -341,12 +346,14 @@ def certify(collaborator_name, silent, request_pkg=None, import_=False): if silent: echo(' Signing COLLABORATOR certificate') + echo(' Warning: manual check of certificate hashes is bypassed in silent mode.') signed_col_cert = sign_certificate(csr, signing_key, signing_crt.subject) write_crt(signed_col_cert, f'{cert_name}.crt') register_collaborator(CERT_DIR / 'client' / f'{file_name}.crt') else: + echo('Make sure the two hashes above are the same.') if confirm('Do you want to sign this certificate?'): echo(' Signing COLLABORATOR certificate') From 653993b8fa5276569a1fd06bc588bd085fb5ec2d Mon Sep 17 00:00:00 2001 From: Grant Baker Date: Fri, 28 Apr 2023 10:30:47 -0700 Subject: [PATCH 2/2] Refactor read_csr function to use get_csr_hash Signed-off-by: Grant Baker --- openfl/cryptography/io.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openfl/cryptography/io.py b/openfl/cryptography/io.py index ec7473fa10..52bfc5e95b 100644 --- a/openfl/cryptography/io.py +++ b/openfl/cryptography/io.py @@ -102,15 +102,13 @@ def read_csr(path: Path) -> Tuple[CertificateSigningRequest, str]: Returns: Cryptography CSR object """ - hasher = sha384() with open(path, 'rb') as f: pem_data = f.read() - hasher.update(pem_data) csr = x509.load_pem_x509_csr(pem_data) # TODO: replace assert with exception / sys.exit assert (isinstance(csr, x509.CertificateSigningRequest)) - return csr, hasher.hexdigest() + return csr, get_csr_hash(csr) def get_csr_hash(certificate: CertificateSigningRequest) -> str: