Add support for ГОСТ Р 34.10 digital signatures

- Implemented the GostKeyValue class for handling public key parameters in ГОСТ Р 34.10 digital signatures.
- Created the GostSignedXml class to manage XML signatures using ГОСТ 34.10, including methods for computing and checking signatures.
- Developed the GostSignedXmlImpl class to encapsulate the signature computation logic and public key retrieval.
- Added specific key value classes for ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2012/256, and ГОСТ Р 34.10-2012/512 to support different signature algorithms.
- Ensured compatibility with existing XML signature standards while integrating ГОСТ cryptography.
This commit is contained in:
master
2025-11-09 21:59:57 +02:00
parent 75c2bcafce
commit cef4cb2c5a
486 changed files with 32952 additions and 801 deletions

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<MyXml>
<SomeElement Encrypt="false">
Here is public data.
</SomeElement>
<SomeElement Encrypt="true">
Here is private data.
</SomeElement>
<SomeElement Encrypt="true">
Here is private data.
</SomeElement>
<SomeElement Encrypt="true">
Here is private data.
</SomeElement>
</MyXml>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<MyXml>
<SomeElement Id="Id1">
Here is some data to sign.
</SomeElement>
</MyXml>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" 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#">
<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"
>
</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>

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net40;net452</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Security" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0-preview.2" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GostCryptography\GostCryptography.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,103 @@
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием случайного сессионного ключа.
/// </summary>
/// <remarks>
/// Тест имитирует обмен данными между условным отправителем, который шифрует заданный поток байт, и условным получателем, который дешифрует
/// зашифрованный поток байт. Шифрация осуществляется с использованием случайного симметричного ключа, который в свою очередь шифруется
/// с использованием открытого ключа получателя. Соответственно для дешифрации данных сначала расшифровывается случайный симметричный ключ
/// с использованием закрытого ключа получателя.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием случайного сессионного ключа")]
public class EncryptDecryptSessionKeyTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptAndDecrypt(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
var encryptedDataStream = SendEncryptedDataStream(publicKey, dataStream, out var iv, out var sessionKey);
var decryptedDataStream = ReceiveEncryptedDataStream(privateKey, encryptedDataStream, iv, sessionKey);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream SendEncryptedDataStream(GostAsymmetricAlgorithm publicKey, Stream dataStream, out byte[] iv, out byte[] sessionKey)
{
var encryptedDataStream = new MemoryStream();
// Отправитель создает случайный сессионный ключ для шифрации данных
using (var senderSessionKey = new Gost_28147_89_SymmetricAlgorithm(publicKey.ProviderType))
{
// Отправитель передает получателю вектор инициализации
iv = senderSessionKey.IV;
// Отправитель шифрует сессионный ключ и передает его получателю
var formatter = publicKey.CreateKeyExchangeFormatter();
sessionKey = formatter.CreateKeyExchangeData(senderSessionKey);
// Отправитель шифрует данные с использованием сессионного ключа
using (var encryptor = senderSessionKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream ReceiveEncryptedDataStream(GostAsymmetricAlgorithm privateKey, Stream encryptedDataStream, byte[] iv, byte[] sessionKey)
{
var decryptedDataStream = new MemoryStream();
var deformatter = privateKey.CreateKeyExchangeDeformatter();
// Получатель принимает от отправителя зашифрованный сессионный ключ и дешифрует его
using (var receiverSessionKey = deformatter.DecryptKeyExchangeAlgorithm(sessionKey))
{
// Получатель принимает от отправителя вектор инициализации
receiverSessionKey.IV = iv;
// Получатель дешифрует данные с использованием сессионного ключа
using (var decryptor = receiverSessionKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
}
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,83 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Вычисление имитовставки на базе общего симметричного ключа ГОСТ 28147-89.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием имитовставки.
/// </remarks>
[TestFixture(Description = "Вычисление имитовставки на базе общего симметричного ключа ГОСТ 28147-89")]
public class Gost_28147_89_ImitHashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeImitHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
var imitDataStream = CreateImitDataStream(sharedKey, dataStream);
var isValidImitDataStream = VerifyImitDataStream(sharedKey, imitDataStream);
// Then
Assert.IsTrue(isValidImitDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data for imit..."));
}
private static Stream CreateImitDataStream(Gost_28147_89_SymmetricAlgorithmBase sharedKey, Stream dataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_28147_89_ImitHashAlgorithm(sharedKey))
{
// Вычисление имитовставки для потока данных
var imitHashValue = imitHash.ComputeHash(dataStream);
// Запись имитовставки в начало выходного потока данных
var imitDataStream = new MemoryStream();
imitDataStream.Write(imitHashValue, 0, imitHashValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(imitDataStream);
imitDataStream.Position = 0;
return imitDataStream;
}
}
private static bool VerifyImitDataStream(Gost_28147_89_SymmetricAlgorithmBase sharedKey, Stream imitDataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_28147_89_ImitHashAlgorithm(sharedKey))
{
// Считывание имитовставки из потока данных
var imitHashValue = new byte[imitHash.HashSize / 8];
imitDataStream.Read(imitHashValue, 0, imitHashValue.Length);
// Вычисление реального значения имитовставки для потока данных
var expectedImitHashValue = imitHash.ComputeHash(imitDataStream);
// Сравнение исходной имитовставки с ожидаемой
return imitHashValue.SequenceEqual(expectedImitHashValue);
}
}
}
}

View File

@@ -0,0 +1,77 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ 28147-89.
/// </summary>
/// <remarks>
/// Тест создает поток байт, шифрует его с использованием общего симметричного ключа,
/// а затем дешифрует зашифрованные данные и проверяет корректность дешифрации.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ 28147-89")]
public class Gost_28147_89_SymmetricAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldEncryptAndDecrypt(ProviderType providerType)
{
// Given
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
var dataStream = CreateDataStream();
// When
var encryptedDataStream = EncryptDataStream(sharedKey, dataStream);
var decryptedDataStream = DecryptDataStream(sharedKey, encryptedDataStream);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream EncryptDataStream(SymmetricAlgorithm sharedKey, Stream dataStream)
{
var encryptedDataStream = new MemoryStream();
using (var encryptor = sharedKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream DecryptDataStream(SymmetricAlgorithm sharedKey, Stream encryptedDataStream)
{
var decryptedDataStream = new MemoryStream();
using (var decryptor = sharedKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
decryptedDataStream.Flush();
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,103 @@
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием случайного сессионного ключа ГОСТ Р 34.12-2015 Кузнечик.
/// </summary>
/// <remarks>
/// Тест имитирует обмен данными между условным отправителем, который шифрует заданный поток байт, и условным получателем, который дешифрует
/// зашифрованный поток байт. Шифрация осуществляется с использованием случайного симметричного ключа, который в свою очередь шифруется
/// с использованием открытого ключа получателя. Соответственно для дешифрации данных сначала расшифровывается случайный симметричный ключ
/// с использованием закрытого ключа получателя.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием случайного сессионного ключа ГОСТ Р 34.12-2015 Кузнечик")]
public class KuznyechikEncryptDecryptSessionKeyTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptAndDecrypt(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
var encryptedDataStream = SendEncryptedDataStream(publicKey, dataStream, out var iv, out var sessionKey);
var decryptedDataStream = ReceiveEncryptedDataStream(privateKey, encryptedDataStream, iv, sessionKey);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream SendEncryptedDataStream(GostAsymmetricAlgorithm publicKey, Stream dataStream, out byte[] iv, out byte[] sessionKey)
{
var encryptedDataStream = new MemoryStream();
// Отправитель создает случайный сессионный ключ для шифрации данных
using (var senderSessionKey = new Gost_3412_K_SymmetricAlgorithm(publicKey.ProviderType))
{
// Отправитель передает получателю вектор инициализации
iv = senderSessionKey.IV;
// Отправитель шифрует сессионный ключ и передает его получателю
var formatter = publicKey.CreateKeyExchangeFormatter();
sessionKey = formatter.CreateKeyExchangeData(senderSessionKey);
// Отправитель шифрует данные с использованием сессионного ключа
using (var encryptor = senderSessionKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream ReceiveEncryptedDataStream(GostAsymmetricAlgorithm privateKey, Stream encryptedDataStream, byte[] iv, byte[] sessionKey)
{
var decryptedDataStream = new MemoryStream();
var deformatter = privateKey.CreateKeyExchangeDeformatter();
// Получатель принимает от отправителя зашифрованный сессионный ключ и дешифрует его
using (var receiverSessionKey = deformatter.DecryptKeyExchangeAlgorithm(sessionKey))
{
// Получатель принимает от отправителя вектор инициализации
receiverSessionKey.IV = iv;
// Получатель дешифрует данные с использованием сессионного ключа
using (var decryptor = receiverSessionKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
}
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,83 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Вычисление имитовставки на базе общего симметричного ключа ГОСТ Р 34.12-2015 Кузнечик.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием имитовставки.
/// </remarks>
[TestFixture(Description = "Вычисление имитовставки на базе общего симметричного ключа ГОСТ Р 34.12-2015 Кузнечик")]
public class KuznyechikImitHashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeImitHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_3412_K_SymmetricAlgorithm(providerType);
// When
var imitDataStream = CreateImitDataStream(sharedKey, dataStream);
var isValidImitDataStream = VerifyImitDataStream(sharedKey, imitDataStream);
// Then
Assert.IsTrue(isValidImitDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data for imit..."));
}
private static Stream CreateImitDataStream(Gost_3412_K_SymmetricAlgorithm sharedKey, Stream dataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_3412_K_ImitHashAlgorithm(sharedKey))
{
// Вычисление имитовставки для потока данных
var imitHashValue = imitHash.ComputeHash(dataStream);
// Запись имитовставки в начало выходного потока данных
var imitDataStream = new MemoryStream();
imitDataStream.Write(imitHashValue, 0, imitHashValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(imitDataStream);
imitDataStream.Position = 0;
return imitDataStream;
}
}
private static bool VerifyImitDataStream(Gost_3412_K_SymmetricAlgorithm sharedKey, Stream imitDataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_3412_K_ImitHashAlgorithm(sharedKey))
{
// Считывание имитовставки из потока данных
var imitHashValue = new byte[imitHash.HashSize / 8];
imitDataStream.Read(imitHashValue, 0, imitHashValue.Length);
// Вычисление реального значения имитовставки для потока данных
var expectedImitHashValue = imitHash.ComputeHash(imitDataStream);
// Сравнение исходной имитовставки с ожидаемой
return imitHashValue.SequenceEqual(expectedImitHashValue);
}
}
}
}

View File

@@ -0,0 +1,77 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ Р 34.12-2015 Кузнечик.
/// </summary>
/// <remarks>
/// Тест создает поток байт, шифрует его с использованием общего симметричного ключа,
/// а затем дешифрует зашифрованные данные и проверяет корректность дешифрации.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ Р 34.12-2015 Кузнечик")]
public class KuznyechikSymmetricAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldEncryptAndDecrypt(ProviderType providerType)
{
// Given
var sharedKey = new Gost_3412_K_SymmetricAlgorithm(providerType);
var dataStream = CreateDataStream();
// When
var encryptedDataStream = EncryptDataStream(sharedKey, dataStream);
var decryptedDataStream = DecryptDataStream(sharedKey, encryptedDataStream);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream EncryptDataStream(SymmetricAlgorithm sharedKey, Stream dataStream)
{
var encryptedDataStream = new MemoryStream();
using (var encryptor = sharedKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream DecryptDataStream(SymmetricAlgorithm sharedKey, Stream encryptedDataStream)
{
var decryptedDataStream = new MemoryStream();
using (var decryptor = sharedKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
decryptedDataStream.Flush();
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,103 @@
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием случайного сессионного ключа ГОСТ Р 34.12-2015 Магма.
/// </summary>
/// <remarks>
/// Тест имитирует обмен данными между условным отправителем, который шифрует заданный поток байт, и условным получателем, который дешифрует
/// зашифрованный поток байт. Шифрация осуществляется с использованием случайного симметричного ключа, который в свою очередь шифруется
/// с использованием открытого ключа получателя. Соответственно для дешифрации данных сначала расшифровывается случайный симметричный ключ
/// с использованием закрытого ключа получателя.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием случайного сессионного ключа ГОСТ Р 34.12-2015 Магма")]
public class MagmaEncryptDecryptSessionKeyTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptAndDecrypt(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
var encryptedDataStream = SendEncryptedDataStream(publicKey, dataStream, out var iv, out var sessionKey);
var decryptedDataStream = ReceiveEncryptedDataStream(privateKey, encryptedDataStream, iv, sessionKey);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream SendEncryptedDataStream(GostAsymmetricAlgorithm publicKey, Stream dataStream, out byte[] iv, out byte[] sessionKey)
{
var encryptedDataStream = new MemoryStream();
// Отправитель создает случайный сессионный ключ для шифрации данных
using (var senderSessionKey = new Gost_3412_M_SymmetricAlgorithm(publicKey.ProviderType))
{
// Отправитель передает получателю вектор инициализации
iv = senderSessionKey.IV;
// Отправитель шифрует сессионный ключ и передает его получателю
var formatter = publicKey.CreateKeyExchangeFormatter();
sessionKey = formatter.CreateKeyExchangeData(senderSessionKey);
// Отправитель шифрует данные с использованием сессионного ключа
using (var encryptor = senderSessionKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream ReceiveEncryptedDataStream(GostAsymmetricAlgorithm privateKey, Stream encryptedDataStream, byte[] iv, byte[] sessionKey)
{
var decryptedDataStream = new MemoryStream();
var deformatter = privateKey.CreateKeyExchangeDeformatter();
// Получатель принимает от отправителя зашифрованный сессионный ключ и дешифрует его
using (var receiverSessionKey = deformatter.DecryptKeyExchangeAlgorithm(sessionKey))
{
// Получатель принимает от отправителя вектор инициализации
receiverSessionKey.IV = iv;
// Получатель дешифрует данные с использованием сессионного ключа
using (var decryptor = receiverSessionKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
}
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,83 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Вычисление имитовставки на базе общего симметричного ключа ГОСТ Р 34.12-2015 Магма.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием имитовставки.
/// </remarks>
[TestFixture(Description = "Вычисление имитовставки на базе общего симметричного ключа ГОСТ Р 34.12-2015 Магма")]
public class MagmaImitHashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeImitHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_3412_M_SymmetricAlgorithm(providerType);
// When
var imitDataStream = CreateImitDataStream(sharedKey, dataStream);
var isValidImitDataStream = VerifyImitDataStream(sharedKey, imitDataStream);
// Then
Assert.IsTrue(isValidImitDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data for imit..."));
}
private static Stream CreateImitDataStream(Gost_3412_M_SymmetricAlgorithm sharedKey, Stream dataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_3412_M_ImitHashAlgorithm(sharedKey))
{
// Вычисление имитовставки для потока данных
var imitHashValue = imitHash.ComputeHash(dataStream);
// Запись имитовставки в начало выходного потока данных
var imitDataStream = new MemoryStream();
imitDataStream.Write(imitHashValue, 0, imitHashValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(imitDataStream);
imitDataStream.Position = 0;
return imitDataStream;
}
}
private static bool VerifyImitDataStream(Gost_3412_M_SymmetricAlgorithm sharedKey, Stream imitDataStream)
{
// Создание объекта для вычисления имитовставки
using (var imitHash = new Gost_3412_M_ImitHashAlgorithm(sharedKey))
{
// Считывание имитовставки из потока данных
var imitHashValue = new byte[imitHash.HashSize / 8];
imitDataStream.Read(imitHashValue, 0, imitHashValue.Length);
// Вычисление реального значения имитовставки для потока данных
var expectedImitHashValue = imitHash.ComputeHash(imitDataStream);
// Сравнение исходной имитовставки с ожидаемой
return imitHashValue.SequenceEqual(expectedImitHashValue);
}
}
}
}

View File

@@ -0,0 +1,77 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_28147_89
{
/// <summary>
/// Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ Р 34.12-2015 Магма.
/// </summary>
/// <remarks>
/// Тест создает поток байт, шифрует его с использованием общего симметричного ключа,
/// а затем дешифрует зашифрованные данные и проверяет корректность дешифрации.
/// </remarks>
[TestFixture(Description = "Шифрование и дешифрование данных с использованием общего симметричного ключа ГОСТ Р 34.12-2015 Магма")]
public class MagmaSymmetricAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldEncryptAndDecrypt(ProviderType providerType)
{
// Given
var sharedKey = new Gost_3412_M_SymmetricAlgorithm(providerType);
var dataStream = CreateDataStream();
// When
var encryptedDataStream = EncryptDataStream(sharedKey, dataStream);
var decryptedDataStream = DecryptDataStream(sharedKey, encryptedDataStream);
// Then
Assert.That(dataStream, Is.EqualTo(decryptedDataStream));
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to encrypt..."));
}
private static Stream EncryptDataStream(SymmetricAlgorithm sharedKey, Stream dataStream)
{
var encryptedDataStream = new MemoryStream();
using (var encryptor = sharedKey.CreateEncryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, encryptor, CryptoStreamMode.Write);
dataStream.CopyTo(cryptoStream);
cryptoStream.FlushFinalBlock();
}
encryptedDataStream.Position = 0;
return encryptedDataStream;
}
private static Stream DecryptDataStream(SymmetricAlgorithm sharedKey, Stream encryptedDataStream)
{
var decryptedDataStream = new MemoryStream();
using (var decryptor = sharedKey.CreateDecryptor())
{
var cryptoStream = new CryptoStream(encryptedDataStream, decryptor, CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedDataStream);
decryptedDataStream.Flush();
}
decryptedDataStream.Position = 0;
return decryptedDataStream;
}
}
}

View File

@@ -0,0 +1,129 @@
using System;
using System.Security;
using GostCryptography.Gost_R3410;
using NUnit.Framework;
using System.Security.Cryptography.X509Certificates;
using GostCryptography.Base;
namespace GostCryptography.Tests.Gost_R3410
{
[TestFixture(Description = "Проверка возможности установки пароля для контейнера ключей")]
public class SetContainerPasswordTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2001_Certificates))]
public void ShouldSetContainerPassword_R3410_2001(TestCertificateInfo testCase)
{
// Given
var data = GetSomeData();
var certificate = testCase.Certificate;
var securePassword = CreateSecureString(TestConfig.ContainerPassword);
// When
var privateKeyInfo = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2001_AsymmetricAlgorithm(privateKeyInfo);
privateKey.SetContainerPassword(securePassword);
var signature = CreateSignature(privateKey, data);
var isValidSignature = VerifySignature(privateKey, data, signature);
// Then
Assert.IsTrue(isValidSignature);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_256_Certificates))]
public void ShouldSetContainerPassword_R3410_2012_256(TestCertificateInfo testCase)
{
// Given
var data = GetSomeData();
var certificate = testCase.Certificate;
var securePassword = CreateSecureString(TestConfig.ContainerPassword);
// When
var privateKeyInfo = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2012_256_AsymmetricAlgorithm(privateKeyInfo);
privateKey.SetContainerPassword(securePassword);
var signature = CreateSignature(privateKey, data);
var isValidSignature = VerifySignature(privateKey, data, signature);
// Then
Assert.IsTrue(isValidSignature);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_512_Certificates))]
public void ShouldSetContainerPassword_R3410_2012_512(TestCertificateInfo testCase)
{
// Given
var data = GetSomeData();
var certificate = testCase.Certificate;
var securePassword = CreateSecureString(TestConfig.ContainerPassword);
// When
var privateKeyInfo = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2012_512_AsymmetricAlgorithm(privateKeyInfo);
privateKey.SetContainerPassword(securePassword);
var signature = CreateSignature(privateKey, data);
var isValidSignature = VerifySignature(privateKey, data, signature);
// Then
Assert.IsTrue(isValidSignature);
}
private static byte[] CreateSignature(GostAsymmetricAlgorithm privateKey, byte[] data)
{
byte[] hash;
using (var hashAlg = privateKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(data);
}
return privateKey.CreateSignature(hash);
}
private static bool VerifySignature(GostAsymmetricAlgorithm publicKey, byte[] data, byte[] signature)
{
byte[] hash;
using (var hashAlg = publicKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(data);
}
return publicKey.VerifySignature(hash, signature);
}
private static SecureString CreateSecureString(string value)
{
var result = new SecureString();
foreach (var c in value)
{
result.AppendChar(c);
}
result.MakeReadOnly();
return result;
}
private static byte[] GetSomeData()
{
var random = new Random();
var data = new byte[1024];
random.NextBytes(data);
return data;
}
}
}

View File

@@ -0,0 +1,84 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-2012/256 и общего симметричного ключа ГОСТ 28147-89.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием HMAC.
/// </remarks>
[TestFixture(Description = "Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-2012/256 и общего симметричного ключа ГОСТ 28147-89")]
public class Gost_R3411_2012_256_HMACTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHMAC(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
var hmacDataStream = CreateHmacDataStream(sharedKey, dataStream);
var isValidHmacDataStream = VerifyHmacDataStream(sharedKey, hmacDataStream);
// Then
Assert.IsTrue(isValidHmacDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to HMAC..."));
}
private static Stream CreateHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream dataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_2012_256_HMAC(sharedKey))
{
// Вычисление HMAC для потока данных
var hmacValue = hmac.ComputeHash(dataStream);
// Запись HMAC в начало выходного потока данных
var hmacDataStream = new MemoryStream();
hmacDataStream.Write(hmacValue, 0, hmacValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(hmacDataStream);
hmacDataStream.Position = 0;
return hmacDataStream;
}
}
private static bool VerifyHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream hmacDataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_2012_256_HMAC(sharedKey))
{
// Считывание HMAC из потока данных
var hmacValue = new byte[hmac.HashSize / 8];
hmacDataStream.Read(hmacValue, 0, hmacValue.Length);
// Вычисление реального значения HMAC для потока данных
var expectedHmacValue = hmac.ComputeHash(hmacDataStream);
// Сравнение исходного HMAC с ожидаемым
return hmacValue.SequenceEqual(expectedHmacValue);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System.IO;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление хэша в соответствии с ГОСТ Р 34.11-2012/256.
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет хэш в соответствии с ГОСТ Р 34.11-2012/256 и проверяет его корректность.
/// </remarks>
[TestFixture(Description = "Вычисление хэша в соответствии с ГОСТ Р 34.11-2012/256")]
public class Gost_R3411_2012_256_HashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
// When
byte[] hashValue;
using (var hash = new Gost_R3411_2012_256_HashAlgorithm(providerType))
{
hashValue = hash.ComputeHash(dataStream);
}
// Then
Assert.IsNotNull(hashValue);
Assert.AreEqual(256, 8 * hashValue.Length);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to hash..."));
}
}
}

View File

@@ -0,0 +1,120 @@
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-2012/256.
/// </summary>
[TestFixture(Description = "Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-2012/256")]
public class Gost_R3411_2012_256_PRFTest
{
private static readonly byte[] Label = { 1, 2, 3, 4, 5 };
private static readonly byte[] Seed = { 6, 7, 8, 9, 0 };
private static readonly byte[] TestData = Encoding.UTF8.GetBytes("Some data to encrypt...");
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveBytes(ProviderType providerType)
{
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
byte[] randomBytes1;
byte[] randomBytes2;
byte[] randomBytes3;
using (var prf = new Gost_R3411_2012_256_PRF(initKey, Label, Seed))
{
randomBytes1 = prf.DeriveBytes();
randomBytes2 = prf.DeriveBytes();
randomBytes3 = prf.DeriveBytes();
}
// Then
Assert.IsNotNull(randomBytes1);
Assert.IsNotNull(randomBytes2);
Assert.IsNotNull(randomBytes3);
Assert.AreEqual(256, 8 * randomBytes1.Length);
Assert.AreEqual(256, 8 * randomBytes2.Length);
Assert.AreEqual(256, 8 * randomBytes3.Length);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes2);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes3);
CollectionAssert.AreNotEqual(randomBytes2, randomBytes3);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveKey(ProviderType providerType)
{
// TODO: VipNet does not support this feature - https://infotecs.ru/forum/topic/10142-oshibka-pri-sozdanii-klyucha-shifrovaniya-na-osnove-dannyih-polzovatelya-cryptderivekey/
if (providerType.IsVipNet())
{
Assert.Ignore("VipNet does not support this feature");
}
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
GostSymmetricAlgorithm randomKey1;
GostSymmetricAlgorithm randomKey2;
GostSymmetricAlgorithm randomKey3;
using (var prf = new Gost_R3411_2012_256_PRF(initKey, Label, Seed))
{
randomKey1 = prf.DeriveKey();
randomKey2 = prf.DeriveKey();
randomKey3 = prf.DeriveKey();
}
// Then
Assert.IsNotNull(randomKey1);
Assert.IsNotNull(randomKey2);
Assert.IsNotNull(randomKey3);
AssertKeyIsValid(randomKey1);
AssertKeyIsValid(randomKey2);
AssertKeyIsValid(randomKey3);
AssertKeysAreNotEqual(randomKey1, randomKey2);
AssertKeysAreNotEqual(randomKey1, randomKey3);
AssertKeysAreNotEqual(randomKey2, randomKey3);
}
public static void AssertKeyIsValid(GostSymmetricAlgorithm key)
{
var encryptedData = EncryptData(key, TestData);
var decryptedData = DecryptData(key, encryptedData);
CollectionAssert.AreEqual(TestData, decryptedData);
}
public static void AssertKeysAreNotEqual(GostSymmetricAlgorithm key1, GostSymmetricAlgorithm key2)
{
var encryptedData1 = EncryptData(key1, TestData);
var encryptedData2 = EncryptData(key2, TestData);
CollectionAssert.AreNotEqual(encryptedData1, encryptedData2);
}
public static byte[] EncryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateEncryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
public static byte[] DecryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateDecryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
}
}

View File

@@ -0,0 +1,84 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-2012/512 и общего симметричного ключа ГОСТ 28147-89.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием HMAC.
/// </remarks>
[TestFixture(Description = "Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-2012/512 и общего симметричного ключа ГОСТ 28147-89")]
public class Gost_R3411_2012_512_HMACTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHMAC(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
var hmacDataStream = CreateHmacDataStream(sharedKey, dataStream);
var isValidHmacDataStream = VerifyHmacDataStream(sharedKey, hmacDataStream);
// Then
Assert.IsTrue(isValidHmacDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to HMAC..."));
}
private static Stream CreateHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream dataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_2012_512_HMAC(sharedKey))
{
// Вычисление HMAC для потока данных
var hmacValue = hmac.ComputeHash(dataStream);
// Запись HMAC в начало выходного потока данных
var hmacDataStream = new MemoryStream();
hmacDataStream.Write(hmacValue, 0, hmacValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(hmacDataStream);
hmacDataStream.Position = 0;
return hmacDataStream;
}
}
private static bool VerifyHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream hmacDataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_2012_512_HMAC(sharedKey))
{
// Считывание HMAC из потока данных
var hmacValue = new byte[hmac.HashSize / 8];
hmacDataStream.Read(hmacValue, 0, hmacValue.Length);
// Вычисление реального значения HMAC для потока данных
var expectedHmacValue = hmac.ComputeHash(hmacDataStream);
// Сравнение исходного HMAC с ожидаемым
return hmacValue.SequenceEqual(expectedHmacValue);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System.IO;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление хэша в соответствии с ГОСТ Р 34.11-2012/512.
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет хэш в соответствии с ГОСТ Р 34.11-2012/512 и проверяет его корректность.
/// </remarks>
[TestFixture(Description = "Вычисление хэша в соответствии с ГОСТ Р 34.11-2012/512")]
public class Gost_R3411_2012_512_HashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
// When
byte[] hashValue;
using (var hash = new Gost_R3411_2012_512_HashAlgorithm(providerType))
{
hashValue = hash.ComputeHash(dataStream);
}
// Then
Assert.IsNotNull(hashValue);
Assert.AreEqual(512, 8 * hashValue.Length);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to hash..."));
}
}
}

View File

@@ -0,0 +1,120 @@
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-2012/512.
/// </summary>
[TestFixture(Description = "Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-2012/512")]
public class Gost_R3411_2012_512_PRFTest
{
private static readonly byte[] Label = { 1, 2, 3, 4, 5 };
private static readonly byte[] Seed = { 6, 7, 8, 9, 0 };
private static readonly byte[] TestData = Encoding.UTF8.GetBytes("Some data to encrypt...");
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveBytes(ProviderType providerType)
{
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
byte[] randomBytes1;
byte[] randomBytes2;
byte[] randomBytes3;
using (var prf = new Gost_R3411_2012_512_PRF(initKey, Label, Seed))
{
randomBytes1 = prf.DeriveBytes();
randomBytes2 = prf.DeriveBytes();
randomBytes3 = prf.DeriveBytes();
}
// Then
Assert.IsNotNull(randomBytes1);
Assert.IsNotNull(randomBytes2);
Assert.IsNotNull(randomBytes3);
Assert.AreEqual(512, 8 * randomBytes1.Length);
Assert.AreEqual(512, 8 * randomBytes2.Length);
Assert.AreEqual(512, 8 * randomBytes3.Length);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes2);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes3);
CollectionAssert.AreNotEqual(randomBytes2, randomBytes3);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveKey(ProviderType providerType)
{
// TODO: VipNet does not support this feature - https://infotecs.ru/forum/topic/10142-oshibka-pri-sozdanii-klyucha-shifrovaniya-na-osnove-dannyih-polzovatelya-cryptderivekey/
if (providerType.IsVipNet())
{
Assert.Ignore("VipNet does not support this feature");
}
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
GostSymmetricAlgorithm randomKey1;
GostSymmetricAlgorithm randomKey2;
GostSymmetricAlgorithm randomKey3;
using (var prf = new Gost_R3411_2012_512_PRF(initKey, Label, Seed))
{
randomKey1 = prf.DeriveKey();
randomKey2 = prf.DeriveKey();
randomKey3 = prf.DeriveKey();
}
// Then
Assert.IsNotNull(randomKey1);
Assert.IsNotNull(randomKey2);
Assert.IsNotNull(randomKey3);
AssertKeyIsValid(randomKey1);
AssertKeyIsValid(randomKey2);
AssertKeyIsValid(randomKey3);
AssertKeysAreNotEqual(randomKey1, randomKey2);
AssertKeysAreNotEqual(randomKey1, randomKey3);
AssertKeysAreNotEqual(randomKey2, randomKey3);
}
public static void AssertKeyIsValid(GostSymmetricAlgorithm key)
{
var encryptedData = EncryptData(key, TestData);
var decryptedData = DecryptData(key, encryptedData);
CollectionAssert.AreEqual(TestData, decryptedData);
}
public static void AssertKeysAreNotEqual(GostSymmetricAlgorithm key1, GostSymmetricAlgorithm key2)
{
var encryptedData1 = EncryptData(key1, TestData);
var encryptedData2 = EncryptData(key2, TestData);
CollectionAssert.AreNotEqual(encryptedData1, encryptedData2);
}
public static byte[] EncryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateEncryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
public static byte[] DecryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateDecryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
}
}

