Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
VVSoft opened this issue Aug 31, 2021 · 0 comments
Open

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

VVSoft opened this issue Aug 31, 2021 · 0 comments

Comments

@VVSoft
Copy link

VVSoft commented Aug 31, 2021

Доброго дня!

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

Хотел попросить рассмотреть возможность поддержки работы с префиксом на блок 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;
    }
}

`

@VVSoft VVSoft changed the title Работа с префиксом xmldsig Поддержка префиксов в xmldsig Aug 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant