CryptoPro / libcore

44 stars 0 forks source link

CheckSignature всегда возвращает false #68

Closed SwimUp closed 5 months ago

SwimUp commented 5 months ago

Добрый день. Пытаюсь подписать XML и проверить её подпись по документации https://dss.cryptopro.ru/libcore/docs/04-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B/04-03-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-xml-%D0%BF%D0%BE%D0%B4%D0%BF%D0%B8%D1%81%D0%B8.html

Проблема в том, что подписанная хмл не проходит проверку, CheckSignature всегда возвращает false, при этом на сайте https://saas.cryptopro.ru/verifycpca/#/signature подпись проверку проходит.

Спека: .NET 8.0 LibCore 27.06.2024 Windows 10 CryptoPro CSP 4.0

На вход подаю XML

<?xml version="1.0" encoding="utf-8"?>
<CoordinateTaskMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <CoordinateTaskDataMessage>
        <Task>
            <MessageId>c9d37a8b-04ae-41f3-b371-8637c3b85f84</MessageId>
        </Task>
    </CoordinateTaskDataMessage>
</CoordinateTaskMessage>

Затем, читаю + подписываю

using (var store = new CpX509Store(StoreName.My, StoreLocation.CurrentUser))
{
    store.Open(OpenFlags.ReadOnly);
    var storeCerts = store.Certificates;

    var CpCertificate = store.Certificates.Find(X509FindType.FindByThumbprint, "<мой серт>", true)[0];

    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.Load("out.xml");

    var sig = SignXmlFile(xmlDocument, CpCertificate.GetGost3410_2012_256PrivateKey(), CpCertificate);

    File.WriteAllText("out-signed-text.xml", sig.OuterXml, Encoding.UTF8);

    var cc = ValidateXmlFIle(sig);
}

Методы подписания и проверки просто копипаста с доков.

Проверял способ подписания и проверки на .net framework 4.7.2, непосредственно с либами криптопро - там подпись проверку проходит.

Fasjeit commented 5 months ago

Добрый день.

Воспроизвести пока не получилось.

Для начала следует обновиться на актуальную версию csp, как указанно в документации. В настоящий момент это 5.0.13000, скачать можно с сайта: https://www.cryptopro.ru/products/csp

Так как в примере явно не передаётся открытый ключ для проверки - для проверки происходит поиск по хранилищу с целью найти доверенный сертификат, содержащий открытый ключ. Следует проверить, является ли сертификат, установленный в хранилище доверенным. Альтернативно можно явно передавать открытый ключ сертификата в метод CheckSignature.

SwimUp commented 5 months ago

Обновление до 5 версии помогло, метод стал отдавать true. Есть идеи, почему на framework-е CheckSignature true отдает и на 4 версии?

Подписание и проверка на .Net framework 4.7.2 выглядит почти также

   public XmlDocument SignXmlFile(XmlDocument doc)
    {
        SmevSignedXml signedXml = new SmevSignedXml(doc);

        signedXml.SigningKey = Certificate.PrivateKey;

        Reference reference = new Reference();
        reference.Uri = "#body";

        reference.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411_2012_256Url;

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

        XmlDsigExcC14NTransform c14 = new XmlDsigExcC14NTransform();
        reference.AddTransform(c14);

        signedXml.AddReference(reference);

        signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

        signedXml.SignedInfo.SignatureMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3410_2012_256Url;

        signedXml.ComputeSignature();

        XmlElement xmlDigitalSignature = signedXml.GetXml();

        doc.DocumentElement.PrependChild(doc.ImportNode(xmlDigitalSignature, true));

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

        return doc;
    }

    public void VerifyXmlFile(XmlDocument xmlDocument)
    {
        XmlNodeList nodeList = xmlDocument.GetElementsByTagName(
            "Signature", SignedXml.XmlDsigNamespaceUrl);

        for (int curSignature = 0; curSignature < nodeList.Count; curSignature++)
        {
            SmevSignedXml signedXml = new SmevSignedXml(xmlDocument);

            signedXml.LoadXml((XmlElement)nodeList[curSignature]);

            bool result = signedXml.CheckSignature();
            Console.WriteLine(result);
        }
    }

Вот этот же вариант, но уже под LibCore:

Подписание:

  public static XmlDocument SignXml(XmlDocument doc, string id, CpX509Certificate2 CpCertificate)
  {
      CpSignedXml signedXml = new CpSignedXml(doc);

      signedXml.SigningKey = CpCertificate.GetGost3410_2012_256PrivateKey();

      CpReference reference = new CpReference();
      reference.Uri = $"#{id}";

      reference.DigestMethod = CpSignedXml.XmlDsigGost3411_2012_256Url;

      CpKeyInfo keyInfo = new CpKeyInfo();
      keyInfo.AddClause(new CpKeyInfoX509Data(CpCertificate));
      signedXml.KeyInfo = keyInfo;

      CpXmlDsigExcC14NTransform c14 = new CpXmlDsigExcC14NTransform();
      reference.AddTransform(c14);

      signedXml.AddReference(reference);

      signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

      signedXml.SignedInfo.SignatureMethod = CpSignedXml.XmlDsigGost3410_2012_256Url;

      signedXml.ComputeSignature();

      XmlElement xmlDigitalSignature = signedXml.GetXml();

      doc.DocumentElement.PrependChild(doc.ImportNode(xmlDigitalSignature, true));

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

      return doc;
  }

Валидация:

    public static bool ValidateXmlFile(XmlDocument xmlDocument, CpX509Certificate2 cpX509Certificate2)
    {
        XmlNodeList nodeList = xmlDocument.GetElementsByTagName(
            "Signature", SignedXml.XmlDsigNamespaceUrl);

        // Проверяем все подписи.
        for (int curSignature = 0; curSignature < nodeList.Count; curSignature++)
        {
            CpSignedXml signedXml = new CpSignedXml(xmlDocument);

            signedXml.LoadXml((XmlElement)nodeList[curSignature]);

            return signedXml.CheckSignature(cpX509Certificate2, true);
        }
        return true;
    }

Полный код:

        using (var store = new CpX509Store(StoreName.My, StoreLocation.LocalMachine))
        {
            store.Open(OpenFlags.ReadOnly);
            var storeCerts = store.Certificates;

            var CpCertificate = store.Certificates.Find(X509FindType.FindByThumbprint, "<>", false)[0];

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load("out.xml");

            var sig = SignXml(xmlDocument, "body", CpCertificate);

            File.WriteAllText("out-signed-text.xml", sig.OuterXml, Encoding.UTF8);

            var cc = ValidateXmlFile(sig, CpCertificate);

            Console.WriteLine(cc);
        }

XML на вход:

<?xml version="1.0" encoding="utf-8"?>
<CoordinateTaskMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <CoordinateTaskDataMessage Id="body">
        <Task>
            <MessageId>c9d37a8b-04ae-41f3-b371-8637c3b85f84</MessageId>
        </Task>
    </CoordinateTaskDataMessage>
</CoordinateTaskMessage>

В итоге - проверка false изображение

берем XML и пробуем завалидировать на сайте изображение

Fasjeit commented 5 months ago

Для корректной работы Libcore требуется последняя актуальная версия csp 5.0, как указанно в документации.

В более старых версиях csp может отсутствовать требуемая функциональность необходима для корректной работы, так как код работы с сертификатами и XML Был полностью переписан, по сравнению с версией КриптоПро.Net для .net Framework.