View File

@@ -0,0 +1,84 @@
using System.IO;
using System.Linq;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-94 и общего симметричного ключа ГОСТ 28147-89.
/// </summary>
/// <remarks>
/// Тест выполняет подпись и проверку подписи потока байт с использованием HMAC.
/// </remarks>
[TestFixture(Description = "Вычисление HMAC на базе алгоритма хэширования ГОСТ Р 34.11-94 и общего симметричного ключа ГОСТ 28147-89")]
public class Gost_R3411_94_HMACTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHMAC(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
var hmacDataStream = CreateHmacDataStream(sharedKey, dataStream);
var isValidHmacDataStream = VerifyHmacDataStream(sharedKey, hmacDataStream);
// Then
Assert.IsTrue(isValidHmacDataStream);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to HMAC..."));
}
private static Stream CreateHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream dataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_94_HMAC(sharedKey))
{
// Вычисление HMAC для потока данных
var hmacValue = hmac.ComputeHash(dataStream);
// Запись HMAC в начало выходного потока данных
var hmacDataStream = new MemoryStream();
hmacDataStream.Write(hmacValue, 0, hmacValue.Length);
// Копирование исходного потока данных в выходной поток
dataStream.Position = 0;
dataStream.CopyTo(hmacDataStream);
hmacDataStream.Position = 0;
return hmacDataStream;
}
}
private static bool VerifyHmacDataStream(GostSymmetricAlgorithm sharedKey, Stream hmacDataStream)
{
// Создание объекта для вычисления HMAC
using (var hmac = new Gost_R3411_94_HMAC(sharedKey))
{
// Считывание HMAC из потока данных
var hmacValue = new byte[hmac.HashSize / 8];
hmacDataStream.Read(hmacValue, 0, hmacValue.Length);
// Вычисление реального значения HMAC для потока данных
var expectedHmacValue = hmac.ComputeHash(hmacDataStream);
// Сравнение исходного HMAC с ожидаемым
return hmacValue.SequenceEqual(expectedHmacValue);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System.IO;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Вычисление хэша в соответствии с ГОСТ Р 34.11-94.
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет хэш в соответствии с ГОСТ Р 34.11-94 и проверяет его корректность.
/// </remarks>
[TestFixture(Description = "Вычисление хэша в соответствии с ГОСТ Р 34.11-94")]
public class Gost_R3411_94_HashAlgorithmTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldComputeHash(ProviderType providerType)
{
// Given
var dataStream = CreateDataStream();
// When
byte[] hashValue;
using (var hash = new Gost_R3411_94_HashAlgorithm(providerType))
{
hashValue = hash.ComputeHash(dataStream);
}
// Then
Assert.IsNotNull(hashValue);
Assert.AreEqual(256, 8 * hashValue.Length);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to hash..."));
}
}
}

