124 lines
3.7 KiB
Python
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()
|