格式错误的参考元素签署了账单的xml文件

时间:2017-01-18 13:43:14

标签: c# xml sign x509certificate2 xml-signature

我正在开发一个程序,用于在xml中对发票进行数字签名。我按照本指南https://www.profissionaisti.com.br/2010/07/assinando-digitalmente-um-xml-usando-c/#comment-197297进行了操作。但是,我收到错误的格式错误的参考元素。 代码是:

static void Main(string[] args)
    {
        //open certificates of current user
        var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly);

        //Open screen to choose certificate
        var selectedCertificate = X509Certificate2UI.SelectFromCollection(
            store.Certificates,
            "Title",
            "MSG",
            X509SelectionFlag.SingleSelection);

        //Gets the x509 object of the selected certificate
        foreach (X509Certificate2 x509 in selectedCertificate)
        {
            try
            {

                //==============================
                // Start reading xml files
                //==============================
                var txtFiles = Directory.EnumerateFiles("./", "*.xml");

                foreach (string currentFile in txtFiles)
                {

                    Console.WriteLine("Reading file " + currentFile + ":");
                    var originalDoc = XDocument.Load(currentFile);
                    XmlDocument doc = DocumentExtensions.ToXmlDocument(originalDoc);

                    //==============================
                    // Start reading bills
                    //==============================

获取xml节点InfRps,它是xml中帐单的表示形式:

                    XmlNodeList ListInfRps = doc.GetElementsByTagName("InfRps");

                    int NodeCounter = 1;

                    foreach (XmlElement InfRps in ListInfRps)
                    {

.NET框架的命名空间System.Security.Cryptography.Xml上有一个名为SignedXml的类,它实现了W3C标准,用于签名文档和验证签名文档。下面的代码启动了这个类。

                        string id = InfRps.Attributes.GetNamedItem("Id").Value;
                        SignedXml signedXml = new SignedXml(InfRps);
                        signedXml.SigningKey = x509.PrivateKey;

根据IRS,XML必须在处理之前以规范形式放置。类Reference负责处理过程的这一部分,包括节点infNFE的识别和所需的转换:

                        // Transformation for DigestValue
                        Reference reference = new Reference("#" + id);
                        //Reference reference = new Reference("#" + "lote");

                        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
                        reference.AddTransform(new XmlDsigC14NTransform());
                        signedXml.AddReference(reference);

在计算签名之前,我们必须配置对所使用的数字证书信息的处理。根据这些数据,IRS能够验证签名,并确保在账单发送者签署之后没有任何信息被修改。我们必须包含一个包含证书数据的条款。

                        KeyInfo keyInfo = new KeyInfo();
                        keyInfo.AddClause(new KeyInfoX509Data(x509));
                        signedXml.KeyInfo = keyInfo;

现在,我们应该计算签名:这是错误。

                        signedXml.ComputeSignature();

如果计算签名有效,我们可以在xml上创建元素签名:

                        XmlElement xmlSignature = doc.CreateElement("Signature", "http://www.w3.org/2000/09/xmldsig#");

                        XmlAttribute attr = doc.CreateAttribute("Id");
                        attr.Value = "Ass_" + id;

                        //Add the attribute to the node     
                        xmlSignature.Attributes.SetNamedItem(attr);

                        XmlElement xmlSignedInfo = signedXml.SignedInfo.GetXml();
                        XmlElement xmlKeyInfo = signedXml.KeyInfo.GetXml();

                        XmlElement xmlSignatureValue = doc.CreateElement("SignatureValue", xmlSignature.NamespaceURI);
                        string signBase64 = Convert.ToBase64String(signedXml.Signature.SignatureValue);
                        XmlText text = doc.CreateTextNode(signBase64);
                        xmlSignatureValue.AppendChild(text);

                        xmlSignature.AppendChild(doc.ImportNode(xmlSignedInfo, true));
                        xmlSignature.AppendChild(xmlSignatureValue);
                        xmlSignature.AppendChild(doc.ImportNode(xmlKeyInfo, true));

                        XmlNodeList ListRps = doc.GetElementsByTagName("Rps");

                        int RpsCounter = 1;

                        foreach (XmlElement Rps in ListRps)
                        {

                            if (RpsCounter == NodeCounter)
                            {
                                Rps.AppendChild(xmlSignature);
                            }
                            RpsCounter++;
                        }

                        Console.WriteLine("Ok");
                        NodeCounter++;
                    }
                    (...)

我收到CryptographicException:格式错误的引用元素:

System.Security.Cryptography.CryptographicException: Malformed reference element.
at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument
document, CanonicalXmlNodeList refList)
at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences()
at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
at escolhercertificadosimples.Program.Main(String[] args) in
C:\Users\user\Do
cuments\Visual Studio
2015\Projects\assinaturalote\assinaturalote\Program.cs:line 143
Pressione qualquer tecla para continuar. . .

要签名的xml示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<EnviarLoteRpsEnvio xmlns="http://www.abrasf.org.br/nfse.xsd">
    <LoteRps Id="lote" versao="1.00">
        <NumeroLote>8</NumeroLote>
        <Cnpj>09419261123115</Cnpj>
        <InscricaoMunicipal>51624621</InscricaoMunicipal>
        <QuantidadeRps>1</QuantidadeRps>
        <ListaRps>
            <Rps xmlns="http://www.abrasf.org.br/nfse.xsd">
                <InfRps Id="rps:8201603150148">
                    <IdentificacaoRps>
                        <Numero>8201613150148</Numero>
                        <Serie>248</Serie>
                        <Tipo>2</Tipo>
                    </IdentificacaoRps>
                    <DataEmissao>2016-03-15T18:18:39</DataEmissao>
                    <NaturezaOperacao>1</NaturezaOperacao>
                    <OptanteSimplesNacional>2</OptanteSimplesNacional>
                    <IncentivadorCultural>2</IncentivadorCultural>
                    <Status>1</Status>
                    <Servico>
                        <Valores>
                            <ValorServicos>20.00</ValorServicos>
                            <ValorDeducoes>0.00</ValorDeducoes>
                            <ValorPis>1.60</ValorPis>
                            <ValorCofins>2.00</ValorCofins>
                            <ValorInss>0.00</ValorInss>
                            <ValorIr>3.00</ValorIr>
                            <ValorCsll>2.00</ValorCsll>
                            <IssRetido>1</IssRetido>
                            <OutrasRetencoes>0.00</OutrasRetencoes>
                            <DescontoIncondicionado>0.00</DescontoIncondicionado>
                            <DescontoCondicionado>0.00</DescontoCondicionado>
                        </Valores>
                        <ItemListaServico>1.07</ItemListaServico>
                        <CodigoTributacaoMunicipio>10700100</CodigoTributacaoMunicipio>
                        <Discriminacao>test.</Discriminacao>
                        <CodigoMunicipio>4314902</CodigoMunicipio>
                    </Servico>
                    <Prestador>
                        <Cnpj>09419261000115</Cnpj>
                        <InscricaoMunicipal>51624621</InscricaoMunicipal>
                    </Prestador>
                    <Tomador>
                        <IdentificacaoTomador>
                            <CpfCnpj>
                                <Cnpj>14525684000150</Cnpj>
                            </CpfCnpj>
                        </IdentificacaoTomador>
                        <RazaoSocial>test S.A.</RazaoSocial>
                        <Endereco>
                            <Endereco>Rua test</Endereco>
                            <Numero>83</Numero>
                            <Complemento>Sala test</Complemento>
                            <Bairro>Centro</Bairro>
                            <CodigoMunicipio>3304557</CodigoMunicipio>
                            <Uf>RJ</Uf>
                            <Cep>20091007</Cep>
                        </Endereco>
                        <Contato>
                            <Telefone>2136261100</Telefone>
                            <Email>test@test.com.br</Email>
                        </Contato>
                    </Tomador>
                </InfRps>
            </Rps>
        </ListaRps>
    </LoteRps>
</EnviarLoteRpsEnvio>

有人有任何想法吗?任何想法都是apreciated

3 个答案:

答案 0 :(得分:2)

您的Id值(rps:8201603150148)中有冒号,这对于标识符属性是非法的。 (按https://www.w3.org/TR/xml-id/#processingThe normalized value of the attribute is an NCName...,其中&#34; NC&#34;部分&#34; NCName&#34;是&#34;无冒号&#34;)

由于在Id值中有一个冒号,SignedXml不会解决它,所以它表示你的参考指向无处。

如果您正在编写新代码并且对您的(技术上格式错误的)标识符属性值没有要求,那么最好的答案就是摆脱冒号(下划线通常在此角色中很好用)。

由于符合xml:id约束使得最具互操作性的文档成为最佳答案。

下一个最佳答案是扩展SignedXml类并覆盖GetIdElement。你应该尽可能严格地使你的匹配逻辑。请注意,必须在签名者和接收者身上执行此逻辑...并且只有双方都能够接受松散的一致性文档才有效。

internal class RpsSignedXml : SignedXml
{
    // ctors and other members as appropriate

    public override XmlElement GetIdElement(XmlDocument document, string idValue)
    {
        if (document == null)
            return null;
        if (string.IsNullOrEmpty(idValue))
            return null;

        if (!idValue.StartsWith("rps:"))
            return base.GetIdElement(document, idValue);

        string xPath = $"//InfRps[@Id=\"{idValue}\"]";
        XmlNodeList nodeList = document.SelectNodes(xPath);

        if (nodeList == null || nodeList.Count != 1)
            return null;

        return nodeList[0] as XmlElement;
    }
}

答案 1 :(得分:1)

我有类似的问题。我得到了这个例外:

Unable to resolve Uri cid:Part-0986dfc9-2748-41a4-8624-e505e98b29be.
System.Security
   at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList)
   at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences()
   at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
   at EmpTestWeb.BL.ebXMLSigner.ComputeSig(X509Certificate2 cert, XmlDocument doc, String externalReferenceUri) in C:\Websites\TestWeb\BL\ebXMLSigner.cs:line 67
   at ASP.ebxml_aspx.GenerateHeaderXML() in c:\Websites\TestWeb\ebXML.aspx:line 227
   at ASP.ebxml_aspx.btnSubmit_Click(Object sender, EventArgs e) in c:\Websites\TestWeb\ebXML.aspx:line 89

如果我尝试使用这个类,它没有用:

class ebXMLSignedXml : SignedXml
{
    // ctors and other members as appropriate
    public ebXMLSignedXml(XmlDocument doc) : base(doc) { }

    public override XmlElement GetIdElement(XmlDocument document, string idValue)
    {
        if (document == null)
            return null;
        if (string.IsNullOrEmpty(idValue))
            return null;

        if (!idValue.StartsWith("cid:"))
            return base.GetIdElement(document, idValue);

        string xPath = $"//InfRps[@Id=\"{idValue}\"]";
        XmlNodeList nodeList = document.SelectNodes(xPath);

        if (nodeList == null || nodeList.Count != 1)
            return null;

        return nodeList[0] as XmlElement;
    }
}

它永远不会到达应该使用Id="cid:...."

选择元素的位置

<强>解

我最终传递了内容流而不是URI,并手动将URI =“cpa:13131312”属性添加到生成的XML中。

答案 2 :(得分:1)

以我为例,几天之后就被困了。这是Reference.uri。我将其保存为“#1”,但不能以数字开头,我将其保留在“ #test”以及要签名的XML的ID中。我有“ 1”,并留在“测试”上。

https://stackoverflow.com/a/64154967/1536197