From cf3a6f52bceba0453079dca6d611fed2a5bdf100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bahad=C4=B1r=20Kandemir?= Date: Mon, 25 Oct 2010 10:50:43 +0000 Subject: [PATCH] scripts: Package signing tool verifies using certificate. Package signing tool verifies signed data using certificate instead of public key. It also verifies certificate, warns if it's self signed, fails if it's invalid. --- scripts/package-signing/README | 1 - scripts/package-signing/pisign.py | 92 ++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/scripts/package-signing/README b/scripts/package-signing/README index 07bf3f65..b69bf1f3 100644 --- a/scripts/package-signing/README +++ b/scripts/package-signing/README @@ -5,7 +5,6 @@ mkdir certs openssl genrsa -des3 -out certs/enc_key.pem 1024 openssl req -new -subj '/C=TR/ST=Kocaeli/L=Gebze/CN=Pardus' -key certs/enc_key.pem -out certs/req.pem openssl req -x509 -key certs/enc_key.pem -in certs/req.pem -out certs/cert.pem -days 365 -openssl x509 -inform pem -in certs/cert.pem -pubkey -noout > certs/pub_key.pem # Feel free to share 'certs/cert.pem' and 'certs/pub_key.pem' with anyone, keep others to yourself. diff --git a/scripts/package-signing/pisign.py b/scripts/package-signing/pisign.py index f307155e..da1cff2b 100755 --- a/scripts/package-signing/pisign.py +++ b/scripts/package-signing/pisign.py @@ -15,13 +15,18 @@ import sys import tempfile import zipfile -def sign_data(data, certificate, password_fd): +# Certificate validity +VALID = 1 +SELF_SIGNED = 0 +INVALID = -1 + +def sign_data(data, key_file, password_fd): """ Signs data with given certificate. Arguments: data: Data to be signed - certificate: Private certificate + key_file: Private key password_fd: File that contains passphrase Returns: Signed data @@ -31,7 +36,7 @@ def sign_data(data, certificate, password_fd): # Use OpenSSL to sign data command = '/usr/bin/openssl dgst -sha1 -sign %s -passin fd:%d' - command = command % (certificate, password_fd.fileno()) + command = command % (key_file, password_fd.fileno()) command = shlex.split(command) pipe = subprocess.Popen(command, stdin=subprocess.PIPE, @@ -48,14 +53,62 @@ def sign_data(data, certificate, password_fd): return signed_ascii -def verify_data(data, signature, key_file): +def get_public_key(cert_file): """ - Verifies signature of data signed with given key file. + Extracts public key from certificate. + + Arguments: + cert_file: Certificate + Returns: + Public key + """ + # Use OpenSSL to extract public key + command = "openssl x509 -inform pem -in %s -pubkey -noout" + command = command % cert_file + command = shlex.split(command) + + pipe = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + key_ascii = pipe.stdout.read() + + return key_ascii + +def verify_certificate(cert_file): + """ + Verifies a certificate. + + Arguments: + cert_file: Certificate + Returns: + VALID, SELF_SIGNED or INVALID + """ + # Use OpenSSL to verify certificate + command = '/usr/bin/openssl verify %s' + command = command % cert_file + command = shlex.split(command) + + pipe = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + lines = pipe.stdout.read().split('\n') + if len(lines) < 2: + return INVALID + elif lines[1].startswith("error"): + code = lines[1].split()[1] + if code == '18': + return SELF_SIGNED + else: + return INVALID + else: + return VALID + +def verify_data(data, signature, cert_file): + """ + Verifies signature of data signed with given certificate. Arguments: data: Original data signature_file: Signed data - key_file: Public keyfile + cert_file: Certificate Returns: True if valid, False if invalid """ @@ -69,9 +122,14 @@ def verify_data(data, signature, key_file): data_file.write(data) data_file.flush() + # Keep public key in a temporary file + pub_file = tempfile.NamedTemporaryFile() + pub_file.write(get_public_key(cert_file)) + pub_file.flush() + # Use OpenSSL to verify signature command = '/usr/bin/openssl dgst -sha1 -verify %s -signature %s %s' - command = command % (key_file, signature_file.name, data_file.name) + command = command % (pub_file.name, signature_file.name, data_file.name) command = shlex.split(command) pipe = subprocess.Popen(command, stdout=subprocess.PIPE, @@ -81,6 +139,7 @@ def verify_data(data, signature, key_file): # Destroy temporary files signature_file.close() data_file.close() + pub_file.close() return result == 0 @@ -107,13 +166,13 @@ def get_zip_hashes(zip_obj): return "\n".join(hashes) -def verify_zipfile(filename, key_file): +def verify_zipfile(filename, cert_file): """ Verifies integrity of a ZIP file. Arguments: filename: ZIP filename - key_file: Public keyfile + cert_file: Certificate Returns: True if valid, False if invalid """ @@ -133,7 +192,7 @@ def verify_zipfile(filename, key_file): zip_obj.close() # Verify signed data - return verify_data(hashes, signature, key_file) + return verify_data(hashes, signature, cert_file) def sign_zipfile(filename, certificate, password_fd): """ @@ -164,7 +223,7 @@ def print_usage(): print "Usage:" print " %s sign " % sys.argv[0] - print " %s verify " % sys.argv[0] + print " %s verify " % sys.argv[0] sys.exit(1) def main(): @@ -201,13 +260,20 @@ def main(): elif operation == 'verify': try: - key_file = sys.argv[2] + cert_file = sys.argv[2] except IndexError: print_usage() + cert_validity = verify_certificate(cert_file) + if cert_validity == SELF_SIGNED: + print "WARNING: Certificate is self-signed." + elif cert_validity == INVALID: + print "ERROR: Certificate is invalid" + sys.exit(1) + if len(sys.argv[3:]): for filename in sys.argv[3:]: - if verify_zipfile(filename, key_file): + if verify_zipfile(filename, cert_file): print "%s is valid." % filename else: print "%s is corrupted." % filename