Skip to content

Instantly share code, notes, and snippets.

@JuniorPolegato
Created June 9, 2016 21:14
Show Gist options
  • Save JuniorPolegato/afe62b33db5255c2865cab92b854c5ce to your computer and use it in GitHub Desktop.
Save JuniorPolegato/afe62b33db5255c2865cab92b854c5ce to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import lxml.etree
import xmlsec
import urllib2
###############################################################################
# Ajustes para NFe
###############################################################################
# Devido ao nome da chave no Brasil ter "/" e ":",
# no arquivo pk11.py, colocar no início "import urllib2" e abaixo da linha 47:
# (module_path, sep, keyqs) = o.path.rpartition('/')
# colocar a linha:
# keyqs = urllib2.unquote(keyqs)
# Alguns cartões não tem especificação de nome da chave, assim para funcionar
# deve-se retirar (CKA_LABEL, keyname) na linha 135:
# key = _find_object(session, [(CKA_LABEL, keyname), (CKA_CLASS, ...
###############################################################################
# O ID slot deve vir da lista de slots, assim, no arquivo pk11.py, trocar:
# session = lib.openSession(slot) # linha 164
# por:
# session = lib.openSession(lib.getSlotList()[slot])
###############################################################################
# O atributo reference_uri no NFe é dado por "Id", mas o xmlsec procura por
# "id" e "ID", não encontrando o da NFe, assim deve-se adicionar este em
# __init__.py, na linha 48:
# id_attributes = pyconfig.setting("xmlsec.id_attributes", ['ID', 'id'])
# ficando:
# id_attributes = pyconfig.setting("xmlsec.id_attributes", ['ID', 'id', 'Id'])
###############################################################################
# Para sair o certificado na assinatura, é preciso, no arquivo __init__.py,
# na função _load_keyspec, abaixo da linha 165:
# 'source': 'pkcs11',
# colocar a linha:
# 'data': data,
# e depois, na função sign, trocar a linha 551:
# public = None
# por:
# public = private if private['source'] == 'pkcs11' else None
###############################################################################
# Leitora Gemalto de R$ 25,00 compatívle com Linux
# Usando lib da A.E.T. Europe B.V., do pacote safesignidentityclient 3.0.88-12
# para Debian. Essa versão é importante para suporte ao cartão Certisign 2015
# O keyname, na minha leitora de cartão, é o label do Private Key obtido com:
# pkcs11-tool -O -l --module /usr/lib/libaetpkss.so
module = '/usr/lib/libaetpkss.so'
keyname = 'Nome da empresa:12345678901 (01/01/2016 ~ 01/01/2018)'
pin = '1234'
# Dados da NFe para assinar
chave = '12345678901234567890123456789012345678901234'
nfe = '/tmp/%s-nfe.xml' % chave # caminho para a NFe da chave acima
reference_uri = '#NFe' + chave
xml = lxml.etree.parse(nfe)
# Tags XML da assinatura conforme padrão da NFe
transforms = (xmlsec.constants.TRANSFORM_ENVELOPED_SIGNATURE,
xmlsec.constants.TRANSFORM_C14N_INCLUSIVE)
xmlsec.add_enveloped_signature(xml,
transforms=transforms,
reference_uri=reference_uri,
pos=-1)
# Especificação para usar o A3
keyname = urllib2.quote(keyname, '')
pk11_uri = 'pkcs11://%s/%s?pin=%s' % (module, keyname, pin)
# Assinando a NFe com A3
xmlsec.sign(xml, pk11_uri)
# Salvando a NFe assinada
xml.write(nfe[:-3] + '-assinada.xml',
encoding=xml.docinfo.encoding,
xml_declaration=True)
@JuniorPolegato
Copy link
Author

JuniorPolegato commented Jul 23, 2022

Olá!

No meu caso a assinatura é por NFe, e não por lote, sendo que esta tem uma chave única de 44 dígitos que pode ser consultada na Sefaz do estado emissor ou no ambiente nacional https://www.nfe.fazenda.gov.br , creio que você já deva ter acessado este para consultar alguma NFe.

Assim cada NFe (ou XML), a ser assinada tem que ter uma TAG com atributo "Id", no caso da NFe é a TAG "infNFe" que tem um atributo "Id" com conteúdo "NFe<chave_nfe_44_dígitos>".

Dessa forma, passando a "reference_uri" como sendo o conteúdo do "Id" precedido por "#" para o xmlsec, este irá procurar no XML qual TAG tem um atributo "Id" com o referido conteúdo e adicionar a TAG "Signature" após essa TAG encontrada, no caso da NFe é a TAG "infNFe".

No seu caso, se for esse padrão, sua TAG "LoteRPs" deverá ter um atributo "Id", cujo conteúdo será a "reference_uri" precedido por "#". Seria importante ver um XML desse seu assinado para se situar melhor.

No xmlsec, não me recordo bem, mas lembro que é possível especificar TAG e/ou atributo que terá o conteúdo a ser canonizado e assinado, teria que ver na documentação, se for esse seu caso de precisar assinar fora desse padrão universal que segue a NFe.

A variável "module" é a biblioteca compilada (.so no Linux ou .dll no Windows) fornecida pelo fabricante para acessar o dispositivo, onde no meu caso no Linux, leitora Gemalto e .so da A.E.T. Europe B.V (chip da leitora/cartão), no seu caso será para seu token USB, que é o mesmo que você coloca no Firefox Linux. No Windows não sei dizer.

A variável "keyname" é o nome/label do certificado exibido por esse "module", tal como eu documentei no código, obtido executando "pkcs11-tool -O -l --module /usr/lib/libaetpkss.so", no meu caso.

Fechando, fiz isso há muito tempo mais como prova de conceito de que é possível utilizar A3 no Linux, funcionou, mas nunca coloquei em produção devido a facilidade do A1 NFe e independência desse fabricantes e revendedores que te dão zero de atenção ou suporte, e também no A1 NFe posso ter vários e especificar um CPF responsável cada, podendo revogar a qualquer momento sem perdas. A3 hoje em dia é de obrigatoriedade muito específica ( https://www.certisign.com.br/certificado-digital/indicacao-uso ) e quase não usado, venho utilizando somente com e-CPF para sócios/diretores/contadores em nuvem, muito mais seguro e cada acesso fica registrado.

Abraços.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment