diff --git a/source/cfdi-cert.py b/source/cfdi_cert.py similarity index 82% rename from source/cfdi-cert.py rename to source/cfdi_cert.py index 4b7ea77..ec1937f 100644 --- a/source/cfdi-cert.py +++ b/source/cfdi_cert.py @@ -6,7 +6,6 @@ import datetime import getpass from pathlib import Path -import xmlsec from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography import x509 @@ -20,15 +19,17 @@ from conf import TOKEN class SATCertificate(object): - def __init__(self, cer=b'', key=b'', password=''): + def __init__(self, cer=b'', key=b'', pem=b'', password=''): self._error = '' self._init_values() self._get_data_cer(cer) self._get_data_key(key, password) + self._get_data_pem(pem) def _init_values(self): self._rfc = '' self._serial_number = '' + self._serial_number2 = '' self._not_before = None self._not_after = None self._is_fiel = False @@ -37,9 +38,11 @@ class SATCertificate(object): self._cer_pem = '' self._cer_txt = '' self._key_enc = b'' + self._key_pem = b'' self._p12 = b'' self._cer_modulus = 0 self._key_modulus = 0 + self._issuer = '' return def __str__(self): @@ -66,9 +69,12 @@ class SATCertificate(object): obj = x509.load_der_x509_certificate(cer, default_backend()) self._rfc = obj.subject.get_attributes_for_oid( NameOID.X500_UNIQUE_IDENTIFIER)[0].value.split(' ')[0] + self._serial_number2 = obj.serial_number self._serial_number = '{0:x}'.format(obj.serial_number)[1::2] self._not_before = obj.not_valid_before self._not_after = obj.not_valid_after + self._issuer = ','.join([i.rfc4514_string() for i in obj.issuer]) + now = datetime.datetime.utcnow() self._is_valid_time = (now > self.not_before) and (now < self.not_after) if not self._is_valid_time: @@ -81,6 +87,7 @@ class SATCertificate(object): self._cer_pem = obj.public_bytes(serialization.Encoding.PEM).decode() self._cer_txt = ''.join(self._cer_pem.split('\n')[1:-2]) self._cer_modulus = obj.public_key().public_numbers().n + return def _get_data_key(self, key, password): @@ -126,6 +133,14 @@ class SATCertificate(object): ) return key_pem + def _get_data_pem(self, pem): + if not pem: + return + + self._key_pem = serialization.load_pem_private_key( + pem, None, backend=default_backend()) + return + # Not work def _get_p12(self): obj = serialization.pkcs12.serialize_key_and_certificates('test', @@ -139,7 +154,26 @@ class SATCertificate(object): firma = private_key.sign(data, padding.PKCS1v15(), hashes.SHA256()) return base64.b64encode(firma).decode() + def _sign_with_pem(self, data, name_hash): + if name_hash == 'sha256': + type_hash = hashes.SHA256() + elif name_hash == 'sha1': + type_hash = hashes.SHA1() + + firma = self._key_pem.sign(data, padding.PKCS1v15(), type_hash) + return base64.b64encode(firma).decode() + + def sign_sha1(self, data, password=''): + if self._key_pem: + return self._sign_with_pem(data, 'sha1') + + private_key = self._get_key(password) + firma = private_key.sign(data, padding.PKCS1v15(), hashes.SHA1()) + return base64.b64encode(firma).decode() + def sign_xml(self, tree): + import xmlsec + node = xmlsec.tree.find_node(tree, xmlsec.constants.NodeSignature) ctx = xmlsec.SignatureContext() key = xmlsec.Key.from_memory(self.key_pem, xmlsec.constants.KeyDataFormatPem) @@ -157,6 +191,10 @@ class SATCertificate(object): def serial_number(self): return self._serial_number + @property + def serial_number2(self): + return self._serial_number2 + @property def not_before(self): return self._not_before @@ -197,6 +235,10 @@ class SATCertificate(object): def key_enc(self): return self._key_enc + @property + def issuer(self): + return self._issuer + @property def p12(self): return self._get_p12() @@ -204,52 +246,3 @@ class SATCertificate(object): @property def error(self): return self._error - - -def main(args): - # ~ contra = getpass.getpass('Introduce la contraseƱa del archivo KEY: ') - contra = '12345678a' - if not contra.strip(): - msg = 'La contraseƱa es requerida' - print(msg) - return - - path_cer = Path(args.cer) - path_key = Path(args.key) - - if not path_cer.is_file(): - msg = 'El archivo CER es necesario' - print(msg) - return - - if not path_key.is_file(): - msg = 'El archivo KEY es necesario' - print(msg) - return - - cer = path_cer.read_bytes() - key = path_key.read_bytes() - cert = SATCertificate(cer, key, contra) - - if cert.error: - print(cert.error) - else: - print(cert) - return - - -def _process_command_line_arguments(): - parser = argparse.ArgumentParser(description='CFDI Certificados') - - help = 'Archivo CER' - parser.add_argument('-c', '--cer', help=help, default='') - help = 'Archivo KEY' - parser.add_argument('-k', '--key', help=help, default='') - - args = parser.parse_args() - return args - - -if __name__ == '__main__': - args = _process_command_line_arguments() - main(args)