CryptoPro

Sign and verify SOAP messages for SMEV using .NET

Mar 14th, 2012
2,541
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.37 KB | None | 0 0
  1. using System;
  2. using System.Text;
  3. using System.Security.Cryptography.X509Certificates;
  4. using System.Xml;
  5. using System.Security.Cryptography.Xml;
  6.  
  7. namespace Samples.Xml.cs
  8. {
  9.     class SignSmevRequest
  10.     {
  11.         [STAThread]
  12.         static void Main(string[] args)
  13.         {
  14.             // Разбираем аргументы
  15.             if (args.Length < 2)
  16.             {
  17.                 Console.WriteLine("<doc_to_sign> <signed_doc>");
  18.                 return;
  19.             }
  20.  
  21.             X509Store certStore = new X509Store(StoreLocation.CurrentUser);
  22.             certStore.Open(OpenFlags.ReadOnly);
  23.             X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(
  24.                                                                             certStore.Certificates,
  25.                                                                             "Выберите сертификат",
  26.                                                                             "Пожалуйста, выберите сертификат электронной подписи",
  27.                                                                             X509SelectionFlag.SingleSelection);
  28.  
  29.             if (certs.Count == 0)
  30.             {
  31.                 Console.WriteLine("Сертификат не выбран.");
  32.                 return;
  33.             }
  34.  
  35.             // Подписываем запрос
  36.             SignXmlFile(args[0], args[1], certs[0]);
  37.  
  38.             // Проверяем подпись
  39.             VerifyXmlFile(args[1]);
  40.         }
  41.  
  42.         static void SignXmlFile(string FileName, string SignedFileName, X509Certificate2 Certificate)
  43.         {
  44.             System.Xml.XmlDocument doc = new XmlDocument();
  45.             doc.PreserveWhitespace = false;
  46.             doc.Load(new XmlTextReader(FileName));
  47.  
  48.             // Класс с перегруженным GetIdElement для корректной обработки wsu:Id
  49.             MySignedXml signedXml = new MySignedXml(doc);
  50.             signedXml.SigningKey = Certificate.PrivateKey;
  51.             Reference reference = new Reference();
  52.             reference.Uri = "#body";
  53. #pragma warning disable 612
  54.             //warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete
  55.  
  56.             reference.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete;
  57.  
  58. #pragma warning restore 612
  59.  
  60.             XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
  61.             reference.AddTransform(env);
  62.             XmlDsigExcC14NTransform c14 = new XmlDsigExcC14NTransform();
  63.             reference.AddTransform(c14);
  64.             signedXml.AddReference(reference);
  65.             KeyInfo keyInfo = new KeyInfo();
  66.             keyInfo.AddClause(new KeyInfoX509Data(Certificate));
  67.             signedXml.KeyInfo = keyInfo;
  68.             signedXml.SignedInfo.CanonicalizationMethod = c14.Algorithm;
  69.  
  70. #pragma warning disable 612
  71.             //warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete
  72.  
  73.             signedXml.SignedInfo.SignatureMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3410UrlObsolete;
  74.  
  75. #pragma warning restore 612
  76.  
  77.             signedXml.ComputeSignature();
  78.             XmlElement xmlDigitalSignature = signedXml.GetXml();
  79.             doc.GetElementsByTagName("ds:Signature")[0].PrependChild(doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignatureValue")[0], true));
  80.             doc.GetElementsByTagName("ds:Signature")[0].PrependChild(doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignedInfo")[0], true));
  81.             doc.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText = xmlDigitalSignature.GetElementsByTagName("X509Certificate")[0].InnerText;
  82.  
  83.             using (XmlTextWriter xmltw = new XmlTextWriter(SignedFileName,
  84.                 new UTF8Encoding(false)))
  85.             {
  86.                 doc.WriteTo(xmltw);
  87.             }
  88.         }
  89.  
  90.         static void VerifyXmlFile(string SignedFileName)
  91.         {
  92.             // Создаем новый XML документ в памяти.
  93.             XmlDocument xmlDocument = new XmlDocument();
  94.  
  95.             // Сохраняем все пробельные символы, они важны при проверке
  96.             // подписи.
  97.             xmlDocument.PreserveWhitespace = true;
  98.  
  99.             // Загружаем подписанный документ из файла.
  100.             xmlDocument.Load(SignedFileName);
  101.  
  102.             // Ищем все node "Signature" и сохраняем их в объекте XmlNodeList
  103.             XmlNodeList nodeList = xmlDocument.GetElementsByTagName(
  104.                 "Signature", SignedXml.XmlDsigNamespaceUrl);
  105.  
  106.             Console.WriteLine("Найдено:{0} подпис(ей).", nodeList.Count);
  107.  
  108.             // Проверяем все подписи.
  109.             for (int curSignature = 0; curSignature < nodeList.Count; curSignature++)
  110.             {
  111.                 // Создаем объект SignedXml для проверки подписи документа.
  112.                 MySignedXml signedXml = new MySignedXml(xmlDocument);
  113.  
  114.                 // Загружаем узел с подписью.
  115.                 signedXml.LoadXml((XmlElement)nodeList[curSignature]);
  116.  
  117.                 // SignXml самостоятельно не найдет сертификат подписи
  118.                 // т.к. он лежит вне узла Signature.
  119.                 // Поэтому самостоятельно извлечем сертификат по ссылке из KeyInfo
  120.                 // и явно зададим открытый ключ для проверки подписи.
  121.                 XmlNodeList referenceList = signedXml.KeyInfo.GetXml().GetElementsByTagName(
  122.                                                                                     "Reference",
  123.                                                                                     "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
  124.  
  125.                 if (referenceList.Count == 0)
  126.                 {
  127.                     throw new XmlException("Не удалось найти ссылку на сертификат");
  128.                 }
  129.  
  130.                 // Ищем среди аттрибутов ссылку на сертификат.
  131.                 string binaryTokenReference = null;
  132.                 foreach (XmlAttribute attribute in referenceList[0].Attributes)
  133.                 {
  134.                     if (attribute.Name.ToUpper().Equals("URI"))
  135.                     {
  136.                         // Получаем ссылку на сертификат.
  137.                         // формат #Value. '#' - нужно выбросить.
  138.                         binaryTokenReference = attribute.Value.Substring(1);
  139.                         break;
  140.                     }
  141.                 }
  142.  
  143.                 if (string.IsNullOrEmpty(binaryTokenReference))
  144.                 {
  145.                     throw new XmlException("Не удалось найти ссылку на сертификат");
  146.                 }
  147.  
  148.                 // Получаем узел BinarySecurityToken с закодированным в base64 сертификатом
  149.                 XmlElement binaryTokenElement = signedXml.GetIdElement(xmlDocument, binaryTokenReference);
  150.  
  151.                 if (binaryTokenElement == null)
  152.                 {
  153.                     throw new XmlException("Не удалось найти сертификат");
  154.                 }
  155.                
  156.                 // Декодируем сертификат
  157.                 byte[] certBytes = Convert.FromBase64String(binaryTokenElement.InnerText);
  158.                 X509Certificate2 cert = new X509Certificate2();
  159.                 cert.Import(certBytes);
  160.  
  161.                 // Проверяем подпись и выводим результат.
  162.                 bool result = signedXml.CheckSignature(cert.PublicKey.Key);
  163.  
  164.                 // Выводим результат проверки подписи в консоль.
  165.                 if (result)
  166.                     Console.WriteLine("XML подпись[{0}] верна.", curSignature + 1);
  167.                 else
  168.                     Console.WriteLine("XML подпись[{0}] не верна.", curSignature + 1);
  169.             }
  170.         }
  171.  
  172.         //Класс MySignedXml
  173.         class MySignedXml : SignedXml
  174.         {
  175.             public MySignedXml(XmlDocument document)
  176.                 : base(document)
  177.             {
  178.             }
  179.             public override XmlElement GetIdElement(XmlDocument document, string idValue)
  180.             {
  181.                 XmlNameTable myXmlNameTable = new NameTable();
  182.                 XmlNamespaceManager myNamespacemanager = new XmlNamespaceManager(myXmlNameTable);
  183.                 myNamespacemanager.AddNamespace("wsu",
  184.                         "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
  185.                 XmlNodeList lst = document.SelectNodes("//*[@wsu:Id='" + idValue + "' or @wsu:ID='" + idValue +
  186.                         "' or @wsu:ID='" + idValue + "']", myNamespacemanager);
  187.                 if (lst.Count != 1)
  188.                     return null;
  189.                 return (XmlElement)lst.Item(0);
  190.             }
  191.         }
  192.     }
  193. }
Advertisement
Add Comment
Please, Sign In to add comment