签署票据xml文件的引用元素格式不正确



我正在开发一个用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);

在计算签名之前,我们必须配置对所使用的数字证书信息的处理。基于这些数据,美国国税局能够验证签名,并确保在账单发送者签名后没有修改任何信息。我们必须在证书数据中加入一条条款。

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:UsersuserDo
cumentsVisual Studio
2015ProjectsassinaturaloteassinaturaloteProgram.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>

有人知道吗?任何想法都会被认为是

您的Id值(rps:8201603150148)中有一个冒号,这对于标识符属性来说是非法的。(根据https://www.w3.org/TR/xml-id/#processing,The normalized value of the attribute is an NCName...,其中"NCName"的"NC"部分为"无冒号")

由于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;
}
}

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

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:WebsitesTestWebBLebXMLSigner.cs:line 67
at ASP.ebxml_aspx.GenerateHeaderXML() in c:WebsitesTestWebebXML.aspx:line 227
at ASP.ebxml_aspx.btnSubmit_Click(Object sender, EventArgs e) in c:WebsitesTestWebebXML.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:11313112"属性添加到生成的XML中。

在我的情况下,在被困了几天后。这是Reference.uri。我把它保存为"#1〃;它不能以数字开头,我把它留在"#测试";以及要签名的XML的ID;1〃;我把它留在";测试";。

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

最新更新