2017-01-18 9 views
0

xmlで請求書に電子署名するプログラムを開発しています。私はこのガイドhttps://www.profissionaisti.com.br/2010/07/assinando-digitalmente-um-xml-usando-c/#comment-197297に従った。しかし、私は誤った形式の参照要素を取得しています。 コードです:請求書のxmlファイルに署名する不正な参照要素

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での法案の表現であるXMLノードInfRpsをゲット:

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

        int NodeCounter = 1; 

        foreach (XmlElement InfRps in ListInfRps) 
        { 

の名前空間System.Security.Cryptography.Xml上のクラスがありますSignedXmlという.NETフレームワークは、ドキュメントの署名と署名付きドキュメントの検証のためのW3C標準を実装しています。以下のコードは、このクラスを開始します。

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

IRSによれば、XMLは処理前に標準形式にする必要があります。クラスリファレンスノード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:不正な参照要素:XMLの

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 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>[email protected]</Email> 
         </Contato> 
        </Tomador> 
       </InfRps> 
      </Rps> 
     </ListaRps> 
    </LoteRps> 
</EnviarLoteRpsEnvio> 

誰もが考えている?任意のアイデアが評価される

答えて

2

ID属性(rps:8201603150148)にコロンがありますが、これは識別子属性には不正です。

ID値にコロンがあるため、SignedXmlは解決しないため、リファレンスポイントがどこにもないことを示しています(この場合、「NCName」の「NC」部分は「ノーコロン」です。、The normalized value of the attribute is an NCName...) 。

新しいコードを記述していて、(技術的に不正な形式の)識別子属性値を必要としない場合は、コロンを取り除くことをお勧めします(アンダースコアは通常、この役割でうまく機能します)。

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; 
    } 
} 
+0

です。私は何かを考えなければならない。助けてくれてありがとう! – giancarloap

+0

回避策はありますか?まだそれを探しています。 – giancarloap

+0

答えを編集して、 'GetIdElement'を上書きする例を追加しました。 – bartonjs

関連する問題