Статус: Активный участник
Группы: Участники
Зарегистрирован: 06.07.2012(UTC) Сообщений: 267 Откуда: Калининград Сказал «Спасибо»: 51 раз Поблагодарили: 30 раз в 22 постах
|
Добрый день. На основе примера SignSmevRequest.cs подписываю сообщение ключом гост-2001 (указываю CPSignedXml.XmlDsigGost3410Url), однако при проверке подписи методом signedXml.CheckSignature(cert.PublicKey.Key) результат - "подпись не верна". Если подписываю алгоритмом SignedXml.XmlDsigRSASHA1Url, то пишет, что подпись верна. При этом проверка на сайте https://dss.cryptopro.ru/Verify/Verify/ для обоих подписанных файлов выдает "Подпись математически корректна. Не удается построить цепочку сертификатов для доверенного корневого центра." Подскажите, пожалуйста, чего не хватает, чтобы подпись гост определялась как математически корректная? Windows 10, Криптопро 4.0.9944, КроиптоПро .Net Клиент 1.0.6893.0 (срок действия обеих лицензий: Неограничен) Прилагаю итоговый подписанный xml: Код:<?xml version="1.0" standalone="no"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><S:Header><wsse:Security S:actor="http://smev.gosuslugi.ru/actors/smev"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102001-gostr3411" /><Reference URI="#body"><Transforms><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr3411" /><DigestValue>1dcWoS3yDpXKi095psQcUIri4VIZOHsOHCwhUDFMWXw=</DigestValue></Reference></SignedInfo><SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">FZnLGCOH+B2GeFphG0HbPINYl6JOqNEbo2GomZJ4bH3x6EeXY6mrC3NOiLBe23c6JhmxFgvAbRhbpt4QpggANQ==</SignatureValue><ds:KeyInfo><wsse:SecurityTokenReference><wsse:Reference URI="#SenderCertificate" /></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature><wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="SenderCertificate">MIIEnTCCBEygAwIBAgIKcO+OjwAJAALc8jAIBgYqhQMCAgMwgYcxJTAjBgkqhkiG9w0BCQEWFnNlY3VyaXR5QHN2aWF6LWJhbmsucnUxCzAJBgNVBAYTAlJVMQ8wDQYDVQQHEwZNb3Njb3cxEzARBgNVBAoTClN2aWF6LUJhbmsxKzApBgNVBAMTIlN2aWF6LUJhbmsgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgwMjE0MTMyMzAzWhcNMTkwMjE0MTMyMzAzWjCCAQYxJjAkBgkqhkiG9w0BCQEWF29pYkBlbmVyZ290cmFuc2JhbmsuY29tMQswCQYDVQQGEwJSVTE2MDQGA1UECAwt0JrQsNC70LjQvdC40L3Qs9GA0LDQtNGB0LrQsNGPINC+0LHQu9Cw0YHRgtGMMR8wHQYDVQQHDBbQmtCw0LvQuNC90LjQvdCz0YDQsNC0MTkwNwYDVQQKDDDQmtCRICcn0K3QvdC10YDQs9C+0YLRgNCw0L3RgdCx0LDQvdC6JycgKNCe0JDQnikxOzA5BgNVBAMMMtCc0LDRgdC70L7QstGB0LrQuNC5INCR0L7RgNC40YEg0JDQvdC00YDQtdC10LLQuNGHMGMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEDQwAEQMa86IR4kddGtXKMQ+1opDqRIbSCSS3LLn/Zui9uMrsz7Nj1//ZjgsaBMP7FkDsTRBdQUuhzRZRc5z4XaB00hh2jggIUMIICEDAmBgNVHSUEHzAdBggrBgEFBQcDAgYIKwYBBQUHAwQGByqFAwICIgYwFQYDVR0RBA4wDKAKBgNVBAygAxMBMzAdBgNVHQ4EFgQU029uLVqatD0oiI/5YOGkF7TSa2cwDgYDVR0PAQH/BAQDAgTwMIHDBgNVHSMEgbswgbiAFAlBTrUiGvM6bq4yaKNg84S+Zd8toYGNpIGKMIGHMSUwIwYJKoZIhvcNAQkBFhZzZWN1cml0eUBzdmlhei1iYW5rLnJ1MQswCQYDVQQGEwJSVTEPMA0GA1UEBxMGTW9zY293MRMwEQYDVQQKEwpTdmlhei1CYW5rMSswKQYDVQQDEyJTdmlhei1CYW5rIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ghBYRtrrCEZCok+hLLuT4mhsMDIGCSsGAQQBgjcVCgQlMCMwCgYIKwYBBQUHAwIwCgYIKwYBBQUHAwQwCQYHKoUDAgIiBjCBpQYDVR0fBIGdMIGaME6gTKBKhkhodHRwOi8vY2Euc3ZpYXotYmFuay5ydS9jZHAvMDk0MTRlYjUyMjFhZjMzYTZlYWUzMjY4YTM2MGYzODRiZTY1ZGYyZC5jcmwwSKBGoESGQmh0dHA6Ly9ibGl6a28uYml6L2NybC8wOTQxNGViNTIyMWFmMzNhNmVhZTMyNjhhMzYwZjM4NGJlNjVkZjJkLmNybDAIBgYqhQMCAgMDQQAqo9/OkOpmp7h9kgRQT2Z7gQZZcZr6C1+cCEqNrmA6Ln7CXv3en6Was3tahITdBhDYof3V8Q1W7/eui2iqmDBF</wsse:BinarySecurityToken></wsse:Security></S:Header><S:Body wsu:Id="body"><ws:queryINNFL xmlns:ws="http://ws.unisoft/"><smev:Message xmlns:smev="http://smev.gosuslugi.ru/rev110801"><smev:Sender><smev:Code>MINECONOMSK_SYS_1</smev:Code><smev:Name>Минэкономразвития СК</smev:Name></smev:Sender><smev:Recipient><smev:Code>13312</smev:Code><smev:Name>ФНС</smev:Name></smev:Recipient><smev:Originator><smev:Code>MINECONOMSK_SYS_1</smev:Code><smev:Name>Минэкономразвития СК</smev:Name></smev:Originator><smev:TypeCode>2</smev:TypeCode><smev:Date>2012-03-13T11:10:54.54Z</smev:Date></smev:Message><smev:MessageData xmlns:smev="http://smev.gosuslugi.ru/rev110801"><smev:AppData wsu:Id="fns-AppData"><Документ xmlns="http://ws.unisoft/FNSINN/queryINNFL" ВерсФорм="4.01" ИдЗапрос="AB324006-978B-44D4-933D-C5E6DFA8A576"><СвЮЛ ИННЮЛ="7825497650" НаимОрг="Нагрузочное тестирование" ОГРН="1037843048880" /><СвФЛ ДатаРожд="12.07.1954" МестоРожд="РОССИЯ,,ГОРЬКОВСКАЯ ОБЛ.,АРЗАМАССКИЙ Р-Н,,НИКОЛЬСКОЕ С., ,,,"><ФИО Имя="ПЕТР" Отчество="АЛЕКСЕЕВИЧ" Фамилия="ЧАХЛОВ" /><УдЛичнФЛ ВыдДок="АРОВД" ДатаДок="16.11.2002" КодВидДок="21" СерНомДок="22 02 919928" /></СвФЛ></Документ></smev:AppData></smev:MessageData></ws:queryINNFL></S:Body></S:Envelope>
И код программы: Код:class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
// TODO: Implement Functionality Here
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
// Разбираем аргументы
if (args.Length < 2)
{
Console.WriteLine("<doc_to_sign> <signed_doc>");
return;
}
X509Store certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(
certStore.Certificates,
"Выберите сертификат",
"Пожалуйста, выберите сертификат электронной подписи",
X509SelectionFlag.SingleSelection);
if (certs.Count == 0)
{
Console.WriteLine("Сертификат не выбран.");
return;
}
// Подписываем запрос
SignXmlFile(args[0], args[1], certs[0]);
// Проверяем подпись
VerifyXmlFile(args[1]);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
// Подписывает файл XML с помощью заданного сертификата и сохраняет подписанный документ
// в новый файл.
static void SignXmlFile(string FileName, string SignedFileName, X509Certificate2 Certificate)
{
// Создаем новый документ XML.
XmlDocument doc = new XmlDocument();
// Читаем документ из файла.
doc.Load(new XmlTextReader(FileName));
// Создаём объект SmevSignedXml - наследник класса SignedXml с перегруженным GetIdElement
// для корректной обработки атрибута wsu:Id.
SmevSignedXml signedXml = new SmevSignedXml(doc);
// Задаём ключ подписи для документа SmevSignedXml.
signedXml.SigningKey = Certificate.PrivateKey;
// Создаем ссылку на подписываемый узел XML. В данном примере и в методических
// рекомендациях СМЭВ подписываемый узел soapenv:Body помечен идентификатором "body".
Reference reference = new Reference();
reference.Uri = "#body";
// Задаём алгоритм хэширования подписываемого узла - ГОСТ Р 34.11-94. Необходимо
// использовать устаревший идентификатор данного алгоритма, т.к. именно такой
// идентификатор используется в СМЭВ.
#pragma warning disable 612
//warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete
reference.DigestMethod = CPSignedXml.XmlDsigGost3411Url;
//reference.DigestMethod = SignedXml.XmlDsigSHA1Url;
#pragma warning restore 612
// Добавляем преобразование для приведения подписываемого узла к каноническому виду
// по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими
// рекомендациями СМЭВ.
XmlDsigExcC14NTransform c14 = new XmlDsigExcC14NTransform();
reference.AddTransform(c14);
// Добавляем ссылку на подписываемый узел.
signedXml.AddReference(reference);
// Задаём преобразование для приведения узла ds:SignedInfo к каноническому виду
// по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими
// рекомендациями СМЭВ.
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
// Задаём алгоритм подписи - ГОСТ Р 34.10-2001. Необходимо использовать устаревший
// идентификатор данного алгоритма, т.к. именно такой идентификатор используется в
// СМЭВ.
#pragma warning disable 612
//warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete
signedXml.SignedInfo.SignatureMethod = CPSignedXml.XmlDsigGost3410Url;
//signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
#pragma warning restore 612
// Вычисляем подпись.
signedXml.ComputeSignature();
// Получаем представление подписи в виде XML.
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Добавляем необходимые узлы подписи в исходный документ в заготовленное место.
doc.GetElementsByTagName("ds:Signature")[0].PrependChild(
doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignatureValue")[0], true));
doc.GetElementsByTagName("ds:Signature")[0].PrependChild(
doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignedInfo")[0], true));
// Добавляем сертификат в исходный документ в заготовленный узел
// wsse:BinarySecurityToken.
doc.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText =
Convert.ToBase64String(Certificate.RawData);
// Сохраняем подписанный документ в файл.
using (XmlTextWriter xmltw = new XmlTextWriter(SignedFileName,
new UTF8Encoding(false)))
{
doc.WriteTo(xmltw);
}
}
// Проверяет подписи в файле XML.
static void VerifyXmlFile(string SignedFileName)
{
// Создаем новый документ XML.
XmlDocument xmlDocument = new XmlDocument();
// Форматируем документ с сохранением всех пробельных символов, т.к. они
// важны при проверке подписи.
xmlDocument.PreserveWhitespace = true;
// Загружаем подписанный документ XML из файла.
xmlDocument.Load(SignedFileName);
// Ищем все узлы ds:Signature и сохраняем их в объекте XmlNodeList
XmlNodeList nodeList = xmlDocument.GetElementsByTagName(
"Signature", SignedXml.XmlDsigNamespaceUrl);
Console.WriteLine("Найдено подписей: {0}.", nodeList.Count);
// Проверяем все подписи.
for (int curSignature = 0; curSignature < nodeList.Count; curSignature++)
{
// Создаём объект SmevSignedXml - наследник класса SignedXml с перегруженным
// GetIdElement для корректной обработки атрибута wsu:Id.
SmevSignedXml signedXml = new SmevSignedXml(xmlDocument);
// Загружаем узел с подписью.
signedXml.LoadXml((XmlElement)nodeList[curSignature]);
// Получаем идентификатор ссылки на узел wsse:BinarySecurityToken,
// содержащий сертификат подписи.
XmlNodeList referenceList = signedXml.KeyInfo.GetXml().GetElementsByTagName(
"Reference", WSSecurityWSSENamespaceUrl);
if (referenceList.Count == 0)
{
throw new XmlException("Не удалось найти ссылку на сертификат");
}
// Ищем среди аттрибутов ссылку на сертификат.
string binaryTokenReference = ((XmlElement)referenceList[0]).GetAttribute("URI");
// Ссылка должна быть на узел внутри данного документа XML, т.е. она имеет вид
// #ID, где ID - идентификатор целевого узла
if (string.IsNullOrEmpty(binaryTokenReference) || binaryTokenReference[0] != '#')
{
throw new XmlException("Не удалось найти ссылку на сертификат");
}
// Получаем узел BinarySecurityToken с закодированным в base64 сертификатом
XmlElement binaryTokenElement = signedXml.GetIdElement(
xmlDocument, binaryTokenReference.Substring(1));
if (binaryTokenElement == null)
{
throw new XmlException("Не удалось найти сертификат");
}
// Создаём объект X509Certificate2
X509Certificate2 cert =
new X509Certificate2(Convert.FromBase64String(binaryTokenElement.InnerText));
// Проверяем подпись.
// ВНИМАНИЕ! Проверка сертификата в данном примере не осуществляется. Её необходимо
// реализовать самостоятельно в соответствии с требованиями к подписи проверяемого
// типа сообщения СМЭВ.
bool result = signedXml.CheckSignature(cert.PublicKey.Key);
// Выводим результат проверки подписи в консоль
if (result)
{
Console.WriteLine("Подпись №{0} верна.", curSignature + 1);
}
else
{
Console.WriteLine("Подпись №{0} не верна.", curSignature + 1);
}
}
}
// Класс SmevSignedXml - наследник класса SignedXml с перегруженным
// GetIdElement для корректной обработки атрибута wsu:Id.
class SmevSignedXml : SignedXml
{
public SmevSignedXml(XmlDocument document)
: base(document)
{
}
public override XmlElement GetIdElement(XmlDocument document, string idValue)
{
XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
nsmgr.AddNamespace("wsu", WSSecurityWSUNamespaceUrl);
return document.SelectSingleNode("//*[@wsu:Id='" + idValue + "']", nsmgr) as XmlElement;
}
}
public const string WSSecurityWSSENamespaceUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
public const string WSSecurityWSUNamespaceUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
}
|