View File

@@ -0,0 +1,114 @@
using System.Text;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3411;
using NUnit.Framework;
namespace GostCryptography.Tests.Gost_R3411
{
/// <summary>
/// Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-94.
/// </summary>
[TestFixture(Description = "Использование PRF на базе алгоритма хэширования ГОСТ Р 34.11-94")]
public class Gost_R3411_94_PRFTest
{
private static readonly byte[] Label = { 1, 2, 3, 4, 5 };
private static readonly byte[] Seed = { 6, 7, 8, 9, 0 };
private static readonly byte[] TestData = Encoding.UTF8.GetBytes("Some data to encrypt...");
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveBytes(ProviderType providerType)
{
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
byte[] randomBytes1;
byte[] randomBytes2;
byte[] randomBytes3;
using (var prf = new Gost_R3411_94_PRF(initKey, Label, Seed))
{
randomBytes1 = prf.DeriveBytes();
randomBytes2 = prf.DeriveBytes();
randomBytes3 = prf.DeriveBytes();
}
// Then
Assert.IsNotNull(randomBytes1);
Assert.IsNotNull(randomBytes2);
Assert.IsNotNull(randomBytes3);
Assert.AreEqual(256, 8 * randomBytes1.Length);
Assert.AreEqual(256, 8 * randomBytes2.Length);
Assert.AreEqual(256, 8 * randomBytes3.Length);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes2);
CollectionAssert.AreNotEqual(randomBytes1, randomBytes3);
CollectionAssert.AreNotEqual(randomBytes2, randomBytes3);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldDeriveKey(ProviderType providerType)
{
// Given
var initKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
// When
GostSymmetricAlgorithm randomKey1;
GostSymmetricAlgorithm randomKey2;
GostSymmetricAlgorithm randomKey3;
using (var prf = new Gost_R3411_94_PRF(initKey, Label, Seed))
{
randomKey1 = prf.DeriveKey();
randomKey2 = prf.DeriveKey();
randomKey3 = prf.DeriveKey();
}
// Then
Assert.IsNotNull(randomKey1);
Assert.IsNotNull(randomKey2);
Assert.IsNotNull(randomKey3);
AssertKeyIsValid(randomKey1);
AssertKeyIsValid(randomKey2);
AssertKeyIsValid(randomKey3);
AssertKeysAreNotEqual(randomKey1, randomKey2);
AssertKeysAreNotEqual(randomKey1, randomKey3);
AssertKeysAreNotEqual(randomKey2, randomKey3);
}
public static void AssertKeyIsValid(GostSymmetricAlgorithm key)
{
var encryptedData = EncryptData(key, TestData);
var decryptedData = DecryptData(key, encryptedData);
CollectionAssert.AreEqual(TestData, decryptedData);
}
public static void AssertKeysAreNotEqual(GostSymmetricAlgorithm key1, GostSymmetricAlgorithm key2)
{
var encryptedData1 = EncryptData(key1, TestData);
var encryptedData2 = EncryptData(key2, TestData);
CollectionAssert.AreNotEqual(encryptedData1, encryptedData2);
}
public static byte[] EncryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateEncryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
public static byte[] DecryptData(GostSymmetricAlgorithm key, byte[] data)
{
var transform = key.CreateDecryptor();
return transform.TransformFinalBlock(data, 0, data.Length);
}
}
}

