private static XmlElement Smev3Signed(Stream message, string id) { var document = new XmlDocument(); document.PreserveWhitespace = false; document.Load(message); var signedXml = new SmevSignedXml(document); using (var key = GostCryptoConfig.CreateGost3410AsymmetricAlgorithm()) { var reference = new Reference(); reference.Uri = "#" + id; reference.AddTransform(new XmlDsigExcC14NTransform()); reference.AddTransform(new XmlDsigSmevTransform()); signedXml.SigningKey = key; signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; if (GostCryptoConfig.ProviderType == ProviderTypes.CryptoPro256) { signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigGost3410_2012_256Url; reference.DigestMethod = SignedXml.XmlDsigGost3411_2012_256Url; } else if (GostCryptoConfig.ProviderType == ProviderTypes.CryptoPro512) { signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigGost3410_2012_512Url; reference.DigestMethod = SignedXml.XmlDsigGost3411_2012_512Url; } else { signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigGost3410UrlObsolete; reference.DigestMethod = SignedXml.XmlDsigGost3411UrlObsolete; } signedXml.AddReference(reference); var keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(key.ContainerCertificate)); signedXml.KeyInfo = keyInfo; signedXml.ComputeSignature("ds"); } var smevSign = signedXml.GetXml("ds"); return(smevSign); }
private static XmlDocument SignSmevRequest(XmlDocument smevRequest, Gost3410AsymmetricAlgorithm signingCertificate) { // Создание подписчика XML-документа var signedXml = new SmevSignedXml(smevRequest); // { GetIdElementHandler = GetSmevIdElement }; // Установка ключа для создания подписи signedXml.SigningKey = signingCertificate; // Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования ГОСТ Р 34.11-94 (в соответствии с методическими рекомендациями СМЭВ) var dataReference = new Reference { Uri = "#body", DigestMethod = SignedXml.XmlDsigGost3411UrlObsolete }; // Метод преобразования, применяемый к данным перед их подписью (в соответствии с методическими рекомендациями СМЭВ) var dataTransform = new XmlDsigExcC14NTransform(); dataReference.AddTransform(dataTransform); // Установка ссылки на узел signedXml.AddReference(dataReference); // Установка алгоритма нормализации узла SignedInfo (в соответствии с методическими рекомендациями СМЭВ) signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; // Установка алгоритма подписи ГОСТ Р 34.10-2001 (в соответствии с методическими рекомендациями СМЭВ) signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigGost3410UrlObsolete; // Вычисление подписи signedXml.ComputeSignature(); // Получение XML-представления подписи var signatureXml = signedXml.GetXml(); // Добавление подписи в исходный документ smevRequest.GetElementsByTagName("ds:Signature")[0].PrependChild(smevRequest.ImportNode(signatureXml.GetElementsByTagName("SignatureValue")[0], true)); smevRequest.GetElementsByTagName("ds:Signature")[0].PrependChild(smevRequest.ImportNode(signatureXml.GetElementsByTagName("SignedInfo")[0], true)); smevRequest.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText = Convert.ToBase64String(signingCertificate.ContainerCertificate.RawData); return(smevRequest); }
private static bool VerifySmevRequestSignature(XmlDocument signedSmevRequest) { // Создание подписчика XML-документа var signedXml = new SmevSignedXml(signedSmevRequest); // { GetIdElementHandler = GetSmevIdElement }; // Поиск узла с подписью var nodeList = signedSmevRequest.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl); // Загрузка найденной подписи signedXml.LoadXml((XmlElement)nodeList[0]); // Поиск ссылки на BinarySecurityToken var references = signedXml.KeyInfo.GetXml().GetElementsByTagName("Reference", WsSecurityExtNamespace); if (references.Count > 0) { // Определение ссылки на сертификат (ссылка на узел документа) var binaryTokenReference = ((XmlElement)references[0]).GetAttribute("URI"); if (!String.IsNullOrEmpty(binaryTokenReference) && binaryTokenReference[0] == '#') { // Поиск элемента с закодированным в Base64 сертификатом var binaryTokenElement = signedXml.GetIdElement(signedSmevRequest, binaryTokenReference.Substring(1)); if (binaryTokenElement != null) { // Загрузка сертификата, который был использован для подписи var signingCertificate = new X509Certificate(Convert.FromBase64String(binaryTokenElement.InnerText)); // Проверка подписи return(signedXml.CheckSignature(signingCertificate.PublicKey.Key)); } } } return(false); }
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 = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #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 = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3410UrlObsolete; #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); } } }
/// <summary> /// Метод подписи XML подписью органа власти /// </summary> /// <param name="doc"></param> /// <param name="certificate"></param> /// <returns></returns> private XmlDocument SignMessage2XX(XmlDocument xml, IntPtr certificate) { try { // Удаляем тэг Actor string message = string.Empty; try { log.LogDebug("Пытаемся удалить атрибут 'Actor'."); message = SoapDSigUtil.RemoveActor(xml); log.LogDebug("Атрибут 'Actor' успешно удален."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке удалить атрибут 'Actor'. {ex.Message}."); } XmlDocument doc = new XmlDocument() { PreserveWhitespace = true }; try { doc.LoadXml(message); } catch (Exception ex) { throw new Exception($"Ошибка при формировании XML после удаления атрибута 'Actor'. {ex.Message}."); } try { log.LogDebug("Получаем значение тэга для подписи."); this.tagForSign = this.FindSmevTagForSign(doc); log.LogDebug($"Значение тэга для подписи получено. {tagForSign}."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке получить тэг с элементом для подписи. {ex.Message}."); } SmevSignedXml signedXml = new SmevSignedXml(doc); try { log.LogDebug($"Выполняем добавление элемента Reference в XML."); signedXml = (SmevSignedXml)SmevXmlHelper.AddReference(doc, signedXml, certificate, SignWithId, mrVersion, ElemForSign, ref idCounter, tagForSign, tagForSignNamespaceUri, namespaceIdAttr: NamespaceUri.OasisWSSecurityUtility ); log.LogDebug($"Добавление элемента Reference в XML выполнено успешно."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке добавить элемент Reference. {ex.Message}."); } signedXml.NamespaceForReference = NamespaceUri.OasisWSSecurityUtility; signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; try { log.LogDebug($"Пытаемся получить значение SignatureMethod."); signedXml.SignedInfo.SignatureMethod = SignServiceUtils.GetSignatureMethod(SignServiceUtils.GetAlgId(certificate)); log.LogDebug($"Значение SignatureMethod успешно получено: {signedXml.SignedInfo.SignatureMethod}."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке получить значение метода подписи. {ex.Message}."); } XmlElement keyInfoElem = doc.CreateElement("KeyInfo", NamespaceUri.WSXmlDSig); XmlElement binaryTokenElem = doc.CreateElement("wsse", "BinarySecurityToken", NamespaceUri.OasisWSSecuritySecext); XmlElement tokenReferenceElem = doc.CreateElement("wsse", "SecurityTokenReference", NamespaceUri.OasisWSSecuritySecext); XmlElement referenceElem = doc.CreateElement("wsse", "Reference", NamespaceUri.OasisWSSecuritySecext); string certId = "uuid-" + Guid.NewGuid().ToString(); XmlAttribute idAttr = doc.CreateAttribute("u", "Id", NamespaceUri.OasisWSSecurityUtility); XmlAttribute valueTypeTokenAttr = doc.CreateAttribute("ValueType"); XmlAttribute encodingTypeTokenAttr = doc.CreateAttribute("EncodingType"); idAttr.Value = certId; valueTypeTokenAttr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; encodingTypeTokenAttr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; binaryTokenElem.Attributes.Append((XmlAttribute)doc.ImportNode(idAttr, true)); binaryTokenElem.Attributes.Append((XmlAttribute)doc.ImportNode(valueTypeTokenAttr, true)); binaryTokenElem.Attributes.Append((XmlAttribute)doc.ImportNode(encodingTypeTokenAttr, true)); X509Certificate2 cert = SignServiceUtils.GetX509Certificate2(certificate); binaryTokenElem.InnerText = Convert.ToBase64String(cert.RawData); XmlAttribute valueTypeAttr = doc.CreateAttribute("ValueType"); valueTypeAttr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; referenceElem.Attributes.Append((XmlAttribute)doc.ImportNode(valueTypeAttr, true)); XmlAttribute uriAttr = doc.CreateAttribute("URI"); uriAttr.Value = "#" + certId; referenceElem.Attributes.Append((XmlAttribute)doc.ImportNode(uriAttr, true)); tokenReferenceElem.PrependChild(doc.ImportNode(referenceElem, true)); keyInfoElem.PrependChild(doc.ImportNode(tokenReferenceElem, true)); try { KeyInfoNode keyNode = new KeyInfoNode(tokenReferenceElem); KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(keyNode); signedXml.KeyInfo = keyInfo; } catch (Exception ex) { throw new Exception($"Ошибка при формировании элемента KeyInfo. {ex.Message}."); } try { log.LogDebug($"Пытаемся вычислить подпись."); signedXml.ComputeSignatureWithoutPrivateKey(xmldsigPrefix, certificate); log.LogDebug($"Вычисление подписи выполнено успешно."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке вычислить подпись для XML. {ex.Message}."); } XmlElement signatureElem = null; try { log.LogDebug("Пытаемся получить элемент с подписью."); signatureElem = signedXml.GetXml(xmldsigPrefix); log.LogDebug("Элемент с подписью успешно получен."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке получить элемент содержащий подпись. {ex.Message}."); } try { log.LogDebug("Пытаемся добавить подпись в XML содержимое."); FillSignatureElement(doc, signatureElem, certificate, binaryTokenElem); log.LogDebug("Подпись успешно добавлена."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке заполнить XML информацией о подписи. {ex.Message}."); } try { log.LogDebug("Пытаемся добавить атрибут 'Actor'."); SoapDSigUtil.AddActor(doc); log.LogDebug("Атрибут 'Actor' успешно добавлен."); } catch (Exception ex) { throw new Exception($"Ошибка при попытке добавить атрибут 'Actor'. {ex.Message}."); } return(doc); } catch (Exception ex) { log.LogError($"Ошибка при попытке подписать XML. {ex.Message}."); throw new CryptographicException($"Ошибка при попытке подписать XML для версии {mrText[mrVersion]}. {ex.Message}."); } finally { SignServiceUtils.FreeHandleCertificate(certificate); } }