Skip to content

Поддержка префиксов в xmldsig #42

Description

@RegBox22

Доброго дня!

Спасибо за продукт! Очень выручаете!

Хотел попросить рассмотреть возможность поддержки работы с префиксом на блок Signature лично столкнулся с проблемой на примере сервиса ГИИС ДМДК https://dmdk.ru они отступают от стандарта и требуют префикс причем только "ds" пока решил временным костылём но хочется видеть интегрированное в библиотеку, более изящное и лаконичное решение так как не имею опыта работы с xml прикладываю то, что у меня работает.

Работающий код:

`
public static void SignDmdkXml(string requestFileName, string requestSignedFileName,
X509Certificate2 certificate, bool saveFormat = true)
{
// Подгружаем документ
var xdoc = new XmlDocument
{
PreserveWhitespace = saveFormat
};

            xdoc.Load(requestFileName);

            // Создание подписчика XML-документа
            var signedXml = new PrefixedSignedXml(xdoc,"ds")
            {
                // Установка ключа для создания подписи
                SigningKey = certificate.PrivateKey,
                SignedInfo =
                {
                    // Установка алгоритма нормализации узла SignedInfo (в соответствии с методическими рекомендациями СМЭВ)
                    CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl,
                    // Установка алгоритма хэширования (в соответствии с методическими рекомендациями СМЭВ)
                    SignatureMethod = CPSignedXml.XmlDsigGost3410_2012_256Url
                }
            };

            // Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования
            var dataReference = new Reference
            {
                Uri = "#body",
                DigestMethod = CPSignedXml.XmlDsigGost3411_2012_256Url
            };

            // Метод преобразования, применяемый к данным перед их подписью (в соответствии с методическими рекомендациями СМЭВ)
            dataReference.AddTransform(new XmlDsigExcC14NTransform());
            dataReference.AddTransform(new XmlDsigSmevTransform());

            signedXml.SafeCanonicalizationMethods.Add("urn://smev-gov-ru/xmldsig/transform");

            // Установка ссылки на узел
            signedXml.AddReference(dataReference);

            // Установка информации о сертификате, который использовался для создания подписи
            var keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(certificate));
            signedXml.KeyInfo = keyInfo;

            // Вычисление подписи
            signedXml.ComputeSignature();

            // Получение XML-представления подписи
            var signatureXml = signedXml.GetXml();

            //// Добавление подписи в исходный документ
            xdoc.GetElementsByTagName("ns:CallerSignature")[0].AppendChild(xdoc.ImportNode(signatureXml, true));

            // Охраняем документ в выходной файл
            if (!saveFormat)
            {
                var settings = new XmlWriterSettings
                {
                    Indent = false,
                    NewLineChars = Empty
                };

                using var writer = XmlWriter.Create(requestSignedFileName, settings);
                xdoc.Save(writer);
            }
            else
                xdoc.Save(requestSignedFileName);
        }
    }

`

Использую обвертку над XmlSigned:

`
///


/// Thx https://stackoverflow.com/a/12343267
///

public class PrefixedSignedXml : SignedXml
{
private readonly string _prefix;

    public PrefixedSignedXml(XmlDocument document, string prefix)
        : base(document)
    {
        _prefix = prefix;
    }

    public void ComputeSignature()
    {
        BuildDigestedReferences();
        var signingKey = SigningKey;
        if (signingKey == null)
        {
            throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
        }
        if (SignedInfo.SignatureMethod == null)
        {
            if (signingKey is not DSA)
            {
                if (signingKey is not RSA)
                {
                    throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
                }

                SignedInfo.SignatureMethod ??= "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
            }
            else
            {
                SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
            }
        }

        if (CryptoConfig.CreateFromName(SignedInfo.SignatureMethod) is not SignatureDescription description)
        {
            throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
        }
        var hash = description.CreateDigest();
        if (hash == null)
        {
            throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
        }
        GetC14NDigest(hash, _prefix);
        m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
    }

    public new XmlElement GetXml()
    {
        var e = base.GetXml();
        SetPrefix(_prefix, e);
        return e;
    }

    //Отражательно вызывать закрытый метод SignedXml.BuildDigestedReferences
    private void BuildDigestedReferences()
    {
        var t = typeof(SignedXml);
        var m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
        m?.Invoke(this, new object[] { });
    }

    private void GetC14NDigest(HashAlgorithm hash, string prefix)
    {
        //string securityUrl = (this.m_containingDocument == null) ? null : this.m_containingDocument.BaseURI;
        //XmlResolver xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl);
        var document = new XmlDocument
        {
            PreserveWhitespace = true
        };
        var e = SignedInfo.GetXml();
        document.AppendChild(document.ImportNode(e, true));
        //CanonicalXmlNodeList namespaces = (this.m_context == null) ? null : Utils.GetPropagatedAttributes(this.m_context);
        //Utils.AddNamespaces(document.DocumentElement, namespaces);

        var canonicalizationMethodObject = SignedInfo.CanonicalizationMethodObject;
        //canonicalizationMethodObject.Resolver = xmlResolver;
        //canonicalizationMethodObject.BaseURI = securityUrl;
        SetPrefix(prefix, document.DocumentElement); //мы устанавливаем префикс перед вычислением хеша (иначе подпись не будет действительной)
        canonicalizationMethodObject.LoadInput(document);
        canonicalizationMethodObject.GetDigestedOutput(hash);
    }

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

`

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions