Подпись ИЭМК: различия между версиями
Перейти к навигации
Перейти к поиску
Dmitriy (обсуждение | вклад) Новая страница: «Подпись с помощью CryptoPro 1. Установка Crypro Pro. 2. Установка Crypro Pro.Net 3. Установка Crypro Pro.Net SDK <pre>…» |
(нет различий)
|
Текущая версия от 12:37, 13 февраля 2015
Подпись с помощью CryptoPro 1. Установка Crypro Pro. 2. Установка Crypro Pro.Net 3. Установка Crypro Pro.Net SDK
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.Xml.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using CryptoPro.Sharpei;
using CryptoPro.Sharpei.Xml;
namespace Samples.Xml.cs
{
class SignSmevRequest
{
[STAThread]
static void Main(string[] args)
{
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.ReadKey();
}
static void SignXmlFile(string FileName, string SignedFileName, X509Certificate2 Certificate)
{
// Создаем новый документ XML.
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
// Читаем документ из файла.
doc.Load(new XmlTextReader(FileName));
// Создаём объект SmevSignedXml - наследник класса SignedXml с перегруженным GetIdElement
// для корректной обработки атрибута wsu:Id.
SmevSignedXml signedXml = new SmevSignedXml(doc);
// Задаём ключ подписи для документа SmevSignedXml.
signedXml.SigningKey = Certificate.PrivateKey;
var sign = 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);
// #wsa-300 !MessageID
Reference referenceWSA300 = new Reference();
referenceWSA300.Uri = "#WSA-300";
#pragma warning disable 612
referenceWSA300.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceWSA300.AddTransform(c14);
signedXml.AddReference(referenceWSA300);
// #wsa-301 !Action
Reference referenceWSA301 = new Reference();
referenceWSA301.Uri = "#WSA-301";
#pragma warning disable 612
referenceWSA301.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceWSA301.AddTransform(c14);
signedXml.AddReference(referenceWSA301);
// WSA-302 ! ReplyTo
Reference referenceWSA302 = new Reference();
referenceWSA302.Uri = "#WSA-302";
#pragma warning disable 612
referenceWSA302.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceWSA302.AddTransform(c14);
signedXml.AddReference(referenceWSA302);
// WSA-303 ! To
Reference referenceWSA303 = new Reference();
referenceWSA303.Uri = "#WSA-303";
#pragma warning disable 612
referenceWSA303.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceWSA303.AddTransform(c14);
signedXml.AddReference(referenceWSA303);
// #TRHEAD
Reference referenceTH = new Reference();
referenceTH.Uri = "#TRHEAD";
#pragma warning disable 612
referenceTH.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceTH.AddTransform(c14);
signedXml.AddReference(referenceTH);
//
doc.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText =
Convert.ToBase64String(Certificate.RawData);
// #SenderCertificate
Reference referenceSS = new Reference();
referenceSS.Uri = "#CertId";
#pragma warning disable 612
referenceSS.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
#pragma warning restore 612
referenceSS.AddTransform(c14);
signedXml.AddReference(referenceSS);
// Задаём преобразование для приведения узла 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("Signature")[0].PrependChild(
doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignatureValue")[0], true));
doc.GetElementsByTagName("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);
}
}
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);
}
}
}
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";
}
}