From 6e937cb09fc9f1cf94bc7f755393eb83c85f9831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Neto?= Date: Mon, 1 May 2023 15:55:47 -0300 Subject: [PATCH 1/4] [IMP] enable legacy SHA1 support --- src/erpbrasil/assinatura/assinatura.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/erpbrasil/assinatura/assinatura.py b/src/erpbrasil/assinatura/assinatura.py index 8db375a..f54e521 100644 --- a/src/erpbrasil/assinatura/assinatura.py +++ b/src/erpbrasil/assinatura/assinatura.py @@ -10,6 +10,19 @@ _logger = logging.getLogger(__name__) +class XMLSignerWithSHA1(signxml.XMLSigner): + """ + Extension of the `XMLSigner` class that adds support for the SHA1 hash algorithm + to the XML signature process. + Note: + SHA1-based algorithms are not supported in the default configuration because + they are not secure, but in the NF-e project, other more modern algorithms + are still not accepted. + """ + def check_deprecated_methods(self): + "Override to disable deprecated Check" + + class Assinatura(object): def __init__(self, certificado): @@ -48,7 +61,7 @@ def assina_xml2(self, xml_element, reference, getchildren=False): if element.text is not None and not element.text.strip(): element.text = None - signer = signxml.XMLSigner( + signer = XMLSignerWithSHA1( method=signxml.methods.enveloped, signature_algorithm="rsa-sha1", digest_algorithm='sha1', @@ -91,7 +104,7 @@ def assina_xml2(self, xml_element, reference, getchildren=False): def assina_nfse(self, xml_etree): - signer = signxml.XMLSigner( + signer = XMLSignerWithSHA1( method=signxml.methods.enveloped, signature_algorithm="rsa-sha1", digest_algorithm='sha1', From 4cab45ff20a7ea577c3f3b2bbda3b26e7d9c31b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Neto?= Date: Mon, 1 May 2023 15:59:28 -0300 Subject: [PATCH 2/4] [FIX] namespaces --- src/erpbrasil/assinatura/assinatura.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/erpbrasil/assinatura/assinatura.py b/src/erpbrasil/assinatura/assinatura.py index f54e521..61c956e 100644 --- a/src/erpbrasil/assinatura/assinatura.py +++ b/src/erpbrasil/assinatura/assinatura.py @@ -68,7 +68,8 @@ def assina_xml2(self, xml_element, reference, getchildren=False): c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315' ) - signer.namespaces = {"ds": "http://www.w3.org/2000/09/xmldsig#"} + signer.excise_empty_xmlns_declarations = True + signer.namespaces = {None: signxml.namespaces.ds} ref_uri = ('#%s' % reference) if reference else None From a92160714517dd5a1256086f365638b97507c7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Neto?= Date: Mon, 1 May 2023 16:10:58 -0300 Subject: [PATCH 3/4] [IMP] add sign method for RPS additional sign for NFS-e Paulistana --- src/erpbrasil/assinatura/assinatura.py | 29 +++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/erpbrasil/assinatura/assinatura.py b/src/erpbrasil/assinatura/assinatura.py index 61c956e..7fbf5ae 100644 --- a/src/erpbrasil/assinatura/assinatura.py +++ b/src/erpbrasil/assinatura/assinatura.py @@ -117,11 +117,8 @@ def assina_nfse(self, xml_etree): key=self.chave_privada, cert=self.cert, ) - signed_root = etree.tostring(signed_root, encoding=str) - signed_root = signed_root.replace('\r', '').replace('\n', '') - return signed_root def assina_string(self, message): @@ -136,6 +133,32 @@ def assina_string(self, message): ) return signature + def sign_rps_nfse_paulistana(self, data): + """ + Generates an additional signature for RPS. + + The private key used to sign the RPS is obtained + from the `certificado` attribute of the class. + + The signing process uses the `PKCS1v15` padding and the `SHA1` hash algorithm, + which are required by the NFSe Paulistana specification. + + Args: + ---- + data (bytes): The data to be signed. + + Returns: + ------- + signature (bytes): The signature of the data. + """ + private_key = self.certificado.key + signature = private_key.sign( + data, + padding.PKCS1v15(), + hashes.SHA1() + ) + return signature + def assina_pdf(self, arquivo, dados_assinatura, algoritmo='sha256'): try: from endesive import pdf From 3d0e5c38a1dbd0c5153d872a18e21a4582352667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B4nio=20Neto?= Date: Mon, 1 May 2023 16:10:58 -0300 Subject: [PATCH 4/4] Add sign_pkcs1v15_sha1 method for NFSe Paulistana RPS signing --- src/erpbrasil/assinatura/assinatura.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/erpbrasil/assinatura/assinatura.py b/src/erpbrasil/assinatura/assinatura.py index 8db375a..78323a5 100644 --- a/src/erpbrasil/assinatura/assinatura.py +++ b/src/erpbrasil/assinatura/assinatura.py @@ -103,11 +103,8 @@ def assina_nfse(self, xml_etree): key=self.chave_privada, cert=self.cert, ) - signed_root = etree.tostring(signed_root, encoding=str) - signed_root = signed_root.replace('\r', '').replace('\n', '') - return signed_root def assina_string(self, message): @@ -122,6 +119,26 @@ def assina_string(self, message): ) return signature + def sign_pkcs1v15_sha1(self, data): + """ + Sign data using PKCS1v15 padding and SHA1 hash algorithm. + + This method is specifically tailored for the NFSe Paulistana RPS signing process. + + Args: + data (bytes): Data to be signed. + + Returns: + bytes: Generated signature. + """ + private_key = self.certificado.key + signature = private_key.sign( + data, + padding.PKCS1v15(), + hashes.SHA1() + ) + return signature + def assina_pdf(self, arquivo, dados_assinatura, algoritmo='sha256'): try: from endesive import pdf