Вопрос

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

Решение

Прежде всего, на самом деле нет никаких веских причин для этого.Обе формы функционально эквивалентны.Любой исправный 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 после того, как оно было подписано возможно, его не удастся проверить, как это было в приведенном выше примере.ИМХО, это недостаток в реализации цифровой подписи MSFT, с которым вам придется смириться.

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

ОБНОВЛЯТЬ:Подписные знаки все в элементе SignedInfo, поэтому, если вы обновите этот элемент постфактум, подпись перестанет быть действительной.Вы «подделали» сообщение.

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

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

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

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

Это значение дайджеста генерируется в методе 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, добавить параметр 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");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top