Skip to content

Commit

Permalink
Merge pull request #6 from chadgates/extract_certificate_info
Browse files Browse the repository at this point in the history
Extract certificate information to dictionary function.
  • Loading branch information
abhishek-ram authored Jun 3, 2019
2 parents e7ff37f + f239930 commit 8dbf864
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 3 deletions.
48 changes: 48 additions & 0 deletions pyas2lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from io import BytesIO

from pyas2lib.exceptions import AS2Exception
from datetime import datetime


def unquote_as2name(quoted_name):
Expand Down Expand Up @@ -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
18 changes: 17 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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()
Binary file added tests/fixtures/cert_extract_private.cer
Binary file not shown.
49 changes: 49 additions & 0 deletions tests/fixtures/cert_extract_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIgfSzwLtO4wsCAggA
MB0GCWCGSAFlAwQBKgQQfAIjuBBfqCedJyQh0fvnRASCBNDbGjojvuy02uUpmPCr
sFdlxbAbnodKqxFi7xamh2uuqpl430S7R2QEuCZJUOzjIPPfAhdleVbdU56QIztz
d+RI+/jE8tLWPGeQ0vF1IOeSZqSjPf0ETxiRqrpEJ0KurLnyeul/7nOeEwe/bQAc
kKCDR80d7MZbPQd4L/kZ0uG2VU1tpuVljpkqI778QgOgX08/CGHNKXks3c/Yhfn1
AxRx6eg1y0gazkR9g9wsOAJ69TAjX1hoBlmSZw22nJNZtn8eRlgzWKcKluI0NKFR
V97CA8WKUb59posspV4iyLFiFE+TvO/oMz8CzAoguLHIW5lGX5tQ6HUqbxEHkMbH
7Hv+6JhQGdHVeGZppcuIeWc/gnCc2X5PWSTx+0c7vBv2FIHM0WjX/krNagzcAL/A
u/6pGv8DrLLQTCSSdCojWJhD6q1VdiXkWnI2uQDHUNFODxywfmUkGROHHF8BcNsA
4YNKtNCEkNCs3QfoRkLXyRhEL6Rb0k1woR0iz8zK3hiuBTVvu3z6kASoJZJk7isF
DbeekZB2dnrOWZs9HcJ8gNWV61nQg9q67Rf8GzK0DA30r6tFLlWwqmWSzR3QucB/
ddLXHiq9DyBlSznowYliGo8smHH+oxliMcZ7B8AmmrKwpEHKRGxFR1LkE9PFBIKI
X07PcuZRpynIq2/W+HFSRxFBjUWm7lsykO+ciojbj2caODfKfWs/Ma2kscopghBQ
hqqRzKlsfOxZBWeiJYrqLHZDz4asiYC1gvrc8Krx8u2mZdBodo7T2jfLAtaHMYnR
JwWEhPMq6Ixhc9OnsRVH+KeolthyT0XjR19quqH9mB8oByAhR0eQcmduPoMTwknU
Lah//rckT7sNGvJwum3iGUtIE0y0GBcU/OQ94bHelYKL4kZu//mXvvy+B0eYbqjW
3C5uy6GhPjBQ6BBMuafu+tfJ/ZGOU3ZG0g/4yrspa/qN5JuDzdMeKax8Y9jQZ5Ba
ZdjCnvr5MWO6krC6evQlkmnag+IOTAfqv+mBtOgZjVS9I49s+6XzR4UNr8dAKMg5
E53dM2gHvg5k80i6JksspONP6+m+rL0ckrB2pYkWrGUyQi/U5f8h2CCChJfLPAEl
PUuhG9Ynh7rGubFtLFe3+RvHWtnIRmg+pxW+W9HBhv26qhkkTlx/AdVeYSoPaldG
7KsJX/6qJA4EJ4v4QWyyupCYJMmTePx/i4kIFz/CxEDiUc+5BQh08+gafiDvMaNc
7uiyhBCm3/BVWb9lSem770nz1QawH3Te4fxgKUTlj2ICaQBj37QvkVawjFaawdRq
KvtwagY/B2d8kIjgRWxRbAe/DCv9cpXxdkgsOANZn4S5fn/jmmSpG6t5zQTu55TT
Fkk4uESDIjpUYs65uuNeWZTAuNEiSJON4ha4W8h0/6g3MAjNyJ7YAQgOSSMuSGqk
xdyjWRJ0zmheL2NFYxIAmlrOquMwHMkU8YJb+9jI6RkNq8szkFenIKfCMgb9yfrX
P0aGOz3AvKRlTpcIgY1TDNoGQ0pSlMjW/VmhJJEfeOjbsQdJVr+XObmggXBYv8wE
DJhQXvVSV83iuyq7rCuEEjPs5gpKkq+K5AqXmTtJzFtVMcQ/BmJdVDssPEnjNmL8
visz5I234/hl2utyZOj4yTSiYg==
-----END ENCRYPTED PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDBDCCAewCCQC+x6S5XDiB+TANBgkqhkiG9w0BAQsFADBEMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UECgwIcHlhczJsaWIxDTALBgNV
BAMMBHRlc3QwHhcNMTkwNjAzMTEzMjU3WhcNMjkwNTMxMTEzMjU3WjBEMQswCQYD
VQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UECgwIcHlhczJsaWIx
DTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/
ialOlAPsVGq9n3cEhFHBO9G9DyZlket2gVkVk/ONF9fqgRd1uGdrhqqOw0dwjYWH
/heuKF4FbkiNGD9r8iOF2B/Wnj8iEJO0Mc5rKKKmi5e2w/84M9VVYhkpo9AGtb0q
3COtIqbp5qU7FTqyOsvTvCa13gAVVhHm8naLxCkp6MnL0om2kNK3Exv8rYQybbpe
iLkdZda/3Qo4QEvSS4EKeQsdnN6/W7Rf9GM8gpFXCKykP2tNsESHndIxXrFBPHma
qvA8llncXyUBPJtUFrhb7Q2n+dLT07TmoctOMm+B/Dw6bN7+lHMW44/9xxMCVd/i
muhYicU7rx+bU9bWPNTFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHfyQ15A/L6A
NZjzwScbmkjnIngjSblxOeTG30Vgcm9f+4T+bLwuF6jd4F5FngkDb/9oE3N3toEk
OwRVtV4mKhiJa5Vn1KGFqDzZ8Hs6GaKaxAFpa8XqPEQx/edVyRmX2S1MFp1qEovu
ldTsYtIC3v2ZmwoxBqPf84974cdmF6j0FrnT/eaBUWCDhjn/XFpL5ZnoDS5JSw5j
E1CpLbNRi4q6fSM+VRPrr1qkGcaXduLUN58B31QizcjCEy2XjAvVSgAq8IoILt3/
bIUOY/Wbp1BQvVEBxALc34yxWOcUbSamIm6KYSLoMpWOsZAoyuQa2rAXIwAZwejY
9RfBSn/YWSo=
-----END CERTIFICATE-----
Binary file added tests/fixtures/cert_extract_public.cer
Binary file not shown.
19 changes: 19 additions & 0 deletions tests/fixtures/cert_extract_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBDCCAewCCQC+x6S5XDiB+TANBgkqhkiG9w0BAQsFADBEMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UECgwIcHlhczJsaWIxDTALBgNV
BAMMBHRlc3QwHhcNMTkwNjAzMTEzMjU3WhcNMjkwNTMxMTEzMjU3WjBEMQswCQYD
VQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UECgwIcHlhczJsaWIx
DTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/
ialOlAPsVGq9n3cEhFHBO9G9DyZlket2gVkVk/ONF9fqgRd1uGdrhqqOw0dwjYWH
/heuKF4FbkiNGD9r8iOF2B/Wnj8iEJO0Mc5rKKKmi5e2w/84M9VVYhkpo9AGtb0q
3COtIqbp5qU7FTqyOsvTvCa13gAVVhHm8naLxCkp6MnL0om2kNK3Exv8rYQybbpe
iLkdZda/3Qo4QEvSS4EKeQsdnN6/W7Rf9GM8gpFXCKykP2tNsESHndIxXrFBPHma
qvA8llncXyUBPJtUFrhb7Q2n+dLT07TmoctOMm+B/Dw6bN7+lHMW44/9xxMCVd/i
muhYicU7rx+bU9bWPNTFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHfyQ15A/L6A
NZjzwScbmkjnIngjSblxOeTG30Vgcm9f+4T+bLwuF6jd4F5FngkDb/9oE3N3toEk
OwRVtV4mKhiJa5Vn1KGFqDzZ8Hs6GaKaxAFpa8XqPEQx/edVyRmX2S1MFp1qEovu
ldTsYtIC3v2ZmwoxBqPf84974cdmF6j0FrnT/eaBUWCDhjn/XFpL5ZnoDS5JSw5j
E1CpLbNRi4q6fSM+VRPrr1qkGcaXduLUN58B31QizcjCEy2XjAvVSgAq8IoILt3/
bIUOY/Wbp1BQvVEBxALc34yxWOcUbSamIm6KYSLoMpWOsZAoyuQa2rAXIwAZwejY
9RfBSn/YWSo=
-----END CERTIFICATE-----
26 changes: 24 additions & 2 deletions tests/test_advanced.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import unicode_literals, absolute_import, print_function
from . import Pyas2TestCase, as2
from . import Pyas2TestCase, as2, utils
import os
import base64

import datetime

class TestAdvanced(Pyas2TestCase):

Expand Down Expand Up @@ -325,6 +325,28 @@ def test_load_private_key(self):
except as2.AS2Exception as e:
self.fail('Failed to load pem private key: %s' % e)

def test_extract_certificate_info(self):
""" Test case that extracts data from private and public certificates in PEM or DER format"""

cert_info = {'valid_from': datetime.datetime(2019, 6, 3, 11, 32, 57),
'valid_to': datetime.datetime(2029, 5, 31, 11, 32, 57),
'subject': [('C', 'AU'), ('ST', 'Some-State'), ('O', 'pyas2lib'), ('CN', 'test')],
'issuer': [('C', 'AU'), ('ST', 'Some-State'), ('O', 'pyas2lib'), ('CN', 'test')],
'serial': 13747137503594840569}
cert_empty = {'valid_from': None,
'valid_to': None,
'subject': None,
'issuer': None,
'serial': None}

# compare result of function with cert_info dict.
self.assertEqual(utils.extract_certificate_info(self.private_pem), cert_info)
self.assertEqual(utils.extract_certificate_info(self.private_cer), cert_info)
self.assertEqual(utils.extract_certificate_info(self.public_pem), cert_info)
self.assertEqual(utils.extract_certificate_info(self.public_cer), cert_info)
self.assertEqual(utils.extract_certificate_info(b''), cert_empty)


def find_org(self, headers):
return self.org

Expand Down

0 comments on commit 8dbf864

Please sign in to comment.