EFD-Reinf: Signature Error - A Xmldocument Context is Required for Enveloped Transformations

Asked

Viewed 517 times

2

I am developing a C# application for the EFD-Reinf and at the time of sending my R-1000 event I am receiving the following message:

"A Xmldocument Context is Needed for Enveloped Transformations."

Follow my code below:

public XmlDocument Assinar(string XMLString, X509Certificate2 X509Cert)
{
    //XmlDocument XMLDoc = null;

    string x = X509Cert.GetKeyAlgorithm().ToString();

    // Create a new XML document.
    XmlDocument doc = new XmlDocument();

    // Format the document to ignore white spaces.
    doc.PreserveWhitespace = false;

    // Load the passed XML file using it's name.
    doc.PreserveWhitespace = false;

    byte[] encodedString = Encoding.UTF8.GetBytes(XMLString.Trim());
    MemoryStream ms = new MemoryStream(encodedString);
    ms.Flush();
    ms.Position = 0;

    doc.Load(ms);

    // Create a SignedXml object.
    SignedXml signedXml = new SignedXml(doc);

    // Add the key to the SignedXml document. 
    signedXml.SigningKey = X509Cert.PrivateKey;

    // Create a reference to be signed.
    Reference reference = new Reference();
    // pega o uri que deve ser assinada
    XmlAttributeCollection _Uri = doc.GetElementsByTagName("ReinfEvtInfoContriInfoContri").Item(0).Attributes;

    foreach (XmlAttribute _atributo in _Uri)
    {
        if (_atributo.Name == "id")
        {
            reference.Uri = "#" + _atributo.InnerText;
        }
    }

    // Add an enveloped transformation to the reference.
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);

    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);


    // Add an RSAKeyValue KeyInfo (optional; helps recipient find key to validate).
    KeyInfo keyInfo = new KeyInfo();
    keyInfo.AddClause(new KeyInfoX509Data(X509Cert));
    signedXml.KeyInfo = keyInfo;

    // Compute the signature.
    signedXml.ComputeSignature();

    // Get the XML representation of the signature and save
    // it to an XmlElement object.
    XmlElement xmlDigitalSignature = signedXml.GetXml();

    // Append the element to the XML document.
    doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));

    if (doc.FirstChild is XmlDeclaration)
    {
        doc.RemoveChild(doc.FirstChild);
    }

    XmlDocument XMLDoc = new XmlDocument();
    XMLDoc.PreserveWhitespace = false;
    XMLDoc = doc;

    return XMLDoc;
}

Does anyone know why I’m getting this mistake?

1 answer

1


You made some mistakes in this routine:

  • You’re just applying the transformation EnvelopedSignature but is not applying to C14N;
  • You’re not reporting on the properties SignatureMethod and DigestMethod the algorithm that should be used, which is the SHA-256;
  • And it’s not a mistake, but it’s unnecessary to create another object XmlDocument at the end of the routine, and also unnecessary to inform the property PreserveWhitespace 3 times during function.

You can do so (I copied and modified the function of this mine another answer):

public void Assinar(XmlDocument xmlDoc, X509Certificate2 certificate, string refUri)
{
   // Cria o objeto SignedXml baseado no XmlDocument passado.
   SignedXml signedXml = new SignedXml(xmlDoc);

   // Adiciona a chave privada do certificado ao documento SignedXml.
   signedXml.SigningKey = certificate.GetRSAPrivateKey();
   // O método de extensão GetRSAPrivateKey() está disponível a partir
   // do .NET Framework 4.6, se for anterior a isso, use a linha abaixo:
   //signedXml.SigningKey = certificate.PrivateKey;

   // https://docs.microsoft.com/en-us/dotnet/framework/whats-new/#Crypto462
   // The .NET Framework 4.6.2 adds support to the SignedXml class for RSA-SHA256,
   // RSA-SHA384, and RSA-SHA512 PKCS#1 signature methods, and SHA256, SHA384,
   // and SHA512 reference digest algorithms.
   signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url;        
   // O campo 'SignedXml.XmlDsigRSASHA256Url' só está disponível a partir
   // do .NET Framework 4.6.2, se for anterior a isso, use a linha abaixo:
   //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.
   refUri = String.IsNullOrEmpty(refUri) ? "" : $"#{refUri}";

   var reference = new Reference(refUri);
   reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
   reference.AddTransform(new XmlDsigC14NTransform());
   reference.DigestMethod = SignedXml.XmlDsigSHA256Url;
   // O campo 'SignedXml.XmlDsigSHA256Url' só está disponível a partir
   // do .NET Framework 4.6.2, se for anterior a isso, use a linha abaixo:
   //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.
   signedXml.KeyInfo = new KeyInfo();
   signedXml.KeyInfo.AddClause(new KeyInfoX509Data(certificate));

   // 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);
}

I chose to pass the event ID, which goes in the attribute Reference.URI, as a function parameter, which makes life much easier.

The function receives an object XmlDocument and adds the signature to that same object, so it returns nothing. If you are creating the XmlDocument from an object string, you can load it like this, before calling this function:

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

But, it may also be that this error is occurring because you are signing the wrong XML portion of the event. View these two other responses to see exactly what needs to be signed:

While you’re at it, check out these other answers, which might help you:

  • Peter, thank you for the collaboration. After using as you guided, I now get an error from "Badly formed Reference Element." in the line "signedXml.Computesignature()" You know what might be causing this error?

  • Are you using the signature function that I posted or made a change to? What are you saying in the parameter refUri? It must be exactly equal to the attribute id of the element evtInfoContri from your XML of the R-1000 event.

Browser other questions tagged

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