SignedXml checksignature возвращает false

Я просмотрел другие сообщения здесь по этой проблеме, и ни один из них, похоже, не касается моей ситуации.

Я пытался проверить утверждение SAML на прошлой неделе, и у меня есть 2 клиента, которые прислали мне SAML, но я не могу его проверить.

Основной процесс заключается в том, что мы получаем утверждение в кодировке base64, и я его декодирую. Загрузите его в XmlDocment с параметром PreserveWhitespace = true.

Метод проверки

  public static bool Verify(X509Certificate2 cert, XmlElement xmlElement, SignedXml signedXml)
  {
       bool flag;
       try
       {
           KeyInfo keyInfo = new KeyInfo();
           var clause = new KeyInfoX509Data(cert);
           keyInfo.AddClause(clause);

            XmlElement signatureElement = GetSignatureElement(xmlElement);
            if (signatureElement == null)
            {
                string message = "The XML does not contain a signature.";
                throw new SAMLSignatureException(message);
            }
            signedXml.LoadXml(signatureElement);
            if (keyInfo != null)
            {
                signedXml.KeyInfo = keyInfo;
            }
            SetSigningKeyFromKeyInfo(signedXml);
            flag = signedXml.CheckSignature(cert.PublicKey.Key);
        }
        catch (Exception exception)
        {
            throw new SAMLSignatureException("Failed to verify the XML signature.", exception);
        }
        return flag;
    }

 private static void SetSigningKeyFromKeyInfo(SignedXml signedXml)
    {
        IEnumerator enumerator = signedXml.KeyInfo.GetEnumerator();
        while (enumerator.MoveNext())
        {
            if (enumerator.Current is KeyInfoX509Data)
            {
                var current = (KeyInfoX509Data) enumerator.Current;
                if (current.Certificates.Count != 0)
                {
                    var certificate = (X509Certificate) current.Certificates[0];
                    var certificate2 = new X509Certificate2(certificate);
                    AsymmetricAlgorithm key = certificate2.PublicKey.Key;
                    signedXml.SigningKey = key;
                    return;
                }
            }
            else
            {
                if (enumerator.Current is RSAKeyValue)
                {
                    var value2 = (RSAKeyValue) enumerator.Current;
                    signedXml.SigningKey = value2.Key;
                    return;
                }
                if (enumerator.Current is DSAKeyValue)
                {
                    var value3 = (DSAKeyValue) enumerator.Current;
                    signedXml.SigningKey = value3.Key;
                    return;
                }
            }
        }
        throw new SAMLSignatureException("No signing key could be found in the key info.");
    }

У меня есть сертификат от клиента, который я прочитал из Web.Config (он хранится в виде строки с кодировкой base64).

Оба клиента получают false, возвращаемую checksignature, но когда я создаю свой собственный подписанный saml с моим сертификатом, он возвращает true.

Что мне здесь не хватает?

РЕДАКТИРОВАТЬ: Да, оба клиента находятся на Java, и я опубликовал метод SetSigningKeyFromKeyInfo.


person Jonathan S.    schedule 14.10.2010    source источник
comment
Дайте угадаю, полученное вами утверждение было сгенерировано на языке, отличном от .net, например на Java?   -  person Klaus Byskov Pedersen    schedule 14.10.2010
comment
Что делает SetSigningKeyFromKeyInfo(signedXml);?   -  person Klaus Byskov Pedersen    schedule 14.10.2010
comment
Когда вы расшифровали утверждение base64, можете ли вы сбросить XML в файл и сравнить его с одним из ваших собственных утверждений, чтобы проверить наличие (тонких) структурных несоответствий?   -  person Klaus Byskov Pedersen    schedule 14.10.2010
comment
Смотрите мою правку. Что касается сравнения двух утверждений, да. Я посмотрел на оба утверждения и не могу найти ничего, что торчало бы у меня.   -  person Jonathan S.    schedule 14.10.2010
comment
Вы можете проверить эту реализацию: digitaliser.dk/resource/ 558794/artefact/oiosaml-dot-net-1.6.zip Это реализация протокола SAML с открытым исходным кодом в .net, в написании которой я принимал участие несколько лет назад. Я не проверял текущую версию, но вы сможете получить оттуда некоторое вдохновение.   -  person Klaus Byskov Pedersen    schedule 14.10.2010
comment
У меня был смутно похожий опыт с шифрованием, в котором использовались разные кодировки (utf-8 против utf-16). Может здесь аналогичная проблема? Я почти уверен, что кодировка по умолчанию — utf-8 для Java и utf-16 для .net.   -  person Michael    schedule 29.06.2012