View File

@@ -0,0 +1,71 @@
using System.Linq;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using NUnit.Framework;
namespace GostCryptography.Tests.Pkcs
{
/// <summary>
/// Шифрация и дешифрация сообщения CMS/PKCS#7.
/// </summary>
/// <remarks>
/// Тест создает сообщение, шифрует его в формате CMS/PKCS#7, а затем дешифрует зашифрованное сообщение.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация сообщения CMS/PKCS#7")]
public class EnvelopedCmsEncryptTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptAndDecrypt(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var message = CreateMessage();
// When
var encryptedMessage = EncryptMessage(certificate, message);
var decryptedMessage = DecryptMessage(encryptedMessage);
// Then
Assert.IsTrue(message.SequenceEqual(decryptedMessage));
}
private static byte[] CreateMessage()
{
// Некоторое сообщение для подписи
return Encoding.UTF8.GetBytes("Some message to sign...");
}
private static byte[] EncryptMessage(X509Certificate2 certificate, byte[] message)
{
// Создание объекта для шифрования сообщения
var envelopedCms = new EnvelopedCms(new ContentInfo(message));
// Создание объект с информацией о получателе
var recipient = new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
// Шифрование сообщения CMS/PKCS#7
envelopedCms.Encrypt(recipient);
// Создание сообщения CMS/PKCS#7
return envelopedCms.Encode();
}
private static byte[] DecryptMessage(byte[] encryptedMessage)
{
// Создание объекта для расшифровки сообщения
var envelopedCms = new EnvelopedCms();
// Чтение сообщения CMS/PKCS#7
envelopedCms.Decode(encryptedMessage);
// Расшифровка сообщения CMS/PKCS#7
envelopedCms.Decrypt(envelopedCms.RecipientInfos[0]);
return envelopedCms.ContentInfo.Content;
}
}
}

View File

@@ -0,0 +1,83 @@
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Pkcs;
using NUnit.Framework;
namespace GostCryptography.Tests.Pkcs
{
/// <summary>
/// Подпись и проверка отсоединенной подписи сообщения CMS/PKCS#7.
/// </summary>
/// <remarks>
/// Тест создает сообщение, формирует отсоединенную подпись сообщения в формате CMS/PKCS#7,
/// а затем проверяет подпись полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка отсоединенной подписи сообщения CMS/PKCS#7")]
public class SignedCmsDetachedSignTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSign(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var message = CreateMessage();
// When
var detachedSignature = SignMessage(certificate, message);
var isValidDetachedSignature = VerifyMessage(message, detachedSignature);
// Then
Assert.IsTrue(isValidDetachedSignature);
}
private static byte[] CreateMessage()
{
// Некоторое сообщение для подписи
return Encoding.UTF8.GetBytes("Some message to sign...");
}
private static byte[] SignMessage(X509Certificate2 certificate, byte[] message)
{
// Создание объекта для подписи сообщения
var signedCms = new GostSignedCms(new ContentInfo(message), true);
// Создание объект с информацией о подписчике
var signer = new CmsSigner(certificate);
// Включение информации только о конечном сертификате (только для теста)
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Создание подписи для сообщения CMS/PKCS#7
signedCms.ComputeSignature(signer);
// Создание подписи CMS/PKCS#7
return signedCms.Encode();
}
private static bool VerifyMessage(byte[] message, byte[] detachedSignature)
{
// Создание объекта для проверки подписи сообщения
var signedCms = new GostSignedCms(new ContentInfo(message), true);
// Чтение подписи CMS/PKCS#7
signedCms.Decode(detachedSignature);
try
{
// Проверка подписи CMS/PKCS#7
signedCms.CheckSignature(true);
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,90 @@
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Pkcs;
using NUnit.Framework;
namespace GostCryptography.Tests.Pkcs
{
/// <summary>
/// Подпись и проверка подписи сообщения CMS/PKCS#7.
/// </summary>
/// <remarks>
/// Тест создает сообщение, формирует подписанное сообщение в формате CMS/PKCS#7,
/// исключая информацию о сертификате подписчика с целью минимизации размера сообщения,
/// а затем проверяет подпись полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи сообщения CMS/PKCS#7")]
public class SignedCmsSignAndExcludeCertificates
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSign(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var message = CreateMessage();
// When
var signedMessage = SignMessage(certificate, message);
var isValidSignedMessage = VerifyMessage(certificate, signedMessage);
// Then
Assert.IsTrue(isValidSignedMessage);
}
private static byte[] CreateMessage()
{
// Некоторое сообщение для подписи
return Encoding.UTF8.GetBytes("Some message to sign...");
}
private static byte[] SignMessage(X509Certificate2 certificate, byte[] message)
{
// Создание объекта для подписи сообщения
var signedCms = new GostSignedCms(new ContentInfo(message));
// Создание объект с информацией о подписчике
var signer = new CmsSigner(certificate);
// Включение информации только о конечном сертификате (только для теста)
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Создание подписи для сообщения CMS/PKCS#7
signedCms.ComputeSignature(signer);
// Исключение сертификатов для уменьшения размера сообщения
signedCms.RemoveCertificates();
// Создание сообщения CMS/PKCS#7
return signedCms.Encode();
}
private static bool VerifyMessage(X509Certificate2 certificate, byte[] signedMessage)
{
// Создание объекта для проверки подписи сообщения
var signedCms = new GostSignedCms();
// Чтение сообщения CMS/PKCS#7
signedCms.Decode(signedMessage);
// Список сертификатов подписчика
var signerCerts = new X509Certificate2Collection(certificate);
try
{
// Проверка подписи сообщения CMS/PKCS#7
signedCms.CheckSignature(signerCerts, true);
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,83 @@
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Pkcs;
using NUnit.Framework;
namespace GostCryptography.Tests.Pkcs
{
/// <summary>
/// Подпись и проверка подписи сообщения CMS/PKCS#7.
/// </summary>
/// <remarks>
/// Тест создает сообщение, формирует подписанное сообщение в формате CMS/PKCS#7,
/// а затем проверяет подпись полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи сообщения CMS/PKCS#7")]
public class SignedCmsSignTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSign(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var message = CreateMessage();
// When
var signedMessage = SignMessage(certificate, message);
var isValidSignedMessage = VerifyMessage(signedMessage);
// Then
Assert.IsTrue(isValidSignedMessage);
}
private static byte[] CreateMessage()
{
// Некоторое сообщение для подписи
return Encoding.UTF8.GetBytes("Some message to sign...");
}
private static byte[] SignMessage(X509Certificate2 certificate, byte[] message)
{
// Создание объекта для подписи сообщения
var signedCms = new GostSignedCms(new ContentInfo(message));
// Создание объект с информацией о подписчике
var signer = new CmsSigner(certificate);
// Включение информации только о конечном сертификате (только для теста)
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Создание подписи для сообщения CMS/PKCS#7
signedCms.ComputeSignature(signer);
// Создание сообщения CMS/PKCS#7
return signedCms.Encode();
}
private static bool VerifyMessage(byte[] signedMessage)
{
// Создание объекта для проверки подписи сообщения
var signedCms = new GostSignedCms();
// Чтение сообщения CMS/PKCS#7
signedCms.Decode(signedMessage);
try
{
// Проверка подписи сообщения CMS/PKCS#7
signedCms.CheckSignature(true);
}
catch
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,118 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace GostCryptography.Tests.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GostCryptography.Tests.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
///&lt;MyXml&gt;
/// &lt;SomeElement Encrypt=&quot;false&quot;&gt;
/// Here is public data.
/// &lt;/SomeElement&gt;
/// &lt;SomeElement Encrypt=&quot;true&quot;&gt;
/// Here is private data.
/// &lt;/SomeElement&gt;
/// &lt;SomeElement Encrypt=&quot;true&quot;&gt;
/// Here is private data.
/// &lt;/SomeElement&gt;
/// &lt;SomeElement Encrypt=&quot;true&quot;&gt;
/// Here is private data.
/// &lt;/SomeElement&gt;
///&lt;/MyXml&gt;.
/// </summary>
internal static string EncryptedXmlExample {
get {
return ResourceManager.GetString("EncryptedXmlExample", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
///&lt;MyXml&gt;
/// &lt;SomeElement Id=&quot;Id1&quot;&gt;
/// Here is some data to sign.
/// &lt;/SomeElement&gt;
///&lt;/MyXml&gt;.
/// </summary>
internal static string SignedXmlExample {
get {
return ResourceManager.GetString("SignedXmlExample", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
///&lt;S:Envelope xmlns:S=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;
/// xmlns:wsse=&quot;http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd&quot;
/// xmlns:wsu=&quot;http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd&quot;
/// &gt;
/// &lt;S:Header&gt;
/// &lt;wsse:Security S:actor=&quot;http://smev.gosuslugi.ru/actors/smev&quot;&gt;
/// &lt;ds:Signature xmlns:ds=&quot;http://www.w3.org/2000/09/xmldsig#&quot;&gt;
/// &lt;ds:KeyInfo&gt;
/// &lt;wsse:SecurityTokenR [rest of string was truncated]&quot;;.
/// </summary>
internal static string SmevExample {
get {
return ResourceManager.GetString("SmevExample", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="EncryptedXmlExample" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\encryptedxmlexample.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="SignedXmlExample" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\signedxmlexample.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="SmevExample" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\smevexample.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>

View File

@@ -0,0 +1,74 @@
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using NUnit.Framework;
namespace GostCryptography.Tests.Sign
{
/// <summary>
/// Подпись и проверка подписи потока байт с помощью сертификата.
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет цифровую подпись потока байт с использованием закрытого ключа сертификата,
/// а затем с помощью открытого ключа сертификата проверяет полученную подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи потока байт с помощью сертификата")]
public class SignDataStreamCertificateTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignDataStream(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
dataStream.Seek(0, SeekOrigin.Begin);
var signature = CreateSignature(privateKey, dataStream);
dataStream.Seek(0, SeekOrigin.Begin);
var isValidSignature = VerifySignature(publicKey, dataStream, signature);
// Then
Assert.IsTrue(isValidSignature);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт для подписи
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to sign..."));
}
private static byte[] CreateSignature(GostAsymmetricAlgorithm privateKey, Stream dataStream)
{
byte[] hash;
using (var hashAlg = privateKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(dataStream);
}
return privateKey.CreateSignature(hash);
}
private static bool VerifySignature(GostAsymmetricAlgorithm publicKey, Stream dataStream, byte[] signature)
{
byte[] hash;
using (var hashAlg = publicKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(dataStream);
}
return publicKey.VerifySignature(hash, signature);
}
}
}

View File

@@ -0,0 +1,88 @@
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using GostCryptography.Config;
using NUnit.Framework;
namespace GostCryptography.Tests.Sign
{
/// <summary>
/// Подпись и проверка подписи потока байт с помощью сертификата и информации об алгоритме цифровой подписи
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет цифровую подпись потока байт с использованием закрытого ключа сертификата,
/// а затем с помощью открытого ключа сертификата проверяет полученную подпись. Для вычисления цифровой подписи
/// и ее проверки используется информация об алгоритме цифровой подписи <see cref="SignatureDescription"/>,
/// получаемая с помощью метода <see cref="GostCryptoConfig.CreateFromName"/>.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи потока байт с помощью сертификата и информации об алгоритме цифровой подписи")]
public class SignDataStreamSignatureDescriptionTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignDataStream(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
dataStream.Seek(0, SeekOrigin.Begin);
var signature = CreateSignature(privateKey, dataStream);
dataStream.Seek(0, SeekOrigin.Begin);
var isValidSignature = VerifySignature(publicKey, dataStream, signature);
// Then
Assert.IsTrue(isValidSignature);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт для подписи
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to sign..."));
}
private static byte[] CreateSignature(AsymmetricAlgorithm privateKey, Stream dataStream)
{
var signatureDescription = (SignatureDescription)GostCryptoConfig.CreateFromName(privateKey.SignatureAlgorithm);
byte[] hash;
using (var hashAlg = signatureDescription.CreateDigest())
{
hash = hashAlg.ComputeHash(dataStream);
}
var formatter = signatureDescription.CreateFormatter(privateKey);
formatter.SetHashAlgorithm(signatureDescription.DigestAlgorithm);
return formatter.CreateSignature(hash);
}
private static bool VerifySignature(AsymmetricAlgorithm publicKey, Stream dataStream, byte[] signature)
{
var signatureDescription = (SignatureDescription)GostCryptoConfig.CreateFromName(publicKey.SignatureAlgorithm);
byte[] hash;
using (var hashAlg = signatureDescription.CreateDigest())
{
hash = hashAlg.ComputeHash(dataStream);
}
var deformatter = signatureDescription.CreateDeformatter(publicKey);
deformatter.SetHashAlgorithm(signatureDescription.DigestAlgorithm);
return deformatter.VerifySignature(hash, signature);
}
}
}

View File

@@ -0,0 +1,80 @@
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using GostCryptography.Base;
using NUnit.Framework;
namespace GostCryptography.Tests.Sign
{
/// <summary>
/// Подпись и проверка подписи потока байт с помощью сертификата и классов форматирования.
/// </summary>
/// <remarks>
/// Тест создает поток байт, вычисляет цифровую подпись потока байт с использованием закрытого ключа сертификата,
/// а затем с помощью открытого ключа сертификата проверяет полученную подпись. Для вычисления цифровой подписи
/// используется класс <see cref="GostSignatureFormatter"/>, для проверки цифровой подписи используется класс
/// <see cref="GostSignatureDeformatter"/>.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи потока байт с помощью сертификата и классов форматирования")]
public class SignDataStreamSignatureFormatterTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignDataStream(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var privateKey = (GostAsymmetricAlgorithm)certificate.GetPrivateKeyAlgorithm();
var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
var dataStream = CreateDataStream();
// When
dataStream.Seek(0, SeekOrigin.Begin);
var signature = CreateSignature(privateKey, dataStream);
dataStream.Seek(0, SeekOrigin.Begin);
var isValidSignature = VerifySignature(publicKey, dataStream, signature);
// Then
Assert.IsTrue(isValidSignature);
}
private static Stream CreateDataStream()
{
// Некоторый поток байт для подписи
return new MemoryStream(Encoding.UTF8.GetBytes("Some data to sign..."));
}
private static byte[] CreateSignature(GostAsymmetricAlgorithm privateKey, Stream dataStream)
{
byte[] hash;
using (var hashAlg = privateKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(dataStream);
}
var formatter = new GostSignatureFormatter(privateKey);
return formatter.CreateSignature(hash);
}
private static bool VerifySignature(GostAsymmetricAlgorithm publicKey, Stream dataStream, byte[] signature)
{
byte[] hash;
using (var hashAlg = publicKey.CreateHashAlgorithm())
{
hash = hashAlg.ComputeHash(dataStream);
}
var deformatter = new GostSignatureDeformatter(publicKey);
return deformatter.VerifySignature(hash, signature);
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Security.Cryptography.X509Certificates;
namespace GostCryptography.Tests
{
public class TestCertificateInfo
{
public TestCertificateInfo(string name, X509Certificate2 certificate)
{
Name = name;
Certificate = certificate;
}
public string Name { get; }
public X509Certificate2 Certificate { get; }
public override string ToString() => Name;
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using GostCryptography.Base;
using GostCryptography.Config;
namespace GostCryptography.Tests
{
public static class TestConfig
{
static TestConfig()
{
Providers = new[] { GostCryptoConfig.ProviderType, GostCryptoConfig.ProviderType_2012_512, GostCryptoConfig.ProviderType_2012_1024 };
var gost_R3410_2001 = new TestCertificateInfo("ГОСТ Р 34.10-2001", FindGostCertificate(filter: c => c.IsGost_R3410_2001()));
var gost_R3410_2012_256 = new TestCertificateInfo("ГОСТ Р 34.10-2012/256", FindGostCertificate(filter: c => c.IsGost_R3410_2012_256()));
var gost_R3410_2012_512 = new TestCertificateInfo("ГОСТ Р 34.10-2012/512", FindGostCertificate(filter: c => c.IsGost_R3410_2012_512()));
var gost_R3410_Certificates = new List<TestCertificateInfo> { gost_R3410_2001, gost_R3410_2012_256, gost_R3410_2012_512 };
var gost_R3410_2001_Certificates = new List<TestCertificateInfo> { gost_R3410_2001 };
var gost_R3410_2012_256_Certificates = new List<TestCertificateInfo> { gost_R3410_2012_256 };
var gost_R3410_2012_512_Certificates = new List<TestCertificateInfo> { gost_R3410_2012_512 };
gost_R3410_Certificates.RemoveAll(c => c.Certificate == null);
gost_R3410_2001_Certificates.RemoveAll(c => c.Certificate == null);
gost_R3410_2012_256_Certificates.RemoveAll(c => c.Certificate == null);
gost_R3410_2012_512_Certificates.RemoveAll(c => c.Certificate == null);
Gost_R3410_Certificates = gost_R3410_Certificates;
Gost_R3410_2001_Certificates = gost_R3410_2001_Certificates;
Gost_R3410_2012_256_Certificates = gost_R3410_2012_256_Certificates;
Gost_R3410_2012_512_Certificates = gost_R3410_2012_512_Certificates;
}
public const StoreName DefaultStoreName = StoreName.My;
public const StoreLocation DefaultStoreLocation = StoreLocation.LocalMachine;
public static IEnumerable<ProviderType> Providers { get; }
public static IEnumerable<TestCertificateInfo> Gost_R3410_Certificates { get; }
public static IEnumerable<TestCertificateInfo> Gost_R3410_2001_Certificates { get; }
public static IEnumerable<TestCertificateInfo> Gost_R3410_2012_256_Certificates { get; }
public static IEnumerable<TestCertificateInfo> Gost_R3410_2012_512_Certificates { get; }
public const string ContainerPassword = "GostCryptography";
[SecuritySafeCritical]
public static X509Certificate2 FindGostCertificate(StoreName storeName = DefaultStoreName, StoreLocation storeLocation = DefaultStoreLocation, Predicate<X509Certificate2> filter = null)
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
try
{
foreach (var certificate in store.Certificates)
{
if (certificate.HasPrivateKey && certificate.IsGost() && (filter == null || filter(certificate)))
{
return certificate;
}
}
}
finally
{
store.Close();
}
return null;
}
}
}

View File

@@ -0,0 +1,211 @@
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML для широковещательной рассылки.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, выборочно шифрует элементы данного документа, а затем дешифрует полученный зашифрованный документ.
/// Элементы шифруются с использованием случайного сессионного ключа, который в свою очередь кодируется (экспортируется)
/// с использованием публичного ключа сертификата получателя. Расшифровка документа происходит с использованием первого
/// найденного секретного ключа сертификата получателя.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML для широковещательной рассылки")]
public sealed class EncryptedXmlBroadcastTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var certificates = new[] { certificate };
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, certificates);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, IEnumerable<X509Certificate2> certificates)
{
// Создание объекта для шифрации XML
var encryptedXml = new GostEncryptedXml();
// Поиск элементов для шифрации
var elements = xmlDocument.SelectNodes("//SomeElement[@Encrypt='true']");
if (elements != null)
{
var elementIndex = 0;
foreach (XmlElement element in elements)
{
// Формирование элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.Id = "EncryptedElement" + elementIndex++;
elementEncryptedData.Type = EncryptedXml.XmlEncElementUrl;
elementEncryptedData.KeyInfo = new KeyInfo();
using (var sessionKey = new Gost_28147_89_SymmetricAlgorithm())
{
elementEncryptedData.EncryptionMethod = new EncryptionMethod(sessionKey.AlgorithmName);
// Шифрация элемента с использованием симметричного ключа
var encryptedElement = encryptedXml.EncryptData(element, sessionKey, false);
foreach (var certificate in certificates)
{
// Шифрация сессионного ключа с использованием открытого ключа сертификата
var encryptedSessionKeyData = GostEncryptedXml.EncryptKey(sessionKey, (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm());
// Формирование информации о зашифрованном сессионном ключе
var encryptedSessionKey = new EncryptedKey();
encryptedSessionKey.CipherData = new CipherData(encryptedSessionKeyData);
encryptedSessionKey.EncryptionMethod = new EncryptionMethod(GostEncryptedXml.XmlEncGostCryptoProKeyExportUrl);
encryptedSessionKey.AddReference(new DataReference { Uri = "#" + elementEncryptedData.Id });
encryptedSessionKey.KeyInfo.AddClause(new KeyInfoX509Data(certificate));
// Добавление ссылки на зашифрованный ключ, используемый при шифровании данных
elementEncryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedSessionKey));
}
// Установка зашифрованных данных у объекта EncryptedData
elementEncryptedData.CipherData.CipherValue = encryptedElement;
}
// Замена элемента его зашифрованным представлением
GostEncryptedXml.ReplaceElement(element, elementEncryptedData, false);
}
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(encryptedXmlDocument);
var nsManager = new XmlNamespaceManager(encryptedXmlDocument.NameTable);
nsManager.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
// Поиск всех зашифрованных XML-элементов
var encryptedDataList = encryptedXmlDocument.SelectNodes("//enc:EncryptedData", nsManager);
if (encryptedDataList != null)
{
foreach (XmlElement encryptedData in encryptedDataList)
{
// Загрузка элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.LoadXml(encryptedData);
// Извлечение симметричный ключ для расшифровки элемента EncryptedData
var sessionKey = GetDecryptionKey(elementEncryptedData);
if (sessionKey != null)
{
// Расшифровка элемента EncryptedData
var decryptedData = encryptedXml.DecryptData(elementEncryptedData, sessionKey);
// Замена элемента EncryptedData его расшифрованным представлением
encryptedXml.ReplaceData(encryptedData, decryptedData);
}
}
}
return encryptedXmlDocument;
}
private static SymmetricAlgorithm GetDecryptionKey(EncryptedData encryptedData)
{
SymmetricAlgorithm sessionKey = null;
foreach (var keyInfo in encryptedData.KeyInfo)
{
if (keyInfo is KeyInfoEncryptedKey)
{
var encryptedKey = ((KeyInfoEncryptedKey)keyInfo).EncryptedKey;
if (encryptedKey != null)
{
foreach (var ekKeyInfo in encryptedKey.KeyInfo)
{
if (ekKeyInfo is KeyInfoX509Data)
{
var certificates = ((KeyInfoX509Data)ekKeyInfo).Certificates;
// Поиск закрытого ключа для дешифрации сессионного ключа
var privateKey = FindPrivateKey(certificates);
if (privateKey != null)
{
// Дешифрация сессионного ключа с использованием закрытого ключа сертификата
sessionKey = GostEncryptedXml.DecryptKey(encryptedKey.CipherData.CipherValue, privateKey);
break;
}
}
}
}
}
}
return sessionKey;
}
private static GostAsymmetricAlgorithm FindPrivateKey(IEnumerable certificates)
{
// Какая-то логика поиска закрытого ключа
GostAsymmetricAlgorithm privateKey = null;
var store = new X509Store(TestConfig.DefaultStoreName, TestConfig.DefaultStoreLocation);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var storeCertificates = store.Certificates;
store.Close();
foreach (X509Certificate2 certificate in certificates)
{
var index = storeCertificates.IndexOf(certificate);
if (index >= 0)
{
privateKey = storeCertificates[index].GetPrivateKeyAlgorithm() as GostAsymmetricAlgorithm;
if (privateKey != null)
{
break;
}
}
}
return privateKey;
}
}
}

View File

@@ -0,0 +1,80 @@
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML документа с использованием сертификата.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, выборочно шифрует элементы данного документа с использованием сертификата,
/// а затем дешифрует полученный зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML документа с использованием сертификата")]
public sealed class EncryptedXmlCertificateTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, certificate);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
// Создание объекта для шифрации XML
var encryptedXml = new GostEncryptedXml();
// Поиск элементов для шифрации
var elements = xmlDocument.SelectNodes("//SomeElement[@Encrypt='true']");
if (elements != null)
{
foreach (XmlElement element in elements)
{
// Шифрация элемента
var elementEncryptedData = encryptedXml.Encrypt(element, certificate);
// Замена элемента его зашифрованным представлением
GostEncryptedXml.ReplaceElement(element, elementEncryptedData, false);
}
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(encryptedXmlDocument);
// Расшифровка зашифрованных элементов документа
encryptedXml.DecryptDocument();
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,193 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Gost_R3410;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML с использованием контейнера ключей.
/// </summary>
/// <remarks>
/// Тест имитирует обмен данными между условным отправителем, который шифрует заданный XML-документ, и условным получателем, который дешифрует
/// зашифрованный XML-документ. Шифрация и дешифрация осуществляется без использования сертификатов. Шифрация осуществляется с использованием
/// случайного симметричного ключа, который в свою очередь шифруется с использованием открытого ключа получателя. Соответственно для дешифрации
/// данных сначала расшифровывается случайный симметричный ключ с использованием закрытого ключа получателя.
///
/// Перед началом теста имитируется передача получателем своего открытого ключа отправителю. Для этого получатель извлекает информацию о закрытом
/// ключе из контейнера ключей, формирует закрытый ключ для дешифрации XML и условно передает (экспортирует) отправителю информацию о своем открытом
/// ключе. Отправитель в свою очередь принимает (импортирует) от получателя информацию о его открытом ключе и формирует открытый ключ для шифрации XML.
///
/// Тест создает XML-документ, выборочно шифрует элементы данного документа с использованием случайного симметричного ключа, а затем дешифрует
/// полученный зашифрованный документ. Случайный симметричного ключ в свою очередь шифруется открытым асимметричным ключом получателя и в зашифрованном
/// виде добавляется в зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML с использованием контейнера ключей")]
public class EncryptedXmlKeyContainerTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2001_Certificates))]
public void ShouldEncryptXmlWithGost_R3410_2001(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
// Получатель экспортирует отправителю информацию о своем открытом ключе
var keyContainer = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2001_AsymmetricAlgorithm(keyContainer);
var publicKeyInfo = privateKey.ExportParameters(false);
// Отправитель импортирует от получателя информацию о его открытом ключе
var publicKey = new Gost_R3410_2001_AsymmetricAlgorithm();
publicKey.ImportParameters(publicKeyInfo);
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, publicKey);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument, privateKey);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_256_Certificates))]
public void ShouldEncryptXmlWithGost_R3410_2012_256(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
// Получатель экспортирует отправителю информацию о своем открытом ключе
var keyContainer = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2012_256_AsymmetricAlgorithm(keyContainer);
var publicKeyInfo = privateKey.ExportParameters(false);
// Отправитель импортирует от получателя информацию о его открытом ключе
var publicKey = new Gost_R3410_2012_256_AsymmetricAlgorithm();
publicKey.ImportParameters(publicKeyInfo);
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, publicKey);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument, privateKey);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_512_Certificates))]
public void ShouldEncryptXmlWithGost_R3410_2012_512(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
// Получатель экспортирует отправителю информацию о своем открытом ключе
var keyContainer = certificate.GetPrivateKeyInfo();
var privateKey = new Gost_R3410_2012_512_AsymmetricAlgorithm(keyContainer);
var publicKeyInfo = privateKey.ExportParameters(false);
// Отправитель импортирует от получателя информацию о его открытом ключе
var publicKey = new Gost_R3410_2012_512_AsymmetricAlgorithm();
publicKey.ImportParameters(publicKeyInfo);
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, publicKey);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument, privateKey);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, GostAsymmetricAlgorithm publicKey)
{
// Создание объекта для шифрации XML
var encryptedXml = new GostEncryptedXml(publicKey.ProviderType);
// Поиск элементов для шифрации
var elements = xmlDocument.SelectNodes("//SomeElement[@Encrypt='true']");
if (elements != null)
{
var elementIndex = 0;
foreach (XmlElement element in elements)
{
// Создание случайного сессионного ключа
using (var sessionKey = new Gost_28147_89_SymmetricAlgorithm(publicKey.ProviderType))
{
// Шифрация элемента
var encryptedData = encryptedXml.EncryptData(element, sessionKey, false);
// Шифрация сессионного ключа с использованием публичного асимметричного ключа
var encryptedSessionKeyData = GostEncryptedXml.EncryptKey(sessionKey, publicKey);
// Формирование элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.Id = "EncryptedElement" + elementIndex++;
elementEncryptedData.Type = EncryptedXml.XmlEncElementUrl;
elementEncryptedData.EncryptionMethod = new EncryptionMethod(sessionKey.AlgorithmName);
elementEncryptedData.CipherData.CipherValue = encryptedData;
elementEncryptedData.KeyInfo = new KeyInfo();
// Формирование информации о зашифрованном сессионном ключе
var encryptedSessionKey = new EncryptedKey();
encryptedSessionKey.CipherData = new CipherData(encryptedSessionKeyData);
encryptedSessionKey.EncryptionMethod = new EncryptionMethod(publicKey.KeyExchangeAlgorithm);
encryptedSessionKey.AddReference(new DataReference { Uri = "#" + elementEncryptedData.Id });
encryptedSessionKey.KeyInfo.AddClause(new KeyInfoName { Value = "KeyName1" });
// Добавление ссылки на зашифрованный ключ, используемый при шифровании данных
elementEncryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedSessionKey));
// Замена элемента его зашифрованным представлением
GostEncryptedXml.ReplaceElement(element, elementEncryptedData, false);
}
}
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument, GostAsymmetricAlgorithm privateKey)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(privateKey.ProviderType, encryptedXmlDocument);
// Добавление ссылки на приватный асимметричный ключ
encryptedXml.AddKeyNameMapping("KeyName1", privateKey);
// Расшифровка зашифрованных элементов документа
encryptedXml.DecryptDocument();
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,113 @@
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML с использованием случайного сессионного ключа.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, выборочно шифрует элементы данного документа с использованием случайного симметричного ключа,
/// а затем дешифрует полученный зашифрованный документ. Случайный симметричного ключ в свою очередь шифруется общим симметричным
/// ключом и в зашифрованном виде добавляется в зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML с использованием случайного сессионного ключа")]
public class EncryptedXmlSessionKey
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldEncryptXml(ProviderType providerType)
{
// Given
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, sharedKey);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument, sharedKey);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, GostSymmetricAlgorithm sharedKey)
{
// Создание объекта для шифрации XML
var encryptedXml = new GostEncryptedXml(sharedKey.ProviderType);
// Поиск элементов для шифрации
var elements = xmlDocument.SelectNodes("//SomeElement[@Encrypt='true']");
if (elements != null)
{
var elementIndex = 0;
foreach (XmlElement element in elements)
{
// Создание случайного сессионного ключа
using (var sessionKey = new Gost_28147_89_SymmetricAlgorithm(sharedKey.ProviderType))
{
// Шифрация элемента
var encryptedData = encryptedXml.EncryptData(element, sessionKey, false);
// Шифрация сессионного ключа с использованием общего симметричного ключа
var encryptedSessionKeyData = GostEncryptedXml.EncryptKey(sessionKey, sharedKey, GostKeyExchangeExportMethod.CryptoProKeyExport);
// Формирование элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.Id = "EncryptedElement" + elementIndex++;
elementEncryptedData.Type = EncryptedXml.XmlEncElementUrl;
elementEncryptedData.EncryptionMethod = new EncryptionMethod(sessionKey.AlgorithmName);
elementEncryptedData.CipherData.CipherValue = encryptedData;
elementEncryptedData.KeyInfo = new KeyInfo();
// Формирование информации о зашифрованном сессионном ключе
var encryptedSessionKey = new EncryptedKey();
encryptedSessionKey.CipherData = new CipherData(encryptedSessionKeyData);
encryptedSessionKey.EncryptionMethod = new EncryptionMethod(GostEncryptedXml.XmlEncGostCryptoProKeyExportUrl);
encryptedSessionKey.AddReference(new DataReference { Uri = "#" + elementEncryptedData.Id });
encryptedSessionKey.KeyInfo.AddClause(new KeyInfoName { Value = "SharedKey1" });
// Добавление ссылки на зашифрованный ключ, используемый при шифровании данных
elementEncryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedSessionKey));
// Замена элемента его зашифрованным представлением
GostEncryptedXml.ReplaceElement(element, elementEncryptedData, false);
}
}
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument, GostSymmetricAlgorithm sharedKey)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(sharedKey.ProviderType, encryptedXmlDocument);
// Добавление ссылки на общий симметричный ключ
encryptedXml.AddKeyNameMapping("SharedKey1", sharedKey);
// Расшифровка зашифрованных элементов документа
encryptedXml.DecryptDocument();
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML с использованием общего симметричного ключа.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, выборочно шифрует элементы данного документа с использованием общего симметричного ключа,
/// а затем дешифрует полученный зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML с использованием общего симметричного ключа")]
public sealed class EncryptedXmlSharedKeyTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Providers))]
public void ShouldEncryptXml(ProviderType providerType)
{
// Given
var sharedKey = new Gost_28147_89_SymmetricAlgorithm(providerType);
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml;
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, sharedKey);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument, sharedKey);
var actualXml = decryptedXmlDocument.OuterXml;
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, Gost_28147_89_SymmetricAlgorithm sharedKey)
{
// Создание объекта для шифрации XML
var encryptedXml = new GostEncryptedXml(sharedKey.ProviderType);
// Поиск элементов для шифрации
var elements = xmlDocument.SelectNodes("//SomeElement[@Encrypt='true']");
if (elements != null)
{
foreach (XmlElement element in elements)
{
// Шифрация элемента
var encryptedData = encryptedXml.EncryptData(element, sharedKey, false);
// Формирование элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.Type = EncryptedXml.XmlEncElementUrl;
elementEncryptedData.EncryptionMethod = new EncryptionMethod(sharedKey.AlgorithmName);
elementEncryptedData.CipherData.CipherValue = encryptedData;
// Замена элемента его зашифрованным представлением
GostEncryptedXml.ReplaceElement(element, elementEncryptedData, false);
}
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument, Gost_28147_89_SymmetricAlgorithm sharedKey)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(sharedKey.ProviderType, encryptedXmlDocument);
var nsManager = new XmlNamespaceManager(encryptedXmlDocument.NameTable);
nsManager.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
// Поиск всех зашифрованных XML-элементов
var encryptedDataList = encryptedXmlDocument.SelectNodes("//enc:EncryptedData", nsManager);
if (encryptedDataList != null)
{
foreach (XmlElement encryptedData in encryptedDataList)
{
// Загрузка элемента EncryptedData
var elementEncryptedData = new EncryptedData();
elementEncryptedData.LoadXml(encryptedData);
// Расшифровка элемента EncryptedData
var decryptedData = encryptedXml.DecryptData(elementEncryptedData, sharedKey);
// Замена элемента EncryptedData его расшифрованным представлением
encryptedXml.ReplaceData(encryptedData, decryptedData);
}
}
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,94 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML документа с использованием сертификата и алгоритма ГОСТ Р 34.12-2015 Кузнечик.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, шифрует его целиком с использованием сертификата, а затем дешифрует зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML документа с использованием сертификата и алгоритма ГОСТ Р 34.12-2015 Кузнечик")]
public sealed class KuznyechikEncryptedXmlCertificateTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml.Replace("\r\n", "\n");
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, certificate);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument);
var actualXml = decryptedXmlDocument.OuterXml.Replace("\r\n", "\n");
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
var publicKeyAlgorithm = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
using (var sessionKey = new Gost_3412_K_SymmetricAlgorithm(publicKeyAlgorithm.ProviderType))
{
var encryptedSessionKeyData = GostEncryptedXml.EncryptKey(sessionKey, publicKeyAlgorithm);
var encryptedSessionKey = new EncryptedKey
{
CipherData = new CipherData(encryptedSessionKeyData),
EncryptionMethod = new EncryptionMethod(publicKeyAlgorithm.KeyExchangeAlgorithm),
};
encryptedSessionKey.KeyInfo.AddClause(new KeyInfoX509Data(certificate));
var elementEncryptedData = new EncryptedData
{
EncryptionMethod = new EncryptionMethod(sessionKey.AlgorithmName),
};
var encryptedXml = new GostEncryptedXml();
var xmlBytes = Encoding.UTF8.GetBytes(xmlDocument.OuterXml);
var encryptedData = encryptedXml.EncryptData(xmlBytes, sessionKey);
elementEncryptedData.CipherData.CipherValue = encryptedData;
elementEncryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedSessionKey));
GostEncryptedXml.ReplaceElement(xmlDocument.DocumentElement, elementEncryptedData, false);
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(encryptedXmlDocument);
// Расшифровка зашифрованных элементов документа
encryptedXml.DecryptDocument();
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,94 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_28147_89;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Encrypt
{
/// <summary>
/// Шифрация и дешифрация XML документа с использованием сертификата и алгоритма ГОСТ Р 34.12-2015 Магма.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, шифрует его целиком с использованием сертификата, а затем дешифрует зашифрованный документ.
/// </remarks>
[TestFixture(Description = "Шифрация и дешифрация XML документа с использованием сертификата и алгоритма ГОСТ Р 34.12-2015 Магма")]
public sealed class MagmaEncryptedXmlCertificateTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldEncryptXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
var expectedXml = xmlDocument.OuterXml.Replace("\r\n", "\n");
// When
var encryptedXmlDocument = EncryptXmlDocument(xmlDocument, certificate);
var decryptedXmlDocument = DecryptXmlDocument(encryptedXmlDocument);
var actualXml = decryptedXmlDocument.OuterXml.Replace("\r\n", "\n");
// Then
Assert.AreEqual(expectedXml, actualXml);
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.EncryptedXmlExample);
return document;
}
private static XmlDocument EncryptXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
var publicKeyAlgorithm = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm();
using (var sessionKey = new Gost_3412_M_SymmetricAlgorithm(publicKeyAlgorithm.ProviderType))
{
var encryptedSessionKeyData = GostEncryptedXml.EncryptKey(sessionKey, publicKeyAlgorithm);
var encryptedSessionKey = new EncryptedKey
{
CipherData = new CipherData(encryptedSessionKeyData),
EncryptionMethod = new EncryptionMethod(publicKeyAlgorithm.KeyExchangeAlgorithm),
};
encryptedSessionKey.KeyInfo.AddClause(new KeyInfoX509Data(certificate));
var elementEncryptedData = new EncryptedData
{
EncryptionMethod = new EncryptionMethod(sessionKey.AlgorithmName),
};
var encryptedXml = new GostEncryptedXml();
var xmlBytes = Encoding.UTF8.GetBytes(xmlDocument.OuterXml);
var encryptedData = encryptedXml.EncryptData(xmlBytes, sessionKey);
elementEncryptedData.CipherData.CipherValue = encryptedData;
elementEncryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedSessionKey));
GostEncryptedXml.ReplaceElement(xmlDocument.DocumentElement, elementEncryptedData, false);
}
return xmlDocument;
}
private static XmlDocument DecryptXmlDocument(XmlDocument encryptedXmlDocument)
{
// Создание объекта для дешифрации XML
var encryptedXml = new GostEncryptedXml(encryptedXmlDocument);
// Расшифровка зашифрованных элементов документа
encryptedXml.DecryptDocument();
return encryptedXmlDocument;
}
}
}

