SignedXml и несколько XMLDSIGN.
От: alex303  
Дата: 26.08.15 11:10
Оценка:
Приветствую.
если в xml-е несколько узлов подписано несколькими подписями (всмысле не один Signature + много Referencе'ов, а много Signature). То наблюдается крайне странная картина: если узел Signature является дочерним узлом по отношению к подписываемому, то валидируется только первая подпись — остальные якобы невалидны. Если же узел Signature вынести за пределы подписываемого узла, то валидируются все подписи.
Воспрос — это баг или фича? Куда копать.

простенький пример.

using System;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace MultiSign
{
    class Program
    {
        private static readonly RSACryptoServiceProvider KEY = new RSACryptoServiceProvider();

        static void Main(string[] args)
        {
            var doc = MakeDocument();
            SignNodes(doc, "//test");
            // doc.Save("test_signed.xml");
            Console.WriteLine("Verifing document:");
            VerifyDocument(doc);
            Console.WriteLine("Verifing nodes:");
            VerifyNodesAsSingleDocument(doc, "//test");
            Console.ReadLine();
        }

        private static XmlDocument MakeDocument()
        {
            var doc = new XmlDocument() { PreserveWhitespace = true };
            doc.LoadXml("<root>"
                        + "  <sub>"
                        + "    <test id='1'>"
                        + "      <data>data1</data>"
                        + "    </test>"
                        + "  </sub>"
                        + "  <sub>"
                        + "    <test id='2'>"
                        + "      <data>data2</data>"
                        + "    </test>"
                        + "  </sub>" 
                        + "  <sub>" 
                        + "    <test id='3'>" 
                        + "      <data>data3</data>" 
                        + "    </test>" 
                        + "  </sub>" 
                        + "</root>");
            return doc;
        }

        private static void SignNodes(XmlDocument doc, string xpath)
        {
            var nodes = doc.SelectNodes(xpath);
            foreach (XmlNode node in nodes)
            {
                SignNode(node);
            }
        }

        private static void SignNode(XmlNode node)
        {
            var doc = node.OwnerDocument;
            var signedXml = new SignedXml(doc) { SigningKey = KEY };
            var reference = new Reference { Uri = "#" + node.Attributes["id"].Value };
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            signedXml.AddReference(reference);
            signedXml.ComputeSignature();

            node.AppendChild(doc.ImportNode(signedXml.GetXml(), true)); // add signature as child of signed node. Will fail on second+ signatures
            //doc.DocumentElement.AppendChild(doc.ImportNode(signedXml.GetXml(), true)); // add signature somewhere else. All works good.
        }

        private static void VerifyDocument(XmlDocument doc)
        {
            var signs = doc.GetElementsByTagName("Signature");
            foreach (XmlElement sign in signs)
            {
                var signedXml = new SignedXml(doc);
                signedXml.LoadXml(sign);
                Console.WriteLine("verified: '{0}'", signedXml.CheckSignature(KEY));
            }
        }

        private static void VerifyNodesAsSingleDocument(XmlDocument doc, string xpath)
        {
            var nodes = doc.SelectNodes(xpath);
            foreach (XmlNode node in nodes)
            {
                var tempDoc = new XmlDocument();
                tempDoc.AppendChild(tempDoc.ImportNode(node, true));
                VerifyDocument(tempDoc);
            }
        }
    }
}
Re: SignedXml и несколько XMLDSIGN.
От: GlebZ Россия  
Дата: 26.08.15 16:28
Оценка:
Здравствуйте, alex303, Вы писали:

A>Приветствую.

A>если в xml-е несколько узлов подписано несколькими подписями (всмысле не один Signature + много Referencе'ов, а много Signature). То наблюдается крайне странная картина: если узел Signature является дочерним узлом по отношению к подписываемому, то валидируется только первая подпись — остальные якобы невалидны. Если же узел Signature вынести за пределы подписываемого узла, то валидируются все подписи.
A>Воспрос — это баг или фича? Куда копать.

Подписи в контейнере могут быть множественными, а могут быть заверяемыми. В случае если signature не в руте, то она попадает в подписываемые данные. Следующая подпись — заверяет предыдущую. Если нет, то она пропускается. Если хочешь сделать по уму — то копай XADES. А так:

An important scenario would be a document requiring two enveloped signatures. Each signature must omit itself from its own digest calculations, but it is also necessary to exclude the second signature element from the digest calculations of the first signature so that adding the second signature does not break the first signature.

http://www.w3.org/TR/xmldsig-core/
Re[2]: SignedXml и несколько XMLDSIGN.
От: alex303  
Дата: 27.08.15 09:23
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Подписи в контейнере могут быть множественными, а могут быть заверяемыми. В случае если signature не в руте, то она попадает в подписываемые данные. Следующая подпись — заверяет предыдущую. Если нет, то она пропускается. Если хочешь сделать по уму — то копай XADES.


Причем тут XADES не понятно, это всего лишь доп.фичи поверх классической xmldsig.
Что касается "по уму", то от меня тут ничего не зависит. Работаем с тем что суют, а суют формат гисгмп =)

GZ>

GZ>An important scenario would be a document requiring two enveloped signatures. Each signature must omit itself from its own digest calculations, but it is also necessary to exclude the second signature element from the digest calculations of the first signature so that adding the second signature does not break the first signature.

GZ>http://www.w3.org/TR/xmldsig-core/

Честно говоря я не понимаю, что именно из этого абзаца нарушает мой пример. Узлы по которым считаются подписи никак не пересекаются. Добавление подписи как дочернего узла подписываемого элемента прекрасно работает, когда эта подпись единственная.
Re: SignedXml и несколько XMLDSIGN.
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 27.08.15 16:18
Оценка: 2 (1)
Здравствуйте, alex303, Вы писали:

A>Воспрос — это баг или фича? Куда копать.

Меня заинтересовал ваш пример, я немного покопался в нем — пока не готов 100% сказать, что это баг, но склоняюсь к этому.
В общем, первым делом я включил максимальный уровень логирования механизма подписания и проверки подписи.

Это можно сделать, добавив app.config примерно такого содержания:
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Security.Cryptography.Xml.SignedXml" switchValue="Verbose" >
        <listeners>
          <add name="myListener" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="TextWriterOutput.log" />
    </sharedListeners>
    <trace autoflush="true" indentsize="4">
    </trace>
  </system.diagnostics>
</configuration>


На проверку там падают вот такие строки:
  Это для проверки внутри документа (VerifyDocument()) для 1-ой подписи
System.Security.Cryptography.Xml.SignedXml Information: 1 : [SignedXml#025019b6, BeginCheckSignatureFormat] Checking signature format using format validator "[System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a] System.Security.Cryptography.Xml.SignedXml.DefaultSignatureFormatValidator".
System.Security.Cryptography.Xml.SignedXml Information: 6 : [SignedXml#025019b6, FormatValidationResult] Signature format validation was successful.
System.Security.Cryptography.Xml.SignedXml Information: 2 : [SignedXml#025019b6, BeginCheckSignedInfo] Checking signature on SignedInfo with id "(null)".
System.Security.Cryptography.Xml.SignedXml Information: 7 : [SignedXml#025019b6, NamespacePropagation] Propagating namespace xmlns="".
System.Security.Cryptography.Xml.SignedXml Information: 0 : [SignedXml#025019b6, BeginCanonicalization] Beginning canonicalization using "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" (XmlDsigC14NTransform).
System.Security.Cryptography.Xml.SignedXml Verbose: 0 : [SignedXml#025019b6, BeginCanonicalization] Canonicalization transform is using resolver System.Xml.XmlSecureResolver and base URI "".
System.Security.Cryptography.Xml.SignedXml Verbose: 5 : [SignedXml#025019b6, CanonicalizedData] Output of canonicalization transform: <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#1"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>58LTUmMo7jINPsX0dpxCrWnm8/s=</DigestValue></Reference></SignedInfo>
System.Security.Cryptography.Xml.SignedXml Information: 14 : [SignedXml#025019b6, VerifySignedInfo] Verifying SignedInfo using key RSACryptoServiceProvider#00d0fbf7, signature description RSAPKCS1SHA1SignatureDescription, hash algorithm SHA1CryptoServiceProvider, and asymmetric signature deformatter RSAPKCS1SignatureDeformatter.
System.Security.Cryptography.Xml.SignedXml Verbose: 14 : [SignedXml#025019b6, VerifySignedInfo] Actual hash value: a87782e1e25b3f96244bd9301271c0852d792a5e
System.Security.Cryptography.Xml.SignedXml Verbose: 14 : [SignedXml#025019b6, VerifySignedInfo] Raw signature: 683706e93b45c5f19371eb08c900a3133fa9c7586866fe0e55cbac0c768488a8cd6c377d27344c561f55b6440fb811546ca80d4b6b6c3472395cc53dd38c376ec0687363babdc8b24651f73ceb6449cc195e7a7f64e1aafff497db9a46915c26beaff958fe5d1425ebf6539dbf4e41364953d3ecf8197ba63fa5f9aab0e77428
System.Security.Cryptography.Xml.SignedXml Verbose: 13 : [SignedXml#025019b6, VerifyReference] Processing reference Reference#029552b9, Uri "#1", Id "", Type "".
System.Security.Cryptography.Xml.SignedXml Verbose: 8 : [Reference#029552b9, ReferenceData] Transformed reference contents: <test id="1">      <data>data1</data>    </test>
System.Security.Cryptography.Xml.SignedXml Verbose: 13 : [SignedXml#025019b6, VerifyReference] Reference Reference#029552b9 hashed with "http://www.w3.org/2000/09/xmldsig#sha1" (SHA1CryptoServiceProvider) has hash value e7c2d3526328ee320d3ec5f4769c42ad69e6f3fb, expected hash value e7c2d3526328ee320d3ec5f4769c42ad69e6f3fb.
System.Security.Cryptography.Xml.SignedXml Information: 9 : [SignedXml#025019b6, SignatureVerificationResult] Verification with key RSACryptoServiceProvider#00d0fbf7 was successful.

  А это для второй
System.Security.Cryptography.Xml.SignedXml Information: 1 : [SignedXml#00ea206d, BeginCheckSignatureFormat] Checking signature format using format validator "[System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a] System.Security.Cryptography.Xml.SignedXml.DefaultSignatureFormatValidator".
System.Security.Cryptography.Xml.SignedXml Information: 6 : [SignedXml#00ea206d, FormatValidationResult] Signature format validation was successful.
System.Security.Cryptography.Xml.SignedXml Information: 2 : [SignedXml#00ea206d, BeginCheckSignedInfo] Checking signature on SignedInfo with id "(null)".
System.Security.Cryptography.Xml.SignedXml Information: 7 : [SignedXml#00ea206d, NamespacePropagation] Propagating namespace xmlns="".
System.Security.Cryptography.Xml.SignedXml Information: 0 : [SignedXml#00ea206d, BeginCanonicalization] Beginning canonicalization using "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" (XmlDsigC14NTransform).
System.Security.Cryptography.Xml.SignedXml Verbose: 0 : [SignedXml#00ea206d, BeginCanonicalization] Canonicalization transform is using resolver System.Xml.XmlSecureResolver and base URI "".
System.Security.Cryptography.Xml.SignedXml Verbose: 5 : [SignedXml#00ea206d, CanonicalizedData] Output of canonicalization transform: <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#2"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>b1p+90+Cy63cDc0WCh1S7xv8PNk=</DigestValue></Reference></SignedInfo>
System.Security.Cryptography.Xml.SignedXml Information: 14 : [SignedXml#00ea206d, VerifySignedInfo] Verifying SignedInfo using key RSACryptoServiceProvider#00d0fbf7, signature description RSAPKCS1SHA1SignatureDescription, hash algorithm SHA1CryptoServiceProvider, and asymmetric signature deformatter RSAPKCS1SignatureDeformatter.
System.Security.Cryptography.Xml.SignedXml Verbose: 14 : [SignedXml#00ea206d, VerifySignedInfo] Actual hash value: 87f028b9b9f7591949a2aeb8ee64f1b4d61654af
System.Security.Cryptography.Xml.SignedXml Verbose: 14 : [SignedXml#00ea206d, VerifySignedInfo] Raw signature: 6378709291fb92085c6b9d6a814fa21c9f8906f0eb2531e7e84f753b73dd8c78fd8cf9fac12318136dc25bc8a7f965b899365fb96e1cb4181b8a9327520cc7cc0013c4d3517fbe71d39c36f255d907b6e599aa72b5e6ab783478b469ddcc3ab89a14bc5d6f7c2be97b1e058948c5c7c5349b4324c66cca59d0849dfa10aa4373
System.Security.Cryptography.Xml.SignedXml Verbose: 13 : [SignedXml#00ea206d, VerifyReference] Processing reference Reference#0078b6a1, Uri "#2", Id "", Type "".
System.Security.Cryptography.Xml.SignedXml Verbose: 8 : [Reference#0078b6a1, ReferenceData] Transformed reference contents: <test id="2">      <data>data2</data>    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod><Reference URI="#2"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>b1p+90+Cy63cDc0WCh1S7xv8PNk=</DigestValue></Reference></SignedInfo><SignatureValue>Y3hwkpH7kghca51qgU+iHJ+JBvDrJTHn6E91O3PdjHj9jPn6wSMYE23CW8in+WW4mTZfuW4ctBgbipMnUgzHzAATxNNRf75x05w28lXZB7blmapyteareDR4tGndzDq4mhS8XW98K+l7HgWJSMXHxTSbQyTGbMpZ0ISd+hCqQ3M=</SignatureValue></Signature></test>
System.Security.Cryptography.Xml.SignedXml Verbose: 13 : [SignedXml#00ea206d, VerifyReference] Reference Reference#0078b6a1 hashed with "http://www.w3.org/2000/09/xmldsig#sha1" (SHA1CryptoServiceProvider) has hash value 9be87294e5851426aad884fa08c679f5d1a2c801, expected hash value 6f5a7ef74f82cbaddc0dcd160a1d52ef1bfc3cd9.
System.Security.Cryptography.Xml.SignedXml Information: 12 : [SignedXml#00ea206d, VerificationFailure] Verification failed checking references.

Обратите внимание на строку с "Transformed reference contents" (я их выделил жирным в обоих случаях) — во втором случае удаление тэга Signature не произошло, хотя преобразование "http://www.w3.org/2000/09/xmldsig#enveloped-signature" указывается везде.

Таким образом, основной подозреваемый это класс System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform, а точнее его метод GetOutput — там происходит преобразование.

Далее я попробовал посмотреть под отладчиком, что происходит в коде и нашел там вот такой фрагмент
XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm);
if (signatureList == null) return _containingDocument;
if (signatureList.Count < _signaturePosition || _signaturePosition <= 0) return _containingDocument;
 
// Remove the signature node with all its children nodes
signatureList[_signaturePosition - 1].ParentNode.RemoveChild(signatureList[_signaturePosition - 1]);
return _containingDocument;

Судя по тому, что я там увидел, проблема в том, что подписи ищутся по фрагменту _containingDocument (это тот узел, на который ссылается Reference), а потом для чего-то (я так и не понял, для чего — надо разбираться глубже) идет сравнение с _signaturePosition, которая означивается вот тут — в момент загрузки узла подписи и означает, судя по коду, номер подписи в документе целиком.

Увы, дальше копать просто нет времени. Если вы сможете найти объяснение или магическую комбинацию при которой это заработает — отпишите.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.