#!/usr/bin/env python import base64 import datetime import re from subprocess import check_output from dateutil.parser import parse OPENSSL = 'openssl' class SATCertificate(object): def __init__(self, cer, pem): self._error = '' self._init_values() self._get_data_cer(cer) self._key_pem = pem def _init_values(self): self._rfc = '' self._serial_number_int = 0 self._serial_number_str = '' self._not_before = None self._not_after = None self._is_fiel = False self._are_couple = False self._is_valid_time = False self._cer_pem = '' self._cer_txt = '' self._key = b'' self._key_enc = b'' self._key_pem = b'' self._cer_modulus = 0 self._key_modulus = 0 self._issuer = '' self._fert = '' return def _get_data_cer(self, cer): # ~ RFC cmd = f'{OPENSSL} x509 -inform der -in "{cer}" -noout -subject' result = check_output(cmd, shell=True).decode() pattern = r'[A-Z]{3,4}[0-9]{6}[A-Z0-9]{3}' self._rfc = re.search(pattern, result)[0] # ~ Serial number sep = '=' cmd = f'{OPENSSL} x509 -inform der -in "{cer}" -noout -serial' result = check_output(cmd, shell=True).decode() self._serial_number2 = result.split(sep)[1] self._serial_number_str = self._serial_number2[1::2] # ~ Dates cmd = f'{OPENSSL} x509 -inform der -in "{cer}" -noout -dates' result = check_output(cmd, shell=True).decode() data = result.split('\n') self._not_before = parse(data[0].split('=')[1]) self._not_after = parse(data[1].split('=')[1]) self._fert = self._not_after.strftime('%y%m%d%H%M%SZ') # ~ Issuer cmd = f'{OPENSSL} x509 -inform der -in "{cer}" -noout -issuer' self._issuer = check_output(cmd, shell=True).decode()[7:] now = datetime.datetime.utcnow().timestamp() self._is_valid_time = (now > self.not_before.timestamp()) and (now < self.not_after.timestamp()) if not self._is_valid_time: msg = 'El certificado no es vigente' self._error = msg # ~ cmd = f'{OPENSSL} x509 -inform der -in "{cer}"' # ~ result = check_output(cmd, shell=True).decode() # ~ self._cer_txt = ''.join(result.split('\n')[1:-2]) return def __str__(self): msg = '\n\tRFC: {}\n'.format(self.rfc) msg += '\tNo de Serie: {}\n'.format(self.serial_number_str) msg += '\tVálido desde: {}\n'.format(self.not_before) msg += '\tVálido hasta: {}\n'.format(self.not_after) msg += '\tEs vigente: {}\n'.format(self.is_valid_time) msg += '\tSon pareja: {}\n'.format(self.are_couple) msg += '\tEs FIEL: {}\n'.format(self.is_fiel) return msg @property def rfc(self): return self._rfc @property def serial_number_str(self): return self._serial_number_str @property def not_before(self): return self._not_before @property def not_after(self): return self._not_after @property def is_valid_time(self): return self._is_valid_time @property def are_couple(self): return self._are_couple @property def is_fiel(self): return self._is_fiel @property def fert(self): return self._fert def sign(self, data, name_hash='sha256'): cmd = f"echo -n -e '{data}' | {OPENSSL} dgst -{name_hash} -sign '{self._key_pem}' | {OPENSSL} enc -base64" result = check_output(cmd, shell=True).decode().replace('\n', '') return base64.b64encode(result.encode()).decode()