From f239930c9f258577318396cee4012e5e4b290ed1 Mon Sep 17 00:00:00 2001 From: Wassilios Lytras Date: Mon, 3 Jun 2019 14:03:55 +0200 Subject: [PATCH] Extract certificate information to dictionary function. --- pyas2lib/utils.py | 48 +++++++++++++++++++++++ tests/__init__.py | 18 ++++++++- tests/fixtures/cert_extract_private.cer | Bin 0 -> 776 bytes tests/fixtures/cert_extract_private.pem | 49 ++++++++++++++++++++++++ tests/fixtures/cert_extract_public.cer | Bin 0 -> 776 bytes tests/fixtures/cert_extract_public.pem | 19 +++++++++ tests/test_advanced.py | 26 ++++++++++++- 7 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/cert_extract_private.cer create mode 100644 tests/fixtures/cert_extract_private.pem create mode 100644 tests/fixtures/cert_extract_public.cer create mode 100644 tests/fixtures/cert_extract_public.pem diff --git a/pyas2lib/utils.py b/pyas2lib/utils.py index 061d67e..40e985e 100644 --- a/pyas2lib/utils.py +++ b/pyas2lib/utils.py @@ -8,6 +8,7 @@ from io import BytesIO from pyas2lib.exceptions import AS2Exception +from datetime import datetime def unquote_as2name(quoted_name): @@ -185,3 +186,50 @@ def verify_certificate_chain(cert_str, trusted_certs, ignore_self_signed=True): except crypto.X509StoreContextError as e: raise AS2Exception('Partner Certificate Invalid: %s' % e.args[-1][-1]) + + +def extract_certificate_info(cert): + """ + Extract validity information from the certificate and return a dictionary. + Provide either key with certificate (private) or public certificate + :param cert: the certificate as byte string in PEM or DER format + :return: a dictionary holding certificate information: + valid_from (datetime) + valid_to (datetime) + subject (list of name, value tuples) + issuer (list of name, value tuples) + serial (int) + """ + + # initialize the cert_info dictionary + cert_info = { + 'valid_from': None, + 'valid_to': None, + 'subject': None, + 'issuer': None, + 'serial': None + } + + # get certificate to DER list + der = pem_to_der(cert) + + # iterate through the list to find the certificate + for _item in der: + try: + # load the certificate. if element is key, exception is triggered and next element is tried + certificate = crypto.load_certificate(crypto.FILETYPE_ASN1, _item) + + # on successful load, extract the various fields into the dictionary + cert_info['valid_from'] = datetime.strptime(certificate.get_notBefore().decode('utf8'), "%Y%m%d%H%M%SZ") + cert_info['valid_to'] = datetime.strptime(certificate.get_notAfter().decode('utf8'), "%Y%m%d%H%M%SZ") + cert_info['subject'] = [tuple(item.decode('utf8') for item in sets) + for sets in certificate.get_subject().get_components()] + cert_info['issuer'] = [tuple(item.decode('utf8') for item in sets) + for sets in certificate.get_issuer().get_components()] + cert_info['serial'] = certificate.get_serial_number() + break + except crypto.Error: + continue + + # return the dictionary + return cert_info diff --git a/tests/__init__.py b/tests/__init__.py index 943bbd9..e851695 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,7 +3,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) -from pyas2lib import as2, exceptions +from pyas2lib import as2, exceptions, utils class Pyas2TestCase(unittest.TestCase): @@ -46,3 +46,19 @@ def setUpClass(cls): with open(os.path.join( cls.TEST_DIR, 'cert_sb2bi_public.ca'), 'rb') as fp: cls.sb2bi_public_ca = fp.read() + + with open(os.path.join( + cls.TEST_DIR, 'cert_extract_private.cer'), 'rb') as fp: + cls.private_cer = fp.read() + + with open(os.path.join( + cls.TEST_DIR, 'cert_extract_private.pem'), 'rb') as fp: + cls.private_pem = fp.read() + + with open(os.path.join( + cls.TEST_DIR, 'cert_extract_public.cer'), 'rb') as fp: + cls.public_pem = fp.read() + + with open(os.path.join( + cls.TEST_DIR, 'cert_extract_public.cer'), 'rb') as fp: + cls.public_cer = fp.read() diff --git a/tests/fixtures/cert_extract_private.cer b/tests/fixtures/cert_extract_private.cer new file mode 100644 index 0000000000000000000000000000000000000000..d984cce2382ee69176f397f91fa71805c26cd71d GIT binary patch literal 776 zcmXqLVrDUDVtm8I$*}MElASRYjXw=|**LY@JlekVGBR?rG8niRavN~6F^96S2{So{ z8VVZ-f;b#JT*3Ldsk*@>i6yCqf(HB`K`tJSg382VqnyknLtX=JkPtHuOG#>RiGiFr zuc4)ZnSrsPp|O#vd6YP>k)?qtkO$#X$(ANYC1mF_vNA9?G4eA2eaOYs#K_37zjLME z6y`S}S$pS~v$O;rw7$5PUoCaw>$1j3(aE2C#jn3=6ffP8p53;p@34D8Z)^KM@pT$; zta%>26871jlv{7eUz=yIBrti4;kj&$Ma#OUZ#(?o!uV=vlBDM13v65WYTZ#@tF-Lp zv!&LeR-3F&U*4m(^&W$0nBcQdW!*r>x@5HMz z+RP3S+Y$YiclpmtmoIO5w(zu{QGVke8>^gq|E3g+J?{T|T$m~J{-aqhB07&+ua}=4 zeC?Xem7`3|j0}v(Aq0$IUr>f#8=#X5a&9NlR5u?hH!EDwni0eme8$nYFZMV*;CW6ENoq2bMr&BmE@vX zM;J4+kG`^TG5q^H^rYnUo4P(?bF+lH-%Y*pCg~E>y}vVOa~ZNNzW=$u`r+|3@fBah zc3%GbtT8a5xy|x_OqBQ2nJ;+tJiYmng#%XVZVv43+EuG;7b^UEebf@kW7EqXT``}} zct1qx?1@9dy3>2OuX-_PedyxQyZb+i6yCqf(HB`K`tJSg382VqnyknLtX=JkPtHuOG#>RiGiFr zuc4)ZnSrsPp|O#vd6YP>k)?qtkO$#X$(ANYC1mF_vNA9?G4eA2eaOYs#K_37zjLME z6y`S}S$pS~v$O;rw7$5PUoCaw>$1j3(aE2C#jn3=6ffP8p53;p@34D8Z)^KM@pT$; zta%>26871jlv{7eUz=yIBrti4;kj&$Ma#OUZ#(?o!uV=vlBDM13v65WYTZ#@tF-Lp zv!&LeR-3F&U*4m(^&W$0nBcQdW!*r>x@5HMz z+RP3S+Y$YiclpmtmoIO5w(zu{QGVke8>^gq|E3g+J?{T|T$m~J{-aqhB07&+ua}=4 zeC?Xem7`3|j0}v(Aq0$IUr>f#8=#X5a&9NlR5u?hH!EDwni0eme8$nYFZMV*;CW6ENoq2bMr&BmE@vX zM;J4+kG`^TG5q^H^rYnUo4P(?bF+lH-%Y*pCg~E>y}vVOa~ZNNzW=$u`r+|3@fBah zc3%GbtT8a5xy|x_OqBQ2nJ;+tJiYmng#%XVZVv43+EuG;7b^UEebf@kW7EqXT``}} zct1qx?1@9dy3>2OuX-_PedyxQyZb+