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

Есть ли способ установить префикс в подписи подписанного документа 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>

Ответ 1

Прежде всего, на самом деле нет веских оснований для этого. Две формы функционально эквивалентны. Любой хорошо обработанный 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";
}

Ответ 2

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

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

UPDATE: подпись подписи все в элементе SignedInfo, поэтому, если вы обновите этот элемент после факта, подпись перестает действовать. Вы "подделали" сообщение.

Ответ 3

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

Метод ComputeSignature будет изменен для добавления параметра префикса

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

Когда этот метод вызывается, он вычисляет значение Signature Value, переваривая значение SignedInfo node, если вы получите это значение без префикса "ds" , а затем добавьте префикс, вы получите недопустимую подпись, так что вы будете необходимо добавить префикс ДОПОЛНИТЕЛЬНОГО получения значения дайджеста signedinfo node.

Это значение дайджеста генерируется в методе 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);
}

Итак, теперь у вас есть значение подписи узлов SignedInfo с префиксом "ds" , так как вы все еще НЕ имеете xml с префиксом, поэтому, если вы просто вызовите метод GetXml, вы получите xml без префикса и, конечно, потому что значение подписи было рассчитано с учетом префикса ds, у вас будет недопустимая подпись. Чтобы избежать этого и получить структуру xml с префиксом, вам необходимо изменить метод GetXml, добавить префиксный параметр и вызвать метод 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;
}

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

CUSTOM CLASS

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");