Ответы (4)


В прошлом я много имел дело с подписанными XML. Все, что я могу сказать, это то, что это был кошмар. По сути, когда вы подписываете XML, он проходит процесс, называемый канонизацией (C14N). Он должен преобразовать текст XML в поток байтов, который можно подписать. Обработка пробелов и пространств имен, среди прочего, в стандартах XML C14N сложна для понимания, еще труднее правильно реализовать. Есть даже несколько типов C14N.

Реализация .NET очень избирательна в отношении того, что она принимает. Вполне возможно, что ваша другая реализация работает не так, как .NET. Это действительно очень печально. Например, если вы можете удалить пробелы и пространства имен из исходного XML перед подписанием, это может помочь. Также, если бы вы могли убедиться, что обе реализации используют одни и те же настройки C14N.

В противном случае вас ждет много отладки. Вы можете отлаживать фреймворк или вызывать его внутренние методы вручную с отражением, чтобы увидеть, как он вычисляет XML-фрагмент и подпись. И сделайте то же самое с другой реализацией. В основном вам нужно видеть точные потоки байтов, которые подписаны в обоих случаях. Это последний шаг преобразования перед подписанием. Если эти потоки байтов совпадают, то, по моему опыту, у вас не будет проблем с частью подписи RSA. Если они не совпадают, как в вашем случае, по крайней мере, вы увидите, в чем проблема.

person fejesjoco    schedule 29.09.2012

У меня была аналогичная проблема, и я потерял много времени, может быть, это может кому-то помочь.

Моя среда — это 100% .Net 4.5, и мой код использует только класс SignedXml. Но утверждение SAML было принято в одном месте и отклонено в другом.

Оказалось, что одно место загружало утверждение через экземпляр XmlDocument, инициализированный с помощью PreserveWhitespace = true, а другое — нет.

И утверждение было красиво напечатано, поэтому в нем были символы возврата каретки и много отступов. Удаление всех возвратов каретки и отступов устранило мою проблему.

person Timores    schedule 13.05.2016
comment
Значение по умолчанию для XmlDocument.PreserveWhitespace — false. Переключился на истину и смог получить «истину» от CheckSignature. Я предполагаю, что пробел использовался при вычислении подписи с сертификатом, и после удаления подпись не считалась действительной. - person voidsstr; 24.06.2020

Была аналогичная проблема с Saml as Timores. Saml нужно было декодировать из Base64, но сначала я использовал:

var saml = System.Text.Encoding.Default.GetString(Convert.FromBase64String(samlToken))

Но это использовало декодирование ASCII и имело проблемы со специальными символами. Это означает, что XML немного отличался от того, когда он был подписан, и именно поэтому он потерпел неудачу. Изменил на:

var saml = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(samlToken))

и это работало для всех случаев.

Поэтому убедитесь, что вы используете правильную кодировку!

person Jeroen VL    schedule 20.07.2018

Я потратил много времени на эту проблему, а потом понял, что не проверяю подпись с нужным сертификатом.

Поэтому я решил проверить сертификат, который я получаю в XML-файле ответов от Azure:

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

X509Certificate2 serviceCertificate = null;
foreach (KeyInfoClause clause in signedXml.KeyInfo)
{
    if (clause is KeyInfoX509Data)
    {
        if (((KeyInfoX509Data)clause).Certificates.Count > 0)
        {
            serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
        }
    }
}

Потом:

bool bTest = signedXml.CheckSignature(serviceCertificate , true);

Наконец, значение bTest было установлено в true.

person Laïd Bouloussa    schedule 29.11.2019