cfdi-uuid/source/sat/cfdi_cert.py

124 lines
3.7 KiB
Python

#!/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()