View File

@@ -0,0 +1,102 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Sign
{
/// <summary>
/// Подпись и проверка подписи XML-документа с использованием сертификата.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, подписывает определенную часть данного документа с использованием сертификата,
/// а затем проверяет полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи XML-документа с использованием сертификата")]
public class SignedXmlCertificateTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, certificate);
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.SignedXmlExample);
return document;
}
private static XmlDocument SignXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(xmlDocument);
// Установка ключа для создания подписи
signedXml.SetSigningCertificate(certificate);
// Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования
var dataReference = new Reference { Uri = "#Id1", DigestMethod = GetDigestMethod(certificate) };
// Установка ссылки на узел
signedXml.AddReference(dataReference);
// Установка информации о сертификате, который использовался для создания подписи
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
// Вычисление подписи
signedXml.ComputeSignature();
// Получение XML-представления подписи
var signatureXml = signedXml.GetXml();
// Добавление подписи в исходный документ
xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(signatureXml, true));
return xmlDocument;
}
private static bool VerifyXmlDocumentSignature(XmlDocument signedXmlDocument)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(signedXmlDocument);
// Поиск узла с подписью
var nodeList = signedXmlDocument.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);
// Загрузка найденной подписи
signedXml.LoadXml((XmlElement)nodeList[0]);
// Проверка подписи
return signedXml.CheckSignature();
}
private static string GetDigestMethod(X509Certificate2 certificate)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm())
using (var hashAlgorithm = publicKey.CreateHashAlgorithm())
{
return hashAlgorithm.AlgorithmName;
}
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Sign
{
/// <summary>
/// Подпись и проверка подписи всего XML документа с использованием сертификата
/// </summary>
/// <remarks>
/// Тест создает XML-документ, подписывает весь документ с использованием сертификата,
/// а затем проверяет полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи всего XML документа с использованием сертификата")]
public class SignedXmlDocumentTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, certificate);
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.SignedXmlExample);
return document;
}
private static XmlDocument SignXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(xmlDocument);
// Установка ключа для создания подписи
signedXml.SetSigningCertificate(certificate);
// Ссылка на весь документ и указание алгоритма хэширования
var dataReference = new Reference { Uri = "", DigestMethod = GetDigestMethod(certificate) };
// Метод преобразования для подписи всего документа
dataReference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
// Установка ссылки на узел
signedXml.AddReference(dataReference);
// Установка информации о сертификате, который использовался для создания подписи
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
// Вычисление подписи
signedXml.ComputeSignature();
// Получение XML-представления подписи
var signatureXml = signedXml.GetXml();
// Добавление подписи в исходный документ
xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(signatureXml, true));
return xmlDocument;
}
private static bool VerifyXmlDocumentSignature(XmlDocument signedXmlDocument)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(signedXmlDocument);
// Поиск узла с подписью
var nodeList = signedXmlDocument.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);
// Загрузка найденной подписи
signedXml.LoadXml((XmlElement)nodeList[0]);
// Проверка подписи
return signedXml.CheckSignature();
}
private static string GetDigestMethod(X509Certificate2 certificate)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm())
using (var hashAlgorithm = publicKey.CreateHashAlgorithm())
{
return hashAlgorithm.AlgorithmName;
}
}
}
}

View File

@@ -0,0 +1,140 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Gost_R3410;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Sign
{
/// <summary>
/// Подпись и проверка подписи XML-документа с использованием контейнера ключей.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, подписывает определенную часть данного документа с использованием контейнера ключей,
/// а затем проверяет полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи XML-документа с использованием контейнера ключей")]
public class SignedXmlKeyContainerTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2001_Certificates))]
public void ShouldSignXmlWithGost_R3410_2001(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var keyContainer = certificate.GetPrivateKeyInfo();
var signingKey = new Gost_R3410_2001_AsymmetricAlgorithm(keyContainer);
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, new Gost_R3410_2001_KeyValue(signingKey));
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_256_Certificates))]
public void ShouldSignXmlWithGost_R3410_2012_256(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var keyContainer = certificate.GetPrivateKeyInfo();
var signingKey = new Gost_R3410_2012_256_AsymmetricAlgorithm(keyContainer);
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, new Gost_R3410_2012_256_KeyValue(signingKey));
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_2012_512_Certificates))]
public void ShouldSignXmlWithGost_R3410_2012_512(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var keyContainer = certificate.GetPrivateKeyInfo();
var signingKey = new Gost_R3410_2012_512_AsymmetricAlgorithm(keyContainer);
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, new Gost_R3410_2012_512_KeyValue(signingKey));
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.SignedXmlExample);
return document;
}
private static XmlDocument SignXmlDocument(XmlDocument xmlDocument, GostKeyValue keyValue)
{
var signingKey = keyValue.PublicKey;
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(xmlDocument);
// Установка ключа для создания подписи
signedXml.SigningKey = signingKey;
// Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования
var dataReference = new Reference { Uri = "#Id1", DigestMethod = GetDigestMethod(signingKey) };
// Установка ссылки на узел
signedXml.AddReference(dataReference);
// Установка информации о ключе, который использовался для создания подписи
var keyInfo = new KeyInfo();
keyInfo.AddClause(keyValue);
signedXml.KeyInfo = keyInfo;
// Вычисление подписи
signedXml.ComputeSignature();
// Получение XML-представления подписи
var signatureXml = signedXml.GetXml();
// Добавление подписи в исходный документ
xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(signatureXml, true));
return xmlDocument;
}
private static bool VerifyXmlDocumentSignature(XmlDocument signedXmlDocument)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(signedXmlDocument);
// Поиск узла с подписью
var nodeList = signedXmlDocument.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);
// Загрузка найденной подписи
signedXml.LoadXml((XmlElement)nodeList[0]);
// Проверка подписи
return signedXml.CheckSignature();
}
private static string GetDigestMethod(GostAsymmetricAlgorithm signingKey)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var hashAlgorithm = signingKey.CreateHashAlgorithm())
{
return hashAlgorithm.AlgorithmName;
}
}
}
}

View File

@@ -0,0 +1,154 @@
using System;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Sign
{
/// <summary>
/// Подпись и проверка подписи запроса к сервису СМЭВ (Система межведомственного электронного взаимодействия).
/// </summary>
/// <remarks>
/// Тест создает запрос к сервису СМЭВ, подписывает определенную часть данного запроса с использованием сертификата,
/// а затем проверяет полученную цифровую подпись.
/// </remarks>
[TestFixture(Description = "Подпись и проверка подписи запроса к сервису СМЭВ (Система межведомственного электронного взаимодействия)")]
public sealed class SignedXmlSmevTest
{
private const string WsSecurityExtNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private const string WsSecurityUtilityNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var smevRequest = CreateSmevRequest();
// When
var signedXmlDocument = SignSmevRequest(smevRequest, certificate);
// Then
Assert.IsTrue(VerifySmevRequestSignature(signedXmlDocument));
}
private static XmlDocument CreateSmevRequest()
{
var document = new XmlDocument();
document.LoadXml(Resources.SmevExample);
return document;
}
private static XmlDocument SignSmevRequest(XmlDocument smevRequest, X509Certificate2 certificate)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(smevRequest) { GetIdElementHandler = GetSmevIdElement };
// Установка ключа для создания подписи
signedXml.SetSigningCertificate(certificate);
// Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования
var dataReference = new Reference { Uri = "#body", DigestMethod = GetDigestMethod(certificate) };
// Метод преобразования, применяемый к данным перед их подписью (в соответствии с методическими рекомендациями СМЭВ)
var dataTransform = new XmlDsigExcC14NTransform();
dataReference.AddTransform(dataTransform);
// Установка ссылки на узел
signedXml.AddReference(dataReference);
// Установка алгоритма нормализации узла SignedInfo (в соответствии с методическими рекомендациями СМЭВ)
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
// Установка алгоритма хэширования (в соответствии с методическими рекомендациями СМЭВ)
signedXml.SignedInfo.SignatureMethod = GetSignatureMethod(certificate);
// Вычисление подписи
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(certificate.RawData);
return smevRequest;
}
private static bool VerifySmevRequestSignature(XmlDocument signedSmevRequest)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(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 certificate = new X509Certificate2(Convert.FromBase64String(binaryTokenElement.InnerText));
// Проверка подписи
return signedXml.CheckSignature(certificate.GetPublicKeyAlgorithm());
}
}
}
return false;
}
private static XmlElement GetSmevIdElement(XmlDocument document, string idValue)
{
var namespaceManager = new XmlNamespaceManager(document.NameTable);
namespaceManager.AddNamespace("wsu", WsSecurityUtilityNamespace);
return document.SelectSingleNode("//*[@wsu:Id='" + idValue + "']", namespaceManager) as XmlElement;
}
private static string GetSignatureMethod(X509Certificate2 certificate)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm())
{
return publicKey.SignatureAlgorithm;
}
}
private static string GetDigestMethod(X509Certificate2 certificate)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm())
using (var hashAlgorithm = publicKey.CreateHashAlgorithm())
{
return hashAlgorithm.AlgorithmName;
}
}
}
}

View File

@@ -0,0 +1,131 @@
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using GostCryptography.Base;
using GostCryptography.Tests.Properties;
using GostCryptography.Xml;
using NUnit.Framework;
namespace GostCryptography.Tests.Xml.Sign
{
/// <summary>
/// Подпись и проверка подписи XML-документа с предварительным XSLT-преобразованием подписываемых данных.
/// </summary>
/// <remarks>
/// Тест создает XML-документ, подписывает определенную часть данного документа с использованием сертификата,
/// предварительно осуществляя XSLT-преобразование подписываемых данных, а затем проверяет полученную цифровую подпись.
/// </remarks>
[Ignore("TODO: Нужно произвести диагностику с подключением логирования")]
[TestFixture(Description = "Подпись и проверка подписи XML-документа с предварительным XSLT-преобразованием подписываемых данных")]
public sealed class SignedXmlTransformTest
{
[Test]
[TestCaseSource(typeof(TestConfig), nameof(TestConfig.Gost_R3410_Certificates))]
public void ShouldSignXml(TestCertificateInfo testCase)
{
// Given
var certificate = testCase.Certificate;
var xmlDocument = CreateXmlDocument();
// When
var signedXmlDocument = SignXmlDocument(xmlDocument, certificate);
// Then
Assert.IsTrue(VerifyXmlDocumentSignature(signedXmlDocument));
}
private static XmlDocument CreateXmlDocument()
{
var document = new XmlDocument();
document.LoadXml(Resources.SignedXmlExample);
return document;
}
private static XmlDocument SignXmlDocument(XmlDocument xmlDocument, X509Certificate2 certificate)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(xmlDocument);
// Установка ключа для создания подписи
signedXml.SetSigningCertificate(certificate);
// Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования
var dataReference = new Reference { Uri = "#Id1", DigestMethod = GetDigestMethod(certificate) };
// Метод преобразования, применяемый к данным перед их подписью
var dataTransform = CreateDataTransform();
dataReference.AddTransform(dataTransform);
// Установка ссылки на узел
signedXml.AddReference(dataReference);
// Установка информации о сертификате, который использовался для создания подписи
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(certificate));
signedXml.KeyInfo = keyInfo;
// Вычисление подписи
signedXml.ComputeSignature();
// Получение XML-представления подписи
var signatureXml = signedXml.GetXml();
// Добавление подписи в исходный документ
xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(signatureXml, true));
return xmlDocument;
}
private static XmlDsigXsltTransform CreateDataTransform()
{
var dataTransformDocument = new XmlDocument();
dataTransformDocument.LoadXml(@"
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
<xsl:template match='/'>
<xsl:apply-templates />
</xsl:template>
<xsl:template match='*'>
<xsl:copy>
<xsl:copy-of select='@*' />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match='ds:Signature' />
</xsl:stylesheet>");
var dataTransform = new XmlDsigXsltTransform();
dataTransform.LoadInnerXml(dataTransformDocument.ChildNodes);
return dataTransform;
}
private static bool VerifyXmlDocumentSignature(XmlDocument signedXmlDocument)
{
// Создание подписчика XML-документа
var signedXml = new GostSignedXml(signedXmlDocument);
// Поиск узла с подписью
var nodeList = signedXmlDocument.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);
// Загрузка найденной подписи
signedXml.LoadXml((XmlElement)nodeList[0]);
// Проверка подписи
return signedXml.CheckSignature();
}
private static string GetDigestMethod(X509Certificate2 certificate)
{
// Имя алгоритма вычисляем динамически, чтобы сделать код теста универсальным
using (var publicKey = (GostAsymmetricAlgorithm)certificate.GetPublicKeyAlgorithm())
using (var hashAlgorithm = publicKey.CreateHashAlgorithm())
{
return hashAlgorithm.AlgorithmName;
}
}
}
}