.NET подписанный префикс XML

Есть ли способ установить префикс для подписи подписанного XML-документа (класс SignedXml в.Net)?

Так что вместо:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>

Я мог бы иметь следующее:

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>

3 ответа

Решение

Прежде всего, на самом деле нет веских причин для этого. Две формы функционально эквивалентны. Любой хорошо ведущий себя XML-процессор будет обрабатывать их абсолютно одинаково. Поэтому, если вы не пытаетесь общаться с приложением, которое должным образом не реализует пространства имен XML, лучше (IMO) просто оставить форму по умолчанию в покое. (И даже в этом случае было бы лучше, если вообще возможно, вместо этого исправить неисправное приложение.)

Тем не менее, вы можете вручную установить префикс для XmlElement, который возвращается SignedXml.GetXml() и его дочерних элементов, используя XPath, например так:

XmlElement signature = signedXml.GetXml();
foreach (XmlNode node in signature.SelectNodes(
    "descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
{
    node.Prefix = "ds";
}

Это не может быть сделано. Если вы измените XML после того, как он был подписан, его невозможно будет проверить, как это было в примере выше. IMO, это недостаток в реализации цифровой подписи MSFT, с которым вам придется жить.

Многие скажут, что для этого нет причин, и они технически правильны. Но когда вы имеете дело с огромным продавцом (например, правительством штата или банком), удачи вам в том, чтобы он изменил его в конце концов. Большинство эталонных реализаций включают его.

ОБНОВЛЕНИЕ: подпись подписывает все в элементе SignedInfo, так что если вы обновите этот элемент после факта, то подпись больше не будет действительной. Вы "подделали" сообщение.

Это можно сделать, но необходимо изменить класс SignedXml, чтобы добавить префикс перед получением значения дайджеста узла SignedInfo.

Метод ComputeSignature будет изменен, чтобы добавить параметр префикса

public void ComputeSignature(string prefix){...}

Когда этот метод вызывается, он вычисляет значение подписи, переваривая значение узла SignedInfo, если вы получите это значение без префикса "ds", а затем добавите префикс, вы получите недопустимую подпись, поэтому вам придется добавить префикс ПЕРЕД получением дайджест-значения для подписанного узла.

Это значение дайджеста генерируется в методе GetC14NDigest, поэтому этот метод будет изменен для добавления параметра префикса и добавления префикса ДО получения значения дайджеста.

private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
    XmlDocument document = new XmlDocument();
    document.PreserveWhitespace = false;
    XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
    document.AppendChild(document.ImportNode(e, true));        
    Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;       
    SetPrefix(prefix, document.DocumentElement); /*Set the prefix before getting the HASH*/
    canonicalizationMethodObject.LoadInput(document);
    return canonicalizationMethodObject.GetDigestedOutput(hash);
}

Итак, теперь у вас есть значение Signature для узлов SignedInfo с префиксом "ds", что означает, что у вас еще нет xml с префиксом, поэтому, если вы просто вызовете метод GetXml, вы получите xml без префикс и, конечно, потому что значение подписи было вычислено с учетом префикса ds, у вас будет недопустимая подпись. Чтобы избежать этого и получить структуру xml с префиксом, необходимо изменить метод GetXml, добавить параметр prefix и вызвать метод SetPrefix, который добавит префикс "ds" ко всем узлам в Signature Xml

public XmlElement GetXml(string prefix)
{
    XmlElement e = this.GetXml();
    SetPrefix(prefix, e); //return the xml structure with the prefix
    return e;
}

Я оставлю здесь класс с этими модификациями

ТАМОЖЕННЫЙ КЛАСС

internal sealed class CustomSignedXml : SignedXml
{
    XmlElement obj = null;
    public CustomSignedXml (XmlDocument xml)
        : base(xml)
    {
    }

    public CustomSignedXml (XmlElement xmlElement)
        : base(xmlElement)
    {

    }

    public XmlElement GetXml(string prefix)
    {
        XmlElement e = this.GetXml();
        SetPrefix(prefix, e);
        return e;
    }

    public void ComputeSignature(string prefix)
    {
        this.BuildDigestedReferences();
        AsymmetricAlgorithm signingKey = this.SigningKey;
        if (signingKey == null)
        {
            throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
        }
        if (this.SignedInfo.SignatureMethod == null)
        {
            if (!(signingKey is DSA))
            {
                if (!(signingKey is RSA))
                {
                    throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
                }
                if (this.SignedInfo.SignatureMethod == null)
                {
                    this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
                }
            }
            else
            {
                this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
            }
        }
        SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
        if (description == null)
        {
            throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
        }
        HashAlgorithm hash = description.CreateDigest();
        if (hash == null)
        {
            throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
        }
        this.GetC14NDigest(hash, prefix);
        this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
    }         

    private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
    {

        XmlDocument document = new XmlDocument();
        document.PreserveWhitespace = false;
        XmlElement e = this.SignedInfo.GetXml();
        document.AppendChild(document.ImportNode(e, true));               

        Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;            
        SetPrefix(prefix, document.DocumentElement); //Set the prefix before getting the HASH
        canonicalizationMethodObject.LoadInput(document);
        return canonicalizationMethodObject.GetDigestedOutput(hash);
    }

    private void BuildDigestedReferences()
    {
        Type t = typeof(SignedXml);
        MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
        m.Invoke(this, new object[] { });
    }

    private void SetPrefix(string prefix, XmlNode node)
    {
       foreach (XmlNode n in node.ChildNodes)
          SetPrefix(prefix, n);
       node.Prefix = prefix;
    }
}

И способ его использования

CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
. 

//compute the signature with the "ds" prefix

signedXml.ComputeSignature("ds");

//get the xml of the signature with the "ds" prefix

XmlElement xmlDigitalSignature = signedXml.GetXml("ds");
Другие вопросы по тегам