EFD-Reinf XML Signature - "Reference Element malformed."

0

I'm new to the community and I'm developing a C # application for EFD-REINF. I had a problem signing in, and I got a lot of help from Pedro in this link .

I'm still not able to send the XML from the R-1000 event to the remote production server, as I'm having problems with my signature: "Bad Reference Element." in the "signedXml.ComputeSignature ()" line . Has anyone else ever had this problem?

Basically, I created my EnvioREINF class by calling individual methods for each of the information to be sent: R-1000, R-2010, R-2020 ... (I believe there may be a simpler way to do this, however as I do not have much experience with Web Service I implemented it this way) as follows:

 //R-1000
    public bool EnvioEventoevtInfoContribuinte(Empresa objEmpresa, DateTime periodoApuracao)
    {
        try
        {

            EventosREINF auxiliar = new EventosREINF();
            _reinf.evtInfoContri = auxiliar.EnviarEvtInfoContri(objEmpresa, periodoApuracao);
            ReinfEvtInfoContri evtInfoContri = _reinf.evtInfoContri;


            //serializa o evento informações do Contribuinte
            string xmlEventoInclusao = XMLConverte.SerializaObjeto<ReinfEvtInfoContri>(evtInfoContri);

            //Exibe a lista de certificados para o usuário selecionar
            X509Certificate2 oX509Cert = BuscarCertificadoEmpresa();

            //Adiciona a assinatura digital ao evento
            XmlDocument xmlDoc = new XmlDocument();
            Assinar(xmlDoc, oX509Cert, xmlEventoInclusao);

            //XmlDocument xmlReinf = Assinar(xmlEventoInclusao, oX509Cert);

            //Inicializa o vetor de eventos do lote a ser enviado
            REINF.Model.v1_04.TArquivoeReinf[] xmlsEvento = new REINF.Model.v1_04.TArquivoeReinf[1];

            //Adicionar Arquivo XML ao array de Eventos do Lote
            REINF.Model.v1_04.TArquivoeReinf arquivoR_1000 = new REINF.Model.v1_04.TArquivoeReinf
            {
                id = objEmpresa.ToString(),
                Any = xmlDoc.DocumentElement
            };
            xmlsEvento[0] = arquivoR_1000;

            //Adiciona o xml do evento de informações do Contribuinte ao xml do evento de envio
            XmlDocument xmlDocEventoEnvio = AdicionarLoteAoEventoEnvio(xmlsEvento, objEmpresa);

            //Envia arquivo xml para o web service e obtém o xml de retorno
            XmlElement xmlRetorno = EnviarXML(xmlDocEventoEnvio, oX509Cert);
        }
        catch
        {
            throw;
        }
        return true;
    }

    //R-2010
    public bool EnvioEventoevtServTom(List<NotaFiscal> objNotasFiscais, Empresa objEmpresa, DateTime periodoApuracao)
    {
        try
        {
            ReinfEvtServTom reinfEvtServTom = new ReinfEvtServTom();

            EventosREINF auxiliar = new EventosREINF();

            _reinf.evtServTom = auxiliar.EnviarevtServTom(objNotasFiscais, objEmpresa, periodoApuracao);

            //Exibe a lista de certificados para o usuário selecionar
            X509Certificate2 oX509Cert = BuscarCertificadoEmpresa();

            //Inicializa o vetor de eventos do lote a ser enviado
            REINF.Model.v1_04.TArquivoeReinf[] xmlsEvento = ComunicacaoREINF(objNotasFiscais, oX509Cert).ToArray();

            //Adicionar Arquivo XML ao array de Eventos do Lote
            REINF.Model.v1_04.TArquivoeReinf arquivoR_2010 = new REINF.Model.v1_04.TArquivoeReinf
            {
                id = objEmpresa.ToString(),
            };
            xmlsEvento[0] = arquivoR_2010;

            //Adiciona o xml do evento de informações do Contribuinte ao xml do evento de envio
            XmlDocument xmlR2010 = AdicionarLoteAoEventoEnvio(xmlsEvento, objEmpresa);

            //Envia arquivo xml para o web service e obtém o xml de retorno
            XmlElement xmlRetorno = EnviarXML(xmlR2010, oX509Cert);

            //Converte o xml de retorno para a classe retorno de envio
            ReinfRetornoLoteEventos reinfRetornoLoteEventos = XMLConverte.DeserializaObjeto<ReinfRetornoLoteEventos>(xmlRetorno.OuterXml);


        }
        catch
        {

            throw;
        }
        return true;
    }

    //R-2020
    public bool EnvioEventoevtServPrest(List<NotaFiscal> objNotasFiscais, Empresa objEmpresa, DateTime periodoApuracao)
    {
        try
        {

            ReinfEvtServPrest reinfEvtServPrest = new ReinfEvtServPrest();

            EventosREINF auxiliar = new EventosREINF();

            _reinf.evtServPrest = auxiliar.EnviarevtServPrest(objNotasFiscais, objEmpresa, periodoApuracao);

            //Exibe a lista de certificados para o usuário selecionar
            X509Certificate2 oX509Cert = BuscarCertificadoEmpresa();

            //Inicializa o vetor de eventos do lote a ser enviado
            REINF.Model.v1_04.TArquivoeReinf[] xmlsEvento = ComunicacaoREINF(objNotasFiscais, oX509Cert).ToArray();

            //Adicionar Arquivo XML ao array de Eventos do Lote
            REINF.Model.v1_04.TArquivoeReinf arquivoR_2020 = new REINF.Model.v1_04.TArquivoeReinf
            {
                id = objEmpresa.ToString(),
            };
            xmlsEvento[0] = arquivoR_2020;

            //Adiciona o xml do evento de informações do Contribuinte ao xml do evento de envio
            XmlDocument xmlR2100 = AdicionarLoteAoEventoEnvio(xmlsEvento, objEmpresa);

            //Envia arquivo xml para o web service e obtém o xml de retorno
            XmlElement xmlRetorno = EnviarXML(xmlR2100, oX509Cert);

            //Converte o xml de retorno para a classe retorno de envio
            ReinfRetornoLoteEventos reinfRetornoLoteEventos = XMLConverte.DeserializaObjeto<ReinfRetornoLoteEventos>(xmlRetorno.OuterXml);


        }
        catch
        {

            throw;
        }
        return true;
    }

    private List<TArquivoeReinf> ComunicacaoREINF(List<NotaFiscal> objNotasFiscais, X509Certificate2 oX509Cert)
    {
        List<TArquivoeReinf> loteEnvio = new List<TArquivoeReinf>();

        foreach (NotaFiscal notaFiscal in objNotasFiscais)
        {
            ReinfLoteEventos reinfLoteEventos = new ReinfLoteEventos();

            string xmlEventoInfoContribuinte = XMLConverte.SerializaObjeto<ReinfLoteEventos>(reinfLoteEventos);

            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlEventoInfoContribuinte);


            TArquivoeReinf ObjArqXmlFilho = new TArquivoeReinf
            {
                id = notaFiscal.TipoDocumento.Id.ToString(),
                Any = xmlDoc.DocumentElement
            };

            loteEnvio.Add(ObjArqXmlFilho);
        }
        return loteEnvio;
    }

    public ReinfRetornoLoteEventos RetornoLoteEventos(ReinfRetornoLoteEventos retornoLoteEventosInicial, string xmlRetorno)
    {

        try
        {
            ReinfRetornoLoteEventos reinfRetornoLoteEventos = new ReinfRetornoLoteEventos();
            reinfRetornoLoteEventos = XMLConverte.DeserializaObjeto<ReinfRetornoLoteEventos>(xmlRetorno);


            ReinfRetornoLoteEventosRetornoEventos reinfRetornoLoteEventosRetornoEventos = new ReinfRetornoLoteEventosRetornoEventos
            {
                evento = reinfRetornoLoteEventos.retornoEventos.evento
            };


            REINF.Model.ConsultaEvento_ProducaoRestrita.ConsultaInformacoesConsolidadasRequest consultaInformacoesConsolidadasRequest = new ConsultaEvento_ProducaoRestrita.ConsultaInformacoesConsolidadasRequest
            {
                Body = new ConsultaEvento_ProducaoRestrita.ConsultaInformacoesConsolidadasRequestBody()
            };
            //consultaInformacoesConsolidadasRequest.Body.numeroInscricaoContribuinte = "";
            //consultaInformacoesConsolidadasRequest.Body.numeroProtocoloFechamento = "";
            //consultaInformacoesConsolidadasRequest.Body.tipoInscricaoContribuinte = "";

            REINF.Model.ConsultaEvento_ProducaoRestrita.ConsultasReinfClient consultasReinfClient = new ConsultaEvento_ProducaoRestrita.ConsultasReinfClient();

        }
        catch
        {
            throw;
        }
        return retornoLoteEventosInicial;
    }

    private XmlDocument AdicionarLoteAoEventoEnvio(TArquivoeReinf[] xmlsEvento, Empresa objEmpresa)
    {
        //Leiaute Mensagem Entrada
        ReinfLoteEventos reinfLoteEventos = new ReinfLoteEventos
        {
            evento = new TArquivoeReinf[1]
        };
        reinfLoteEventos.evento = xmlsEvento;

        //Serializa objeto evento de envio
        string xmlEventoEnvio = XMLConverte.SerializaObjeto<ReinfLoteEventos>(reinfLoteEventos);

        //Converte xml do evento envio para XMLDocumente que será transmitido para o web service
        XmlDocument xmlDocEventoEnvio = XMLConverte.ConverterXMLParaXMLDocument(xmlEventoEnvio);

        return xmlDocEventoEnvio;
    }

    private static string UTF8ByteArrayToString(byte[] characters)
    {
        UTF8Encoding encoding = new UTF8Encoding();
        string constructedString = encoding.GetString(characters);
        return (constructedString);
    }

    public void Assinar(XmlDocument xmlDoc, X509Certificate2 X509Cert, string xmlEventoInclusao)
    {


        // Cria o objeto SignedXml baseado no XmlDocument passado.
        SignedXml signedXml = new SignedXml(xmlDoc);
        signedXml.SigningKey = X509Cert.PrivateKey;
        signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

        // Checa se foi informado um URI para a referência, se foi acrescenta o "#" no começo.
        xmlEventoInclusao = String.IsNullOrEmpty(xmlEventoInclusao) ? "" : $"#{xmlEventoInclusao}";

        Reference reference = new Reference(xmlEventoInclusao);
        reference.Id = _reinf.evtInfoContri.id;
        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        reference.AddTransform(new XmlDsigC14NTransform());
        reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";

        signedXml.AddReference(reference);

        // Carrega o certificado em um objeto KeyInfoX509Data e o adiciona ao objeto KeyInfo.
        KeyInfo KeyInfo = new KeyInfo();
        KeyInfo.AddClause(new KeyInfoX509Data(X509Cert));
        signedXml.KeyInfo = KeyInfo;

        // Calcula a assinatura.
        signedXml.ComputeSignature();

        // Obtém a representação XML da assinatura e a armazena em um objeto XmlElement.
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        // Acrescenta o elemento ao documento XML.
        xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));

        // Se o primeiro nó do documento for o nó de declaração XML
        // '<?xml version="1.0" encoding="utf-8"?>', remove ele.
        if (xmlDoc.FirstChild is XmlDeclaration)
            xmlDoc.RemoveChild(xmlDoc.FirstChild);
    }



    private X509Certificate2 BuscarCertificadoEmpresa()
    {
        X509Certificate2 cert = null;
        ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;

        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
        X509Certificate2Collection certcollection = (X509Certificate2Collection)store.Certificates;
        // pick a certificate from the store
        cert = X509Certificate2UI.SelectFromCollection(certcollection,
                "Autenticação do Certificado",
                "Informe um certificao válido", X509SelectionFlag.SingleSelection)[0];

        store.Close();

        return cert;
    }

    private XmlElement EnviarXML(XmlDocument xmlDocEventoEnvio, X509Certificate2 oX509Cert)
    {
        XmlElement xmlResult = null;

        var urlServicoEnvio = @"https://preprodefdreinf.receita.fazenda.gov.br/wsreinf/RecepcaoLoteReinf.svc";
        var address = new EndpointAddress(urlServicoEnvio);
        var binding = new BasicHttpsBinding();

        // Informa que será usado um certificado digital para acessar o serviço.
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

        // Cria o objeto cliente (do tipo System.ServiceModel.ClientBase) para acesso ao WebService.
        var recepcaoLoteReinfClient = new RecepcaoLoteReinfClient(binding, address);

        // Passa o certificado digital para o objeto do tipo System.ServiceModel.ClientBase.
        recepcaoLoteReinfClient.ClientCredentials.ClientCertificate.Certificate = oX509Cert;

        // Chama o WebService de fato, passando o XML do lote.
        recepcaoLoteReinfClient.Open();

        // O método espera um objeto do tipo XElement, e retorna outro objeto XElement.
        //xmlResult = recepcaoLoteReinfClient.ReceberLoteEventos(xmlDoc.DocumentElement);
        var retornoEnvioXElement = recepcaoLoteReinfClient.ReceberLoteEventos((XElement.Parse(xmlDocEventoEnvio.OuterXml)));
        recepcaoLoteReinfClient.Close();

        return xmlResult;

    }

    private bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        // If the certificate is a valid, signed certificate, return true.
        if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
        {
            return true;
        }

        Console.WriteLine("X509Certificate [{0}] Policy Error: '{1}'",
            certificate.Subject,
            sslPolicyErrors.ToString());

        return false;
    }

EDITION

The XML that is being generated is hidden by hiding the personal information:

<?xml version="1.0" encoding="utf-8"?>
<Reinf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.reinf.esocial.gov.br/schemas/envioLoteEventos/v1_04_00">
  <evtInfoContri id="ID1000000000000002018121815063800001" xmlns="http://www.w3.org/2000/09/xmldsig#">
    <ideEvento>
      <tpAmb>2</tpAmb>
      <procEmi>1</procEmi>
      <verProc>1.0.0.0</verProc>
    </ideEvento>
    <ideContri>
      <tpInsc>1</tpInsc>
      <nrInsc>00000000000000</nrInsc>
    </ideContri>
    <infoContri>
      <inclusao>
        <idePeriodo>
          <iniValid>2018-12</iniValid>
        </idePeriodo>
        <infoCadastro>
          <classTrib>1</classTrib>
          <indEscrituracao>0</indEscrituracao>
          <indDesoneracao>0</indDesoneracao>
          <indAcordoIsenMulta>0</indAcordoIsenMulta>
          <contato>
            <nmCtt>Nome Contato</nmCtt>
            <cpfCtt>00000000000</cpfCtt>
            <foneFixo>0000000000</foneFixo>
          </contato>
          <softHouse>
            <cnpjSoftHouse>00000000000000</cnpjSoftHouse>
            <nmRazao>Empresa</nmRazao>
            <nmCont>Contato</nmCont>
          </softHouse>
        </infoCadastro>
      </inclusao>
    </infoContri>
  </evtInfoContri>
</Reinf>
    
asked by anonymous 15.12.2018 / 23:44

1 answer

0

I think the problem is in this line of the Assinar function:

    reference.Id = _reinf.evtInfoContri.id;

Apparently you did not put the code where you load the value of this _reinf.evtInfoContri.id field, but as I said in my other answer , the reference.Id property should start with "#" , when there is value, and this entered ID must also exist in the document being signed.

So, probably this line should look like this:

    reference.Id = "#" + _reinf.evtInfoContri.id;

But I suggest you pass this ID as a parameter of the signing function, as I suggested in the other answer, because this way you did, you can not use the same signing function for the other events, and everyone needs be signed.

Another suggestion: It would be interesting to try to create a unique function for the 3 events you are dealing with (R-1000, R-2010 and R-2020), because the procedure for all events is almost identical. The code in general is also somewhat confusing, even with variable names that do not match what they are storing, it would be nice to give a revised one.

And one last suggestion: Do not use blocks try / catch only to execute a throw , this is no good, see this answer: Do I need to use try / catch throughout a process's chain? - Stack Overflow in English .

    
17.12.2018 / 00:50