Gujeiros boa tarde…hehe
Estou tentando assinar um xml referênte à nota fiscal eletrônica(NF-e). Para ser mais exato, trata-se do xml da carta de correção eletrônica (CC-e).
Tenho uma aplicaçãozinha aqui que assina todos os xml da Nfe (inutilização, cancelamento, etc), mas não está assinando o da CCe (talvez pelo mal formato do arquivo xml da carta de correção).
Vou postar a classe em java que faz o processo de assinatura, postando também o XML da CCe que estou na luta pra assinar…hehe
Segue:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package assinador;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.Provider;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
*
* @author Albert Eije
*/
public class Assinador {
private static final String C14N_TRANSFORM_METHOD = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
private static final String PROVIDER_CLASS_NAME = "org.jcp.xml.dsig.internal.dom.XMLDSigRI";
private static final String PROVIDER_NAME = "jsr105Provider";
public void assinar(String arquivoXML, String arquivoCertificado,
String password, String arquivoXMLNovo, String operacao) throws Exception {
/*
operacao
'1' - NFE
'2' - CANCELAMENTO
'3' - INUTILIZACAO
*/
String tag = "";
if (operacao.equals("1")) {
tag = "infNFe";
} else if (operacao.equals("2")) {
tag = "infCanc";
} else if (operacao.equals("3")) {
tag = "infInut";
}
else if (operacao.equals("4"))
tag = "infEvento";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document docs = builder.parse(new File(arquivoXML));
NodeList elements = docs.getElementsByTagName(tag);
Element el = (Element) elements.item(0);
String id = el.getAttribute("Id");
// Cria um DOM do tipo XMLSignatureFactory que será utilizado
// para gerar a assinatura envelopada (enveloped signature)
String providerName = System.getProperty(PROVIDER_NAME,
PROVIDER_CLASS_NAME);
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM",
(Provider) Class.forName(providerName).newInstance());
// Define os algoritmos de transformação
ArrayList transformList = new ArrayList();
TransformParameterSpec tps = null;
Transform envelopedTransform = fac.newTransform(Transform.ENVELOPED,
tps);
Transform c14NTransform = fac.newTransform(C14N_TRANSFORM_METHOD, tps);
transformList.add(envelopedTransform);
transformList.add(c14NTransform);
// Cria o objeto Reference
Reference ref = fac.newReference("#" + id, fac.newDigestMethod(
DigestMethod.SHA1, null), transformList, null, null);
// Cria o elemento SignedInfo
SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
Collections.singletonList(ref));
// Carrega o KeyStore e obtem a chave do certificado
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(arquivoCertificado), password.toCharArray());
Enumeration aliasesEnum = ks.aliases();
String alias = "";
while (aliasesEnum.hasMoreElements()) {
alias = (String) aliasesEnum.nextElement();
if (ks.isKeyEntry(alias)) {
break;
}
}
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(password.toCharArray()));
// Instancia um certificado do tipo X509
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Cria o elemente KeyInfo contendo a X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
// Instancia o documento que será assinado
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(
new FileInputStream(arquivoXML));
// Cria o DOMSignContext especificando a chave e o nó pai
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement());
// Cria a XMLSignature, mas não assina ainda
XMLSignature signature = fac.newXMLSignature(si, ki);
// Empacota (marshal), gera e assina
signature.sign(dsc);
// Arquivo novo assinado
OutputStream os = new FileOutputStream(arquivoXMLNovo);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
// Encontra o elemente Signature
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS,
"Signature");
if (nl.getLength() == 0) {
throw new Exception("Não foi possível encontrar o elemente Signature");
}
// Cria um DOMValidateContext
DOMValidateContext valContext = new DOMValidateContext(
new X509KeySelector(ks), nl.item(0));
// Dsempacota (unmarshal) a XMLSignature
XMLSignature signatures = fac.unmarshalXMLSignature(valContext);
// Valida a XMLSignature.
boolean coreValidity = signatures.validate(valContext);
// Checa o status da validação
if (coreValidity == false) {
System.err.println("Falha na Assinatura!");
} else {
System.out.println("Assinatura Correta!");
}
}
public static void main(String[] args) throws Exception {
/*if (args.length != 5) {
System.out.println("Passe os cinco parâmetros necessários");
return;
}*/
String caminhoXml = "D:\\XML-LoteCCe-1.xml";
String caminhoCertificado = "D:/thiago_beserra/certificados_a1/cert_e/dzyon.pfx";
String senha = "***";
String arquivoXmlNovo = "D:\\XML-ped-inu-ass.xml";
String tipo = "4";
File file = new File(caminhoXml);
if (!file.exists()) {
System.out.println("Arquivo " + caminhoXml + " não encontrado!");
return;
}
file = new File(caminhoCertificado);
if (!file.exists()) {
System.out.println("Arquivo " + caminhoCertificado + " não encontrado!");
return;
}
try {
Assinador t = new Assinador();
t.assinar(caminhoXml, caminhoCertificado, senha, arquivoXmlNovo,
tipo);
System.out.println("Arquivo XML assinado com sucesso" + caminhoXml + "!");
} catch (Exception e) {
System.out.println("Erro ao tentar assinar arquivo XML! \n\n" + e.toString());
}
}
}
Agora o XML CCe:
<envEvento versao="1.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<idLote>000000000015255</idLote>
<evento versao="1.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<infEvento Id="ID1101103511031029073900013955001000000001105112804108">
<cOrgao>35</cOrgao>
<tpAmb>2</tpAmb>
<CNPJ>10290739000139</CNPJ>
<chNFe>35110310290739000139550010000000011051128041</chNFe>
<dhEvento>2011-03-03T08:06:00-03:00</dhEvento>
<tpEvento>110110</tpEvento>
<nSeqEvento>8</nSeqEvento>
<verEvento>1.00</verEvento>
<detEvento versao="1.00">
<descEvento>Carta de Correcao</descEvento>
<xCorrecao>Texto de teste para Carta de Correcao. Conteudo do campo xCorrecao.</xCorrecao>
<xCondUso>A Carta de Correcao e disciplinada pelo 1o-A do art. 7o do Convenio SN, de 15 de dezembro de 1970 e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; III - a data de emissao ou de saida.</xCondUso>
</detEvento>
</infEvento>
E o bendito erro que aparece na console do netbeans…hehe
Erro ao tentar assinar arquivo XML!
[Fatal Error] XML-LoteCCe-1.xml:19:1: As estruturas do documento XML devem começar e terminar com a mesma entidade.
org.xml.sax.SAXParseException; systemId: file:/D:/XML-LoteCCe-1.xml; lineNumber: 19; columnNumber: 1; As estruturas do documento XML devem começar e terminar com a mesma entidade.
CONSTRUÍDO COM SUCESSO (tempo total: 0 segundos)
Ressaltando que, o xml foi pego na web, ou seja, do jeito que baixei ele já mandei pra aplicação…=)
Creio que é problema no schema do XML que estou usando…
Se alguem tiver algum xml da CCe, fico grato.
Desde já, obrigado!