Статус: Новичок
Группы: Участники
Зарегистрирован: 30.08.2021(UTC) Сообщений: 2 Сказал(а) «Спасибо»: 3 раз Поблагодарили: 1 раз в 1 постах
|
Автор: two_oceans Добрый день. Подписанный запрос не вижу, так что проверить не могу. Желательно прикреплять файлом, форум искажает содержимое. Не беда если запрос без переводов строк - любой браузер его отобразит с форматированием, так что специально добавлять отступы и переводы строк не нужно.
1) По остальному напрашивается вопрос - трансформ СМЭВ точно ли нужен для этой информационной системы? В некоторых случаях системы крашатся при проверке на неопознанных трансформах; 2) Подписываете документ без удаления переводов строк, а записываете результат, удаляя переводы строк - потенциально может поломать подпись, так как каноникализация оставляет символы 0х10 без изменения (если они были). Желательно чтобы на подписание уже шел документ без лишних табуляций и переводов строк. Например, считали исходный, сразу записали во временный документ с Вашими настройками Writer, считали уже временный документ и его подписываете; 3) Пересадкой из одной подписи в другую также есть риск все поломать. Формируется не том месте или почему к этому прибегаете? В идеале вообще не пересаживать, а если пересаживаете, то весь тег Signature целиком. В частности, настораживает в коде, что берете содержимое (SignedInfo) без префиксов и вставляете в тег с префиксом ds:Signature. Если все так, то это исказит каноническую форму SignedInfo и SignatureValue будет неверно;
Не уверен насчет класса SignedXml, есть ли там такой режим, но достаточно часто используют "шаблон подписи" - формируют содержимое подписи, оставив DigestValue и SignatureValue пустыми, а метод подписания заполняет только их. Спасибо за оперативный ответ, к сожалению не имею должного опыта работы с XMLDSIG и КриптоПро и основывался на оф. примере из SDK. 1) Согласно описанию документации требуется - поэтому принял как должное. 2) Действительно Вы правы это потенциально слабое место, которое не брал во внимание, поэтому немного расширил функционал и добавил PreserveWhitespace = saveFormat получив формат исходного запроса на лету пусть и с плоским блоком Signature. 3) Пересадку брал из примера в КриптоПро SDK в моем случае работают обе версии и корректно так как структура не сложная. Хотя сам по началу подсаживал весь Signature. Режима "шаблон подписи" для SignedXml не нашёл на просторах интернета, но на базе класса, что ниже это можно реализовать чуть позже. Проблема решилась благодаря Вашим наводкам вся сложность оказалась в том, что сервис требует префиксы и строго "ds" ко всем дочерним узлам Signature хотя это отступ от стандарта, который говорит о плохом тоне при использовании префиксов. Я поначалу эту версию прорабатывал так как в примерах префиксы указаны но прочитав описания стандарта подумал ну раз это гос.сервис значит все должно быть по фэншую ровно по стандарту в этот момент я и пошел не потому пути поверив чёткое следование стандарту. К сожалению, прямого пути присвоения префиксов не нашёл воспользовался производным классом от SignedXml (https://stackoverflow.com/a/12343267) с перегрузкой методов и все получилось по крайней мере на базовым запросе дальше буду тестировать. Спасибо! Если кому потребуются прикладываю исходники моих черновиков:
Код: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);
}
}
Код: /// <summary>
/// Thx https://stackoverflow.com/a/12343267
/// </summary>
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;
}
}
Отредактировано пользователем 30 августа 2021 г. 19:11:42(UTC)
| Причина: Не указана
|
1 пользователь поблагодарил Viacheslav за этот пост.
|
|