EFD-Reinf XML Signature - "Badly formed Reference Element."

Asked

Viewed 246 times

1

I’m new to the community and I’m developing a C# application for EFD-REINF. I had a problem with the subscription and I received a great help from Pedro in this link.

I’m still not able to send XML from the R-1000 event to the remote production server, as I’m having problems with my signature: "Poorly formed Reference Element." in line "signedXml.Computesignature()". Someone else has been through this trouble?

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, but since I don’t 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

Follow the XML that is being generated by hiding 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>

1 answer

1


I believe the problem lies in this line of the function Assinar:

    reference.Id = _reinf.evtInfoContri.id;

Apparently you didn’t put the code where you carry the value of this field _reinf.evtInfoContri.id, but, as I said in my other reply, the property reference.Id should start with "#", when there is value, and this informed ID must also exist in the document being signed.

So, probably this line should stay like this:

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

But, I suggest you pass this ID as a signature function parameter, as I suggested in the other answer, because this way you did, you won’t be able to use the same signature function for the other events, and they all need to be signed.

Another suggestion: It would be interesting to try to create a single function for the 3 events you are dealing with (R-1000, R-2010 and R-2020), because the procedure for all events is virtually identical. The code in general is also confused, including with variable names that do not correspond to what they are storing, it would be nice to have a review.

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

  • Hi Pedro, thank you so much for your help. The "_reinf.evtInfoContri.id" field is actually a function I created to pick up the response from the "evtInfoContri.id" field where I followed the advice of your other answer. I added the "#" and I still continue with that problem. I don’t know if it has anything to do with checking the posts about Reinf, my XML is containing the xmlns:xsi and xmlns:xsd elements, which are usually automatically added by the serializer and I don’t know how to remove this from my XML.

  • About the reference.Id: But does this ID you are reporting exist in XML? Post your XML in the question, and the value being reported in the field reference.Id, when the error occurs. About the attributes xmlns:xsiand xmlns:xsd, see item 3 of that reply: C# - EFD Reinf v1_04_00 - How to instantiate and fill all Event properties?.

  • I saw the XML you are trying to send. The value you are reporting in the field reference.Id is "#ID100000000000002018121809024900001"? It should be this (although he is wrong, see item 2 of that answer to see how this ID should be composed)(or did you reset the start of the ID intentionally to not show the company’s CNPJ? ). I also saw that you’re putting the attribute xmlns in the elements <ideEvento>, <ideContri> and <infoContri>, but this attribute should only appear in the root element <Reinf>.

  • I believe the value in Reset. Id is correct as I followed the rule of the orientation manual. About XML, I got some advances yesterday and will post the new XML now. However I’m still having problems related to my Ference.URI

  • But what is the exact amount you are reporting on reference.Id? It needs to be exactly the value that is in the attribute id of the element <evtInfoContri>, added character "#" at first.

  • Actually I followed your guidance as you guided me in that other post; I’m taking the result of the reinf.evtInfoContri.id field (ID+1+CNPJ+AAAAMMDDHHMMSS+00001) and adding # at the beginning. I have now checked the documentation and it says that the last fields would be the sequential number of the key, but I am increasing with 00001. This is correct?

  • Yeah, actually that last part, the sequential number, is just to make sure the ID will be unique. Suppose on 12/19/2018, at 10:50:31, you sent 3 events. Each one must have a unique ID, so the first would have final 00001, the second 00002 and the third 00003. And even reporting on reference.Id exactly the same ID that is in XML, preceded by #, you are receiving the same error, from "Badly formed Reference element"?

  • Yes, I want to send only the R-1000 event for testing, but in the signature part that calculates the signature I get this error from Visual Studio. Is it necessary to inform the Referral.URI? If so, what is the value of this field?

  • In the case of eSocial reference.Id should not be informed, and then the signature is made throughout the XML document, but in the EFD-Reinf it seems necessary to inform the reference.Id, and in such a case shall be informed "#" + {Atributo ID do elemento 'evtInfoContri'}. But his element <evtInfoContri> should not have an attribute either xmlns, let alone referencing the schema of XML Signature: "http://www.w3.org/2000/09/xmldsig#".

  • I understood, I also realized that my XML is not calling the loteEventos, from Reinf it already goes straight to the <evtInfoContri>. I guess that’s getting in the way of my routine, too.

  • No, that’s right. This XML is the event, the element loteEventos is from the batch XML. Then you need to take this event XML and embed it in batch XML.

  • @User1012, did my answer answer your question? If you have answered it, please mark it as accepted by clicking on the visa sign ( ). But do this only when any answer has answered your original question. When you reach 15 reputation points you can also vote in favour of an answer or question. See: Someone answered me and Why vote?. Also do the [tour], for an initial explanation of how the site works.

  • Hi Pedro, answered yes my dear. I clicked on visa more still need to achieve 15 pts of reputation :(

Show 8 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.