diff --git a/DotnetSampleProject.csproj b/DotnetSampleProject.csproj new file mode 100644 index 0000000..9cc998e --- /dev/null +++ b/DotnetSampleProject.csproj @@ -0,0 +1,20 @@ + + + Exe + netcoreapp3.1 + win-x64 + + Microsoft.Private.CoreFx.NETCoreApp;runtime.win-x64.Microsoft.Private.CoreFx.NETCoreApp;$(PackageConflictPreferredPackages) + + + + + + + ..\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Pkcs.dll + + + ..\corefx\artifacts\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Security.Cryptography.Xml.dll + + + \ No newline at end of file diff --git a/DotnetSampleProject.sln b/DotnetSampleProject.sln new file mode 100644 index 0000000..41a26d7 --- /dev/null +++ b/DotnetSampleProject.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetSampleProject", "DotnetSampleProject.csproj", "{8D81D66D-892A-46E4-B058-DAF52F5840CA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D81D66D-892A-46E4-B058-DAF52F5840CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D81D66D-892A-46E4-B058-DAF52F5840CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D81D66D-892A-46E4-B058-DAF52F5840CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D81D66D-892A-46E4-B058-DAF52F5840CA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7101067E-48BD-4F7C-8AF2-2299E6C596F6} + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..8cd3344 --- /dev/null +++ b/Program.cs @@ -0,0 +1,225 @@ +using System; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Xml; +using System.Security.Cryptography.Xml; + +namespace DotnetSample +{ + class Program + { + private static readonly byte[] bytesToHash = + new byte[] + { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + }; + + static void Main(string[] args) + { + var hash = HashAlgorithmName.Gost3411_2012_256; + Program.MakeMeCert(); + var gostCert = new X509Certificate2(@"MyCert.pfx", "1"); + var gostSk = gostCert.PrivateKey as Gost3410_2012_256CryptoServiceProvider; + var signed = gostSk.SignData(bytesToHash, hash); + + var gostPk = gostCert.GetGost3410_2012_256PublicKey(); + var dataValidationResult = gostPk.VerifyData(bytesToHash, signed, hash); + + Console.WriteLine($"Validation result: {dataValidationResult}"); + + Program.CreateSomeXml("test.txml"); + Program.SignXmlFile("test.txml", "test.xml.sig", gostSk, gostCert); + Program.ValidateXmlFIle("test.xml.sig"); + } + + /// + /// Создать сертификат ГОСТ 34.10.2012 256 + /// + private static void MakeMeCert() + { + using (var parent = new Gost3410_2012_256CryptoServiceProvider()) + { + var parentReq = new CertificateRequest( + "CN=Experimental Issuing Authority", + parent, + HashAlgorithmName.Gost3411_2012_256); + + parentReq.CertificateExtensions.Add( + new X509BasicConstraintsExtension(true, false, 0, true)); + + parentReq.CertificateExtensions.Add( + new X509SubjectKeyIdentifierExtension(parentReq.PublicKey, false)); + + using (var parentCert = parentReq.CreateSelfSigned( + DateTimeOffset.UtcNow.AddDays(-45), + DateTimeOffset.UtcNow.AddDays(365))) + { + byte[] certData = parentCert.Export(X509ContentType.Pfx, "1"); + File.WriteAllBytes(@"MyCert.pfx", certData); + } + } + } + + static void CreateSomeXml(string FileName) + { + string SourceDocument = "" + + "" + + " " + + "Here is some data to sign." + + ""; + + // Создать документ по строке + XmlDocument document = new XmlDocument(); + document.LoadXml(SourceDocument); + + // Сохранить подписываемый документ в файле. + using (XmlTextWriter xmltw = new XmlTextWriter(FileName, + new UTF8Encoding(false))) + { + xmltw.WriteStartDocument(); + document.WriteTo(xmltw); + } + } + + /// + /// Подпись xml файла + /// + static void SignXmlFile(string FileName, + string SignedFileName, AsymmetricAlgorithm Key, + X509Certificate Certificate) + { + // Создаем новый XML документ. + XmlDocument doc = new XmlDocument(); + + // Пробельные символы участвуют в вычислении подписи и должны быть сохранены для совместимости с другими реализациями. + doc.PreserveWhitespace = true; + + // Читаем документ из файла. + doc.Load(new XmlTextReader(FileName)); + + // Создаем объект SignedXml по XML документу. + SignedXml signedXml = new SignedXml(doc); + + // Добавляем ключ в SignedXml документ. + signedXml.SigningKey = Key; + + // Создаем ссылку на node для подписи. + // При подписи всего документа проставляем "". + Reference reference = new Reference(); + reference.Uri = ""; + + // Явно проставляем алгоритм хэширования, + // по умолчанию SHA1. + reference.DigestMethod = + SignedXml.XmlDsigGost3411_2012_256Url; + + // Добавляем transform на подписываемые данные + // для удаления вложенной подписи. + XmlDsigEnvelopedSignatureTransform env = + new XmlDsigEnvelopedSignatureTransform(); + reference.AddTransform(env); + + // Добавляем СМЭВ трансформ. + // начиная с .NET 4.5.1 для проверки подписи, необходимо добавить этот трансформ в довернные: + // signedXml.SafeCanonicalizationMethods.Add("urn://smev-gov-ru/xmldsig/transform"); + XmlDsigSmevTransform smev = + new XmlDsigSmevTransform(); + reference.AddTransform(smev); + + // Добавляем transform для канонизации. + XmlDsigC14NTransform c14 = new XmlDsigC14NTransform(); + reference.AddTransform(c14); + + // Добавляем ссылку на подписываемые данные + signedXml.AddReference(reference); + + // Создаем объект KeyInfo. + KeyInfo keyInfo = new KeyInfo(); + + // Добавляем сертификат в KeyInfo + keyInfo.AddClause(new KeyInfoX509Data(Certificate)); + + // Добавляем KeyInfo в SignedXml. + signedXml.KeyInfo = keyInfo; + + // Вычисляем подпись. + signedXml.ComputeSignature(); + + // Получаем XML представление подписи и сохраняем его + // в отдельном node. + XmlElement xmlDigitalSignature = signedXml.GetXml(); + + // Добавляем node подписи в XML документ. + doc.DocumentElement.AppendChild(doc.ImportNode( + xmlDigitalSignature, true)); + + // При наличии стартовой XML декларации ее удаляем + // (во избежание повторного сохранения) + if (doc.FirstChild is XmlDeclaration) + { + doc.RemoveChild(doc.FirstChild); + } + + // Сохраняем подписанный документ в файле. + using (XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, + new UTF8Encoding(false))) + { + xmltw.WriteStartDocument(); + doc.WriteTo(xmltw); + } + } + + /// + /// Проверка xml файла + /// + /// + static void ValidateXmlFIle(string path) + { + // Создаем новый XML документ в памяти. + XmlDocument xmlDocument = new XmlDocument(); + + // Сохраняем все пробельные символы, они важны при проверке + // подписи. + xmlDocument.PreserveWhitespace = true; + + // Загружаем подписанный документ из файла. + xmlDocument.Load(path); + + // Ищем все node "Signature" и сохраняем их в объекте XmlNodeList + XmlNodeList nodeList = xmlDocument.GetElementsByTagName( + "Signature", SignedXml.XmlDsigNamespaceUrl); + + Console.WriteLine("Найдено:{0} подпис(ей).", nodeList.Count); + + // Проверяем все подписи. + for (int curSignature = 0; curSignature < nodeList.Count; curSignature++) + { + // Создаем объект SignedXml для проверки подписи документа. + SignedXml signedXml = new SignedXml(xmlDocument); + + // начиная с .NET 4.5.1 для проверки подписи, необходимо добавить СМЭВ transform в довернные: + + signedXml.SafeCanonicalizationMethods.Add("urn://smev-gov-ru/xmldsig/transform"); + + // Загружаем узел с подписью. + signedXml.LoadXml((XmlElement)nodeList[curSignature]); + + // Проверяем подпись и выводим результат. + bool result = signedXml.CheckSignature(); + + // Выводим результат проверки подписи в консоль. + if (result) + { + Console.WriteLine("XML подпись[{0}] верна.", curSignature + 1); + } + else + { + Console.WriteLine("XML подпись[{0}] не верна.", curSignature + 1); + } + } + } + } +}