Basic formatting applied. Unnecessary comments have been removed. Suspicious code is covered by TODO.
1313 lines
50 KiB
C#
1313 lines
50 KiB
C#
using GostCryptography.Asn1.Gost.Gost_28147_89;
|
||
using GostCryptography.Asn1.Gost.Gost_R3410;
|
||
using GostCryptography.Base;
|
||
using GostCryptography.Properties;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Runtime.InteropServices;
|
||
using System.Security;
|
||
using System.Security.Cryptography;
|
||
using System.Security.Cryptography.X509Certificates;
|
||
using System.Text;
|
||
using System.Threading;
|
||
|
||
namespace GostCryptography.Native
|
||
{
|
||
/// <summary>
|
||
/// Вспомогательные методы для работы с Microsoft CryptoAPI
|
||
/// </summary>
|
||
[SecurityCritical]
|
||
public static class CryptoApiHelper
|
||
{
|
||
/// <summary>
|
||
/// Возвращает <see langword="true"/>, если заданный провайдер установлен
|
||
/// </summary>
|
||
[SecurityCritical]
|
||
public static bool IsInstalled(ProviderType providerType)
|
||
{
|
||
try
|
||
{
|
||
var providerHandle = GetProviderHandle(providerType);
|
||
return !providerHandle.IsInvalid;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2001
|
||
/// </summary>
|
||
/// <exception cref="CryptographicException">Провайдер не установлен</exception>
|
||
[SecuritySafeCritical]
|
||
public static ProviderType GetAvailableProviderType_2001()
|
||
{
|
||
if (IsInstalled(ProviderType.VipNet))
|
||
{
|
||
return ProviderType.VipNet;
|
||
}
|
||
|
||
if (IsInstalled(ProviderType.CryptoPro))
|
||
{
|
||
return ProviderType.CryptoPro;
|
||
}
|
||
|
||
throw ExceptionUtility.CryptographicException(Resources.Provider_2001_IsNotInstalled);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2012/512
|
||
/// </summary>
|
||
/// <exception cref="CryptographicException">Провайдер не установлен</exception>
|
||
[SecuritySafeCritical]
|
||
public static ProviderType GetAvailableProviderType_2012_512()
|
||
{
|
||
if (IsInstalled(ProviderType.VipNet_2012_512))
|
||
{
|
||
return ProviderType.VipNet_2012_512;
|
||
}
|
||
|
||
if (IsInstalled(ProviderType.CryptoPro_2012_512))
|
||
{
|
||
return ProviderType.CryptoPro_2012_512;
|
||
}
|
||
|
||
throw ExceptionUtility.CryptographicException(Resources.Provider_2012_512_IsNotInstalled);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2012/1024
|
||
/// </summary>
|
||
/// <exception cref="CryptographicException">Провайдер не установлен</exception>
|
||
[SecuritySafeCritical]
|
||
public static ProviderType GetAvailableProviderType_2012_1024()
|
||
{
|
||
if (IsInstalled(ProviderType.VipNet_2012_1024))
|
||
{
|
||
return ProviderType.VipNet_2012_1024;
|
||
}
|
||
|
||
if (IsInstalled(ProviderType.CryptoPro_2012_1024))
|
||
{
|
||
return ProviderType.CryptoPro_2012_1024;
|
||
}
|
||
|
||
throw ExceptionUtility.CryptographicException(Resources.Provider_2012_1024_IsNotInstalled);
|
||
}
|
||
|
||
#region Общие объекты
|
||
|
||
private static readonly object ProviderHandleSync = new object();
|
||
private static volatile Dictionary<ProviderType, SafeProvHandleImpl> _providerHandles = new Dictionary<ProviderType, SafeProvHandleImpl>();
|
||
|
||
public static SafeProvHandleImpl GetProviderHandle(ProviderType providerType)
|
||
{
|
||
if (!_providerHandles.ContainsKey(providerType))
|
||
{
|
||
lock (ProviderHandleSync)
|
||
{
|
||
if (!_providerHandles.ContainsKey(providerType))
|
||
{
|
||
var providerParams = new CspParameters(providerType.ToInt());
|
||
var providerHandle = AcquireProvider(providerParams);
|
||
|
||
Thread.MemoryBarrier();
|
||
|
||
_providerHandles.Add(providerType, providerHandle);
|
||
}
|
||
}
|
||
}
|
||
|
||
return _providerHandles[providerType];
|
||
}
|
||
|
||
private static readonly object RandomNumberGeneratorSync = new object();
|
||
private static volatile Dictionary<ProviderType, RNGCryptoServiceProvider> _randomNumberGenerators = new Dictionary<ProviderType, RNGCryptoServiceProvider>();
|
||
|
||
public static RNGCryptoServiceProvider GetRandomNumberGenerator(ProviderType providerType)
|
||
{
|
||
if (!_randomNumberGenerators.ContainsKey(providerType))
|
||
{
|
||
lock (RandomNumberGeneratorSync)
|
||
{
|
||
if (!_randomNumberGenerators.ContainsKey(providerType))
|
||
{
|
||
var providerParams = new CspParameters(providerType.ToInt());
|
||
var randomNumberGenerator = new RNGCryptoServiceProvider(providerParams);
|
||
|
||
Thread.MemoryBarrier();
|
||
|
||
_randomNumberGenerators.Add(providerType, randomNumberGenerator);
|
||
}
|
||
}
|
||
}
|
||
|
||
return _randomNumberGenerators[providerType];
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для работы с криптографическим провайдером
|
||
|
||
public static SafeProvHandleImpl AcquireProvider(CspParameters providerParameters)
|
||
{
|
||
var providerHandle = SafeProvHandleImpl.InvalidHandle;
|
||
|
||
var dwFlags = Constants.CRYPT_VERIFYCONTEXT;
|
||
|
||
if ((providerParameters.Flags & CspProviderFlags.UseMachineKeyStore) != CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_MACHINE_KEYSET;
|
||
}
|
||
|
||
if (!CryptoApi.CryptAcquireContext(ref providerHandle, providerParameters.KeyContainerName, providerParameters.ProviderName, (uint)providerParameters.ProviderType, dwFlags))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return providerHandle;
|
||
}
|
||
|
||
public static SafeProvHandleImpl OpenProvider(CspParameters providerParameters)
|
||
{
|
||
var providerHandle = SafeProvHandleImpl.InvalidHandle;
|
||
var dwFlags = MapCspProviderFlags(providerParameters.Flags);
|
||
|
||
if (!CryptoApi.CryptAcquireContext(ref providerHandle, providerParameters.KeyContainerName, providerParameters.ProviderName, (uint)providerParameters.ProviderType, dwFlags))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return providerHandle;
|
||
}
|
||
|
||
public static SafeProvHandleImpl CreateProvider(CspParameters providerParameters)
|
||
{
|
||
var providerHandle = SafeProvHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptAcquireContext(ref providerHandle, providerParameters.KeyContainerName, providerParameters.ProviderName, (uint)providerParameters.ProviderType, Constants.CRYPT_NEWKEYSET))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return providerHandle;
|
||
}
|
||
|
||
private static uint MapCspProviderFlags(CspProviderFlags flags)
|
||
{
|
||
uint dwFlags = 0;
|
||
|
||
if ((flags & CspProviderFlags.UseMachineKeyStore) != CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_MACHINE_KEYSET;
|
||
}
|
||
|
||
if ((flags & CspProviderFlags.NoPrompt) != CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_PREGEN;
|
||
}
|
||
|
||
return dwFlags;
|
||
}
|
||
|
||
public static void SetProviderParameter(SafeProvHandleImpl providerHandle, int keyNumber, uint keyParamId, IntPtr keyParamValue)
|
||
{
|
||
if ((keyParamId == Constants.PP_KEYEXCHANGE_PIN) || (keyParamId == Constants.PP_SIGNATURE_PIN))
|
||
{
|
||
if (keyNumber == Constants.AT_KEYEXCHANGE)
|
||
{
|
||
keyParamId = Constants.PP_KEYEXCHANGE_PIN;
|
||
}
|
||
else if (keyNumber == Constants.AT_SIGNATURE)
|
||
{
|
||
keyParamId = Constants.PP_SIGNATURE_PIN;
|
||
}
|
||
else
|
||
{
|
||
throw ExceptionUtility.NotSupported(Resources.KeyAlgorithmNotSupported);
|
||
}
|
||
}
|
||
|
||
if (!CryptoApi.CryptSetProvParam(providerHandle, keyParamId, keyParamValue, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
public static ProviderType GetProviderType(SafeProvHandleImpl providerHandle)
|
||
{
|
||
uint providerTypeLen = sizeof(uint);
|
||
byte[] dwData = new byte[sizeof(uint)];
|
||
|
||
if (!CryptoApi.CryptGetProvParam(providerHandle, Constants.PP_PROVTYPE, dwData, ref providerTypeLen, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var providerType = BitConverter.ToUInt32(dwData, 0);
|
||
|
||
return (ProviderType)providerType;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает список имен контейнеров указанного криптопровайдера
|
||
/// </summary>
|
||
/// <param name="providerType">Тип криптопровайдера</param>
|
||
/// <param name="fullContainerName">Вернуть полное имя контейнера</param>
|
||
/// <param name="storeLocation">Область поиска контейнеров</param>
|
||
public static IEnumerable<string> GetContainers(ProviderType providerType, bool fullContainerName = true, StoreLocation storeLocation = StoreLocation.LocalMachine)
|
||
{
|
||
var containers = new List<string>();
|
||
|
||
var providerParameters = new CspParameters((int)providerType);
|
||
|
||
if (storeLocation == StoreLocation.LocalMachine)
|
||
{
|
||
providerParameters.Flags |= CspProviderFlags.UseMachineKeyStore;
|
||
}
|
||
|
||
using (var providerHandle = AcquireProvider(providerParameters))
|
||
{
|
||
var containerNameMaxLength = 0U;
|
||
var flag = Constants.CRYPT_FIRST;
|
||
|
||
if (fullContainerName)
|
||
{
|
||
flag |= (providerType.IsVipNet() ? Constants.CRYPT_UNIQUE : Constants.CRYPT_FQCN);
|
||
}
|
||
|
||
if (!CryptoApi.CryptGetProvParam(providerHandle, Constants.PP_ENUMCONTAINERS, (StringBuilder)null, ref containerNameMaxLength, flag))
|
||
{
|
||
if (Marshal.GetLastWin32Error() != Constants.ERROR_NO_MORE_ITEMS)
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return containers;
|
||
}
|
||
|
||
while (true)
|
||
{
|
||
var containerName = new StringBuilder((int)containerNameMaxLength);
|
||
|
||
if (!CryptoApi.CryptGetProvParam(providerHandle, Constants.PP_ENUMCONTAINERS, containerName, ref containerNameMaxLength, flag))
|
||
{
|
||
if (Marshal.GetLastWin32Error() != Constants.ERROR_NO_MORE_ITEMS)
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
containers.Add(containerName.ToString());
|
||
|
||
flag = Constants.CRYPT_NEXT;
|
||
|
||
if (fullContainerName)
|
||
{
|
||
flag |= (providerType.IsVipNet() ? Constants.CRYPT_UNIQUE : Constants.CRYPT_FQCN);
|
||
}
|
||
}
|
||
}
|
||
|
||
return containers;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает сертификат X.509 для указанного ключа
|
||
/// </summary>
|
||
/// <param name="keyHandle">Дескриптор ключа сертификата</param>
|
||
public static X509Certificate2 GetKeyCertificate(SafeKeyHandleImpl keyHandle)
|
||
{
|
||
uint certDataLength = 0;
|
||
|
||
if (!CryptoApi.CryptGetKeyParam(keyHandle, Constants.KP_CERTIFICATE, null, ref certDataLength, 0))
|
||
{
|
||
if (Marshal.GetLastWin32Error() != Constants.ERROR_NO_SUCH_CERTIFICATE)
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
var certData = new byte[certDataLength];
|
||
|
||
if (!CryptoApi.CryptGetKeyParam(keyHandle, Constants.KP_CERTIFICATE, certData, ref certDataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return new X509Certificate2(certData);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для работы с функцией хэширования криптографического провайдера
|
||
|
||
public static SafeHashHandleImpl CreateHash_3411_94(SafeProvHandleImpl providerHandle)
|
||
{
|
||
return CreateHash_3411(providerHandle, Constants.CALG_GR3411);
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHash_3411_2012_256(SafeProvHandleImpl providerHandle)
|
||
{
|
||
return CreateHash_3411(providerHandle, Constants.CALG_GR3411_2012_256);
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHash_3411_2012_512(SafeProvHandleImpl providerHandle)
|
||
{
|
||
return CreateHash_3411(providerHandle, Constants.CALG_GR3411_2012_512);
|
||
}
|
||
|
||
private static SafeHashHandleImpl CreateHash_3411(SafeProvHandleImpl providerHandle, int hashAlgId)
|
||
{
|
||
var hashHandle = SafeHashHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptCreateHash(providerHandle, (uint)hashAlgId, SafeKeyHandleImpl.InvalidHandle, 0, ref hashHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return hashHandle;
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHashImit(SafeProvHandleImpl providerHandle, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
var hashImitHandle = SafeHashHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptCreateHash(providerHandle, Constants.CALG_G28147_IMIT, symKeyHandle, 0, ref hashImitHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return hashImitHandle;
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHashHMAC_94(ProviderType providerType, SafeProvHandleImpl providerHandle, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
var hmacAlgId = providerType.IsVipNet() ? Constants.CALG_GR3411 : Constants.CALG_GR3411_HMAC;
|
||
return CreateHashHMAC(providerHandle, symKeyHandle, hmacAlgId);
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHashHMAC_2012_256(ProviderType providerType, SafeProvHandleImpl providerHandle, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
var hmacAlgId = providerType.IsVipNet() ? Constants.CALG_GR3411_2012_256 : Constants.CALG_GR3411_2012_256_HMAC;
|
||
return CreateHashHMAC(providerHandle, symKeyHandle, hmacAlgId);
|
||
}
|
||
|
||
public static SafeHashHandleImpl CreateHashHMAC_2012_512(ProviderType providerType, SafeProvHandleImpl providerHandle, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
var hmacAlgId = providerType.IsVipNet() ? Constants.CALG_GR3411_2012_512 : Constants.CALG_GR3411_2012_512_HMAC;
|
||
return CreateHashHMAC(providerHandle, symKeyHandle, hmacAlgId);
|
||
}
|
||
|
||
private static SafeHashHandleImpl CreateHashHMAC(SafeProvHandleImpl providerHandle, SafeKeyHandleImpl symKeyHandle, int hmacAlgId)
|
||
{
|
||
var hashHmacHandle = SafeHashHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptCreateHash(providerHandle, (uint)hmacAlgId, symKeyHandle, 0, ref hashHmacHandle))
|
||
{
|
||
var errorCode = Marshal.GetLastWin32Error();
|
||
|
||
if (errorCode == Constants.NTE_BAD_ALGID)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Resources.AlgorithmNotAvailable);
|
||
}
|
||
|
||
throw ExceptionUtility.CryptographicException(errorCode);
|
||
}
|
||
|
||
return hashHmacHandle;
|
||
}
|
||
|
||
public static unsafe void HashData(SafeHashHandleImpl hashHandle, byte[] data, int dataOffset, int dataLength)
|
||
{
|
||
if (data == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(data));
|
||
}
|
||
|
||
if (dataOffset < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataOffset));
|
||
}
|
||
|
||
if (dataLength < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataLength));
|
||
}
|
||
|
||
if (data.Length < dataOffset + dataLength)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataLength));
|
||
}
|
||
|
||
if (dataLength > 0)
|
||
{
|
||
fixed (byte* dataRef = data)
|
||
{
|
||
var dataOffsetRef = dataRef + dataOffset;
|
||
|
||
if (!CryptoApi.CryptHashData(hashHandle, dataOffsetRef, (uint)dataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public static byte[] EndHashData(SafeHashHandleImpl hashHandle)
|
||
{
|
||
uint dataLength = 0;
|
||
|
||
if (!CryptoApi.CryptGetHashParam(hashHandle, Constants.HP_HASHVAL, null, ref dataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var data = new byte[dataLength];
|
||
|
||
if (!CryptoApi.CryptGetHashParam(hashHandle, Constants.HP_HASHVAL, data, ref dataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
public static void HashKeyExchange(SafeHashHandleImpl hashHandle, SafeKeyHandleImpl keyExchangeHandle)
|
||
{
|
||
if (!CryptoApi.CryptHashSessionKey(hashHandle, keyExchangeHandle, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для работы с функцией шифрования криптографического провайдера
|
||
|
||
public static int EncryptData(ProviderType providerType, SafeKeyHandleImpl symKeyHandle, byte[] data, int dataOffset, int dataLength, ref byte[] encryptedData, int encryptedDataOffset, PaddingMode paddingMode, bool isDone, bool isStream)
|
||
{
|
||
if (dataOffset < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataOffset));
|
||
}
|
||
|
||
if (dataLength < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataLength));
|
||
}
|
||
|
||
if (dataOffset > data.Length)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataOffset), Resources.InvalidDataOffset);
|
||
}
|
||
|
||
var length = dataLength;
|
||
|
||
if (isDone)
|
||
{
|
||
length += 8;
|
||
}
|
||
|
||
// Выровненные данные
|
||
var dataAlignLength = (uint)dataLength;
|
||
var dataAlignArray = new byte[length];
|
||
Array.Clear(dataAlignArray, 0, length);
|
||
Array.Copy(data, dataOffset, dataAlignArray, 0, dataLength);
|
||
|
||
if (isDone)
|
||
{
|
||
var dataPadding = dataLength & 7;
|
||
var dataPaddingSize = (byte)(8 - dataPadding);
|
||
|
||
// Добавление дополнения данных в зависимости от настроек
|
||
switch (paddingMode)
|
||
{
|
||
case PaddingMode.None:
|
||
if ((dataPadding != 0) && !isStream)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Resources.EncryptInvalidDataSize);
|
||
}
|
||
|
||
break;
|
||
case PaddingMode.Zeros:
|
||
if (dataPadding != 0)
|
||
{
|
||
dataAlignLength += dataPaddingSize;
|
||
|
||
// Дополнение заполняется нулевыми байтами
|
||
}
|
||
|
||
break;
|
||
case PaddingMode.PKCS7:
|
||
{
|
||
dataAlignLength += dataPaddingSize;
|
||
|
||
var paddingIndex = dataLength;
|
||
|
||
// Дополнение заполняется байтами, в каждый из которых записывается размер дополнения
|
||
while (paddingIndex < dataAlignLength)
|
||
{
|
||
dataAlignArray[paddingIndex++] = dataPaddingSize;
|
||
}
|
||
}
|
||
break;
|
||
case PaddingMode.ANSIX923:
|
||
{
|
||
dataAlignLength += dataPaddingSize;
|
||
|
||
// Дополнение заполняется нулевыми, кроме последнего - в него записывается размер дополнения
|
||
dataAlignArray[(int)((IntPtr)(dataAlignLength - 1))] = dataPaddingSize;
|
||
}
|
||
break;
|
||
case PaddingMode.ISO10126:
|
||
{
|
||
dataAlignLength += dataPaddingSize;
|
||
|
||
// Дополнение заполняется случайными байтами, кроме последнего - в него записывается размер дополнения
|
||
var randomPadding = new byte[dataPaddingSize - 1];
|
||
GetRandomNumberGenerator(providerType).GetBytes(randomPadding);
|
||
randomPadding.CopyTo(dataAlignArray, dataLength);
|
||
dataAlignArray[(int)((IntPtr)(dataAlignLength - 1))] = dataPaddingSize;
|
||
}
|
||
break;
|
||
default:
|
||
throw ExceptionUtility.Argument(nameof(paddingMode), Resources.InvalidPaddingMode);
|
||
}
|
||
}
|
||
|
||
// Шифрование данных
|
||
if (!CryptoApi.CryptEncrypt(symKeyHandle, SafeHashHandleImpl.InvalidHandle, false, 0, dataAlignArray, ref dataAlignLength, (uint)length))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
// Копирование результата шифрования данных
|
||
if (encryptedData == null)
|
||
{
|
||
encryptedData = new byte[dataAlignLength];
|
||
|
||
Array.Copy(dataAlignArray, 0L, encryptedData, 0L, dataAlignLength);
|
||
}
|
||
else
|
||
{
|
||
if (encryptedDataOffset < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(encryptedDataOffset));
|
||
}
|
||
|
||
if ((encryptedData.Length < dataAlignLength) || ((encryptedData.Length - dataAlignLength) < encryptedDataOffset))
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(encryptedDataOffset), Resources.InvalidDataOffset);
|
||
}
|
||
|
||
Array.Copy(dataAlignArray, 0L, encryptedData, encryptedDataOffset, dataAlignLength);
|
||
}
|
||
|
||
return (int)dataAlignLength;
|
||
}
|
||
|
||
public static int DecryptData(SafeKeyHandleImpl symKeyHandle, byte[] data, int dataOffset, int dataLength, ref byte[] decryptedData, int decryptedDataOffset, PaddingMode paddingMode, bool isDone)
|
||
{
|
||
if (dataOffset < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataOffset));
|
||
}
|
||
|
||
if (dataLength < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataLength));
|
||
}
|
||
|
||
if ((dataOffset > data.Length) || ((dataOffset + dataLength) > data.Length))
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(dataOffset), Resources.InvalidDataOffset);
|
||
}
|
||
|
||
// Выровненные данные
|
||
var dataAlignLength = (uint)dataLength;
|
||
var dataAlign = new byte[dataAlignLength];
|
||
Array.Copy(data, dataOffset, dataAlign, 0L, dataAlignLength);
|
||
|
||
// Расшифровка данных
|
||
if (!CryptoApi.CryptDecrypt(symKeyHandle, SafeHashHandleImpl.InvalidHandle, false, 0, dataAlign, ref dataAlignLength))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var length = (int)dataAlignLength;
|
||
|
||
if (isDone)
|
||
{
|
||
byte dataPaddingSize = 0;
|
||
|
||
// Удаление дополнения данных в зависимости от настроек
|
||
if (((paddingMode == PaddingMode.PKCS7) || (paddingMode == PaddingMode.ANSIX923)) || (paddingMode == PaddingMode.ISO10126))
|
||
{
|
||
if (dataAlignLength < 8)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
// Размер дополнения находится в последнем байте
|
||
dataPaddingSize = dataAlign[(int)((IntPtr)(dataAlignLength - 1))];
|
||
|
||
if (dataPaddingSize > 8)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
// Проверка корректности дополнения данных
|
||
if (paddingMode == PaddingMode.PKCS7)
|
||
{
|
||
for (var paddingIndex = dataAlignLength - dataPaddingSize; paddingIndex < (dataAlignLength - 1); paddingIndex++)
|
||
{
|
||
if (dataAlign[paddingIndex] != dataPaddingSize)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
}
|
||
}
|
||
else if (paddingMode == PaddingMode.ANSIX923)
|
||
{
|
||
for (var paddingIndex = dataAlignLength - dataPaddingSize; paddingIndex < (dataAlignLength - 1); paddingIndex++)
|
||
{
|
||
if (dataAlign[paddingIndex] != 0)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if ((paddingMode != PaddingMode.None) && (paddingMode != PaddingMode.Zeros))
|
||
{
|
||
throw ExceptionUtility.Argument(nameof(paddingMode), Resources.InvalidPaddingMode);
|
||
}
|
||
|
||
length -= dataPaddingSize;
|
||
}
|
||
|
||
if (decryptedData == null)
|
||
{
|
||
decryptedData = new byte[length];
|
||
|
||
Array.Copy(dataAlign, 0, decryptedData, 0, length);
|
||
}
|
||
else
|
||
{
|
||
if (decryptedDataOffset < 0)
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(decryptedDataOffset));
|
||
}
|
||
|
||
if ((decryptedData.Length < length) || ((decryptedData.Length - length) < decryptedDataOffset))
|
||
{
|
||
throw ExceptionUtility.ArgumentOutOfRange(nameof(decryptedData), Resources.InvalidDataOffset);
|
||
}
|
||
|
||
Array.Copy(dataAlign, 0, decryptedData, decryptedDataOffset, length);
|
||
}
|
||
|
||
return length;
|
||
}
|
||
|
||
public static void EndEncrypt(ProviderType providerType, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
uint dataLength = 0;
|
||
var data = new byte[32];
|
||
var success = CryptoApi.CryptEncrypt(symKeyHandle, SafeHashHandleImpl.InvalidHandle, true, 0, data, ref dataLength, (uint)data.Length);
|
||
|
||
if (!success)
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
public static void EndDecrypt(ProviderType providerType, SafeKeyHandleImpl symKeyHandle)
|
||
{
|
||
uint dataLength = 0;
|
||
var data = new byte[0];
|
||
var success = CryptoApi.CryptDecrypt(symKeyHandle, SafeHashHandleImpl.InvalidHandle, true, 0, data, ref dataLength) || providerType.IsVipNet();
|
||
|
||
if (!success)
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для работы с ключами криптографического провайдера
|
||
|
||
public static SafeKeyHandleImpl GenerateKey(SafeProvHandleImpl providerHandle, int algId, CspProviderFlags flags)
|
||
{
|
||
var keyHandle = SafeKeyHandleImpl.InvalidHandle;
|
||
var dwFlags = MapCspKeyFlags(flags);
|
||
|
||
if (!CryptoApi.CryptGenKey(providerHandle, (uint)algId, dwFlags, ref keyHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return keyHandle;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl GenerateDhEphemeralKey(ProviderType providerType, SafeProvHandleImpl providerHandle, int algId, string digestParamSet, string publicKeyParamSet)
|
||
{
|
||
var keyHandle = SafeKeyHandleImpl.InvalidHandle;
|
||
var dwFlags = MapCspKeyFlags(CspProviderFlags.NoFlags) | Constants.CRYPT_PREGEN;
|
||
|
||
if (!CryptoApi.CryptGenKey(providerHandle, (uint)algId, dwFlags, ref keyHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
if (!providerType.IsVipNet())
|
||
{
|
||
SetKeyParameterString(keyHandle, Constants.KP_HASHOID, digestParamSet);
|
||
}
|
||
|
||
SetKeyParameterString(keyHandle, Constants.KP_DHOID, publicKeyParamSet);
|
||
SetKeyParameter(keyHandle, Constants.KP_X, null);
|
||
|
||
return keyHandle;
|
||
}
|
||
|
||
private static uint MapCspKeyFlags(CspProviderFlags flags)
|
||
{
|
||
uint dwFlags = 0;
|
||
|
||
if ((flags & CspProviderFlags.UseNonExportableKey) == CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_EXPORTABLE;
|
||
}
|
||
|
||
if ((flags & CspProviderFlags.UseArchivableKey) != CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_ARCHIVABLE;
|
||
}
|
||
|
||
if ((flags & CspProviderFlags.UseUserProtectedKey) != CspProviderFlags.NoFlags)
|
||
{
|
||
dwFlags |= Constants.CRYPT_USER_PROTECTED;
|
||
}
|
||
|
||
return dwFlags;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl GetUserKey(SafeProvHandleImpl providerHandle, int keyNumber)
|
||
{
|
||
var keyHandle = SafeKeyHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptGetUserKey(providerHandle, (uint)keyNumber, ref keyHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return keyHandle;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl DeriveSymKey(SafeProvHandleImpl providerHandle, SafeHashHandleImpl hashHandle)
|
||
{
|
||
var symKeyHandle = SafeKeyHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptDeriveKey(providerHandle, Constants.CALG_G28147, hashHandle, Constants.CRYPT_EXPORTABLE, ref symKeyHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return symKeyHandle;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl DuplicateKey(IntPtr sourceKeyHandle)
|
||
{
|
||
var keyHandle = SafeKeyHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptDuplicateKey(sourceKeyHandle, null, 0, ref keyHandle))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return keyHandle;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl DuplicateKey(SafeKeyHandleImpl sourceKeyHandle)
|
||
{
|
||
return DuplicateKey(sourceKeyHandle.DangerousGetHandle());
|
||
}
|
||
|
||
public static int GetKeyParameterInt32(SafeKeyHandleImpl keyHandle, uint keyParamId)
|
||
{
|
||
const int doubleWordSize = 4;
|
||
|
||
uint dwDataLength = doubleWordSize;
|
||
var dwDataBytes = new byte[doubleWordSize];
|
||
|
||
if (!CryptoApi.CryptGetKeyParam(keyHandle, keyParamId, dwDataBytes, ref dwDataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
if (dwDataLength != doubleWordSize)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
return BitConverter.ToInt32(dwDataBytes, 0);
|
||
}
|
||
|
||
private static string GetKeyParameterString(SafeKeyHandleImpl keyHandle, uint keyParamId)
|
||
{
|
||
var paramValue = GetKeyParameter(keyHandle, keyParamId);
|
||
|
||
return BytesToString(paramValue);
|
||
}
|
||
|
||
private static string BytesToString(byte[] value)
|
||
{
|
||
string valueString;
|
||
|
||
try
|
||
{
|
||
valueString = Encoding.GetEncoding(0).GetString(value);
|
||
|
||
var length = 0;
|
||
|
||
while (length < valueString.Length)
|
||
{
|
||
// Строка заканчивается нулевым символом
|
||
if (valueString[length] == '\0')
|
||
{
|
||
break;
|
||
}
|
||
|
||
length++;
|
||
}
|
||
|
||
if (length == valueString.Length)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Resources.InvalidString);
|
||
}
|
||
|
||
valueString = valueString.Substring(0, length);
|
||
}
|
||
catch (DecoderFallbackException exception)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(exception, Resources.InvalidString);
|
||
}
|
||
|
||
return valueString;
|
||
}
|
||
|
||
public static byte[] GetKeyParameter(SafeKeyHandleImpl keyHandle, uint keyParamId)
|
||
{
|
||
uint dataLength = 0;
|
||
|
||
if (!CryptoApi.CryptGetKeyParam(keyHandle, keyParamId, null, ref dataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var dataBytes = new byte[dataLength];
|
||
|
||
if (!CryptoApi.CryptGetKeyParam(keyHandle, keyParamId, dataBytes, ref dataLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return dataBytes;
|
||
}
|
||
|
||
public static void SetKeyExchangeExportAlgId(ProviderType providerType, SafeKeyHandleImpl keyHandle, int keyExchangeExportAlgId)
|
||
{
|
||
var keyExchangeExportAlgParamId = providerType.IsVipNet() ? Constants.KP_EXPORTID : Constants.KP_ALGID;
|
||
SetKeyParameterInt32(keyHandle, keyExchangeExportAlgParamId, keyExchangeExportAlgId);
|
||
}
|
||
|
||
public static void SetKeyParameterInt32(SafeKeyHandleImpl keyHandle, int keyParamId, int keyParamValue)
|
||
{
|
||
var dwDataBytes = BitConverter.GetBytes(keyParamValue);
|
||
|
||
if (!CryptoApi.CryptSetKeyParam(keyHandle, (uint)keyParamId, dwDataBytes, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
private static void SetKeyParameterString(SafeKeyHandleImpl keyHandle, int keyParamId, string keyParamValue)
|
||
{
|
||
var stringDataBytes = Encoding.GetEncoding(0).GetBytes(keyParamValue);
|
||
|
||
if (!CryptoApi.CryptSetKeyParam(keyHandle, (uint)keyParamId, stringDataBytes, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
public static void SetKeyParameter(SafeKeyHandleImpl keyHandle, int keyParamId, byte[] keyParamValue)
|
||
{
|
||
if (!CryptoApi.CryptSetKeyParam(keyHandle, (uint)keyParamId, keyParamValue, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для экспорта ключей криптографического провайдера
|
||
|
||
public static byte[] ExportCspBlob(SafeKeyHandleImpl symKeyHandle, SafeKeyHandleImpl keyExchangeHandle, int blobType)
|
||
{
|
||
uint exportedKeyLength = 0;
|
||
|
||
if (!CryptoApi.CryptExportKey(symKeyHandle, keyExchangeHandle, (uint)blobType, 0, null, ref exportedKeyLength))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var exportedKeyBytes = new byte[exportedKeyLength];
|
||
|
||
if (!CryptoApi.CryptExportKey(symKeyHandle, keyExchangeHandle, (uint)blobType, 0, exportedKeyBytes, ref exportedKeyLength))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return exportedKeyBytes;
|
||
}
|
||
|
||
public static T ExportPublicKey<T>(SafeKeyHandleImpl symKeyHandle, T keyExchangeParams, int keySize) where T : Gost_R3410_KeyExchangeParams
|
||
{
|
||
var exportedKeyBytes = ExportCspBlob(symKeyHandle, SafeKeyHandleImpl.InvalidHandle, Constants.PUBLICKEYBLOB);
|
||
return DecodePublicBlob(exportedKeyBytes, keyExchangeParams, keySize);
|
||
}
|
||
|
||
private static T DecodePublicBlob<T>(byte[] encodedPublicBlob, T keyExchangeParams, int keySize) where T : Gost_R3410_KeyExchangeParams
|
||
{
|
||
if (encodedPublicBlob == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(encodedPublicBlob));
|
||
}
|
||
|
||
if (encodedPublicBlob.Length < 16 + keySize / 8)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
var gostKeyMask = BitConverter.ToUInt32(encodedPublicBlob, 8);
|
||
|
||
if (gostKeyMask != Constants.GR3410_1_MAGIC)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
var gostKeySize = BitConverter.ToUInt32(encodedPublicBlob, 12);
|
||
|
||
if (gostKeySize != keySize)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
var encodeKeyParameters = new byte[encodedPublicBlob.Length - 16 - keySize / 8];
|
||
Array.Copy(encodedPublicBlob, 16, encodeKeyParameters, 0, encodedPublicBlob.Length - 16 - keySize / 8);
|
||
keyExchangeParams.DecodeParameters(encodeKeyParameters);
|
||
|
||
var publicKey = new byte[keySize / 8];
|
||
Array.Copy(encodedPublicBlob, encodedPublicBlob.Length - keySize / 8, publicKey, 0, keySize / 8);
|
||
keyExchangeParams.PublicKey = publicKey;
|
||
|
||
return keyExchangeParams;
|
||
}
|
||
|
||
public static Gost_28147_89_KeyExchangeInfo ExportKeyExchange(SafeKeyHandleImpl symKeyHandle, SafeKeyHandleImpl keyExchangeHandle)
|
||
{
|
||
var exportedKeyBytes = ExportCspBlob(symKeyHandle, keyExchangeHandle, Constants.SIMPLEBLOB);
|
||
|
||
return DecodeSimpleBlob(exportedKeyBytes);
|
||
}
|
||
|
||
private static Gost_28147_89_KeyExchangeInfo DecodeSimpleBlob(byte[] exportedKeyBytes)
|
||
{
|
||
if (exportedKeyBytes == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(exportedKeyBytes));
|
||
}
|
||
|
||
if (exportedKeyBytes.Length < 16)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
if (BitConverter.ToUInt32(exportedKeyBytes, 4) != Constants.CALG_G28147)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
if (BitConverter.ToUInt32(exportedKeyBytes, 8) != Constants.G28147_MAGIC)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
if (BitConverter.ToUInt32(exportedKeyBytes, 12) != Constants.CALG_G28147)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_DATA);
|
||
}
|
||
|
||
var keyExchangeInfo = new Gost_28147_89_KeyExchangeInfo();
|
||
|
||
var sourceIndex = 16;
|
||
keyExchangeInfo.Ukm = new byte[8];
|
||
Array.Copy(exportedKeyBytes, sourceIndex, keyExchangeInfo.Ukm, 0, 8);
|
||
sourceIndex += 8;
|
||
|
||
keyExchangeInfo.EncryptedKey = new byte[32];
|
||
Array.Copy(exportedKeyBytes, sourceIndex, keyExchangeInfo.EncryptedKey, 0, 32);
|
||
sourceIndex += 32;
|
||
|
||
keyExchangeInfo.Mac = new byte[4];
|
||
Array.Copy(exportedKeyBytes, sourceIndex, keyExchangeInfo.Mac, 0, 4);
|
||
sourceIndex += 4;
|
||
|
||
var encryptionParamSet = new byte[exportedKeyBytes.Length - sourceIndex];
|
||
Array.Copy(exportedKeyBytes, sourceIndex, encryptionParamSet, 0, exportedKeyBytes.Length - sourceIndex);
|
||
keyExchangeInfo.EncryptionParamSet = Gost_28147_89_KeyExchangeInfo.DecodeEncryptionParamSet(encryptionParamSet);
|
||
|
||
return keyExchangeInfo;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для импорта ключей криптографического провайдера
|
||
|
||
public static int ImportCspBlob(byte[] importedKeyBytes, SafeProvHandleImpl providerHandle, SafeKeyHandleImpl publicKeyHandle, out SafeKeyHandleImpl keyExchangeHandle)
|
||
{
|
||
var dwFlags = MapCspKeyFlags(CspProviderFlags.NoFlags);
|
||
var keyExchangeRef = SafeKeyHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptImportKey(providerHandle, importedKeyBytes, (uint)importedKeyBytes.Length, publicKeyHandle, dwFlags, ref keyExchangeRef))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var keyNumberMask = BitConverter.ToInt32(importedKeyBytes, 4) & 0xE000;
|
||
var keyNumber = (keyNumberMask == 0xA000) ? Constants.AT_KEYEXCHANGE : Constants.AT_SIGNATURE;
|
||
|
||
keyExchangeHandle = keyExchangeRef;
|
||
|
||
return keyNumber;
|
||
}
|
||
|
||
[SecurityCritical]
|
||
public static byte[] EncodePublicBlob(Gost_R3410_KeyExchangeParams publicKeyParameters, int keySize, int signatureAlgId)
|
||
{
|
||
var encodedKeyParams = publicKeyParameters.EncodeParameters();
|
||
var encodedKeyBlob = new byte[16 + encodedKeyParams.Length + publicKeyParameters.PublicKey.Length];
|
||
encodedKeyBlob[0] = 6;
|
||
encodedKeyBlob[1] = 32;
|
||
Array.Copy(BitConverter.GetBytes(signatureAlgId), 0, encodedKeyBlob, 4, 4);
|
||
Array.Copy(BitConverter.GetBytes(Constants.GR3410_1_MAGIC), 0, encodedKeyBlob, 8, 4);
|
||
Array.Copy(BitConverter.GetBytes(keySize), 0, encodedKeyBlob, 12, 4);
|
||
Array.Copy(encodedKeyParams, 0, encodedKeyBlob, 16, encodedKeyParams.Length);
|
||
Array.Copy(publicKeyParameters.PublicKey, 0, encodedKeyBlob, 16 + encodedKeyParams.Length, publicKeyParameters.PublicKey.Length);
|
||
|
||
return encodedKeyBlob;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl ImportKeyExchange(SafeProvHandleImpl providerHandle, Gost_28147_89_KeyExchangeInfo keyExchangeInfo, SafeKeyHandleImpl keyExchangeHandle)
|
||
{
|
||
if (keyExchangeInfo == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(keyExchangeInfo));
|
||
}
|
||
|
||
var importedKeyBytes = EncodeSimpleBlob(keyExchangeInfo);
|
||
|
||
SafeKeyHandleImpl hKeyExchange;
|
||
ImportCspBlob(importedKeyBytes, providerHandle, keyExchangeHandle, out hKeyExchange);
|
||
|
||
return hKeyExchange;
|
||
}
|
||
|
||
public static SafeKeyHandleImpl ImportBulkSessionKey(ProviderType providerType, SafeProvHandleImpl providerHandle, byte[] bulkSessionKey, RNGCryptoServiceProvider randomNumberGenerator)
|
||
{
|
||
if (bulkSessionKey == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(bulkSessionKey));
|
||
}
|
||
|
||
if (randomNumberGenerator == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(randomNumberGenerator));
|
||
}
|
||
|
||
var hSessionKey = SafeKeyHandleImpl.InvalidHandle;
|
||
|
||
if (!CryptoApi.CryptGenKey(providerHandle, Constants.CALG_G28147, 0, ref hSessionKey))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var keyWrap = new Gost_28147_89_KeyExchangeInfo { EncryptedKey = new byte[32] };
|
||
Array.Copy(bulkSessionKey, keyWrap.EncryptedKey, 32);
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_MODE, Constants.CRYPT_MODE_ECB);
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_ALGID, Constants.CALG_G28147);
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_PADDING, Constants.ZERO_PADDING);
|
||
|
||
uint sessionKeySize = 32;
|
||
|
||
if (!CryptoApi.CryptEncrypt(hSessionKey, SafeHashHandleImpl.InvalidHandle, true, 0, keyWrap.EncryptedKey, ref sessionKeySize, sessionKeySize))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_MODE, Constants.CRYPT_MODE_CFB);
|
||
|
||
var hashHandle = CreateHashImit(providerHandle, hSessionKey);
|
||
|
||
keyWrap.Ukm = new byte[8];
|
||
randomNumberGenerator.GetBytes(keyWrap.Ukm);
|
||
|
||
if (!CryptoApi.CryptSetHashParam(hashHandle, Constants.HP_HASHSTARTVECT, keyWrap.Ukm, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
if (!CryptoApi.CryptHashData(hashHandle, bulkSessionKey, 32, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
keyWrap.Mac = EndHashData(hashHandle);
|
||
keyWrap.EncryptionParamSet = GetKeyParameterString(hSessionKey, Constants.KP_CIPHEROID);
|
||
|
||
SetKeyExchangeExportAlgId(providerType, hSessionKey, Constants.CALG_SIMPLE_EXPORT);
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_MODE, Constants.CRYPT_MODE_ECB);
|
||
SetKeyParameterInt32(hSessionKey, Constants.KP_PADDING, Constants.ZERO_PADDING);
|
||
|
||
return ImportKeyExchange(providerHandle, keyWrap, hSessionKey);
|
||
}
|
||
|
||
private static byte[] EncodeSimpleBlob(Gost_28147_89_KeyExchangeInfo keyExchangeInfo)
|
||
{
|
||
if (keyExchangeInfo == null)
|
||
{
|
||
throw ExceptionUtility.ArgumentNull(nameof(keyExchangeInfo));
|
||
}
|
||
|
||
var encryptionParamSet = Gost_28147_89_KeyExchangeInfo.EncodeEncryptionParamSet(keyExchangeInfo.EncryptionParamSet);
|
||
var importedKeyBytes = new byte[encryptionParamSet.Length + 60];
|
||
|
||
var sourceIndex = 0;
|
||
importedKeyBytes[sourceIndex] = 1;
|
||
sourceIndex++;
|
||
|
||
importedKeyBytes[sourceIndex] = 32;
|
||
sourceIndex++;
|
||
sourceIndex += 2;
|
||
|
||
Array.Copy(BitConverter.GetBytes(Constants.CALG_G28147), 0, importedKeyBytes, sourceIndex, 4);
|
||
sourceIndex += 4;
|
||
|
||
Array.Copy(BitConverter.GetBytes(Constants.G28147_MAGIC), 0, importedKeyBytes, sourceIndex, 4);
|
||
sourceIndex += 4;
|
||
|
||
Array.Copy(BitConverter.GetBytes(Constants.CALG_G28147), 0, importedKeyBytes, sourceIndex, 4);
|
||
sourceIndex += 4;
|
||
|
||
Array.Copy(keyExchangeInfo.Ukm, 0, importedKeyBytes, sourceIndex, 8);
|
||
sourceIndex += 8;
|
||
|
||
Array.Copy(keyExchangeInfo.EncryptedKey, 0, importedKeyBytes, sourceIndex, 32);
|
||
sourceIndex += 32;
|
||
|
||
Array.Copy(keyExchangeInfo.Mac, 0, importedKeyBytes, sourceIndex, 4);
|
||
sourceIndex += 4;
|
||
|
||
Array.Copy(encryptionParamSet, 0, importedKeyBytes, sourceIndex, encryptionParamSet.Length);
|
||
|
||
return importedKeyBytes;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Для работы с цифровой подписью
|
||
|
||
public static byte[] SignValue(SafeProvHandleImpl providerHandle, SafeHashHandleImpl hashHandle, int keyNumber, byte[] hashValue)
|
||
{
|
||
SetHashValue(hashHandle, hashValue);
|
||
|
||
uint signatureLength = 0;
|
||
|
||
// Вычисление размера подписи
|
||
if (!CryptoApi.CryptSignHash(hashHandle, (uint)keyNumber, null, 0, null, ref signatureLength))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
var signatureValue = new byte[signatureLength];
|
||
|
||
// Вычисление значения подписи
|
||
if (!CryptoApi.CryptSignHash(hashHandle, (uint)keyNumber, null, 0, signatureValue, ref signatureLength))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
return signatureValue;
|
||
}
|
||
|
||
public static bool VerifySign(SafeProvHandleImpl providerHandle, SafeHashHandleImpl hashHandle, SafeKeyHandleImpl keyHandle, byte[] hashValue, byte[] signatureValue)
|
||
{
|
||
SetHashValue(hashHandle, hashValue);
|
||
|
||
return CryptoApi.CryptVerifySignature(hashHandle, signatureValue, (uint)signatureValue.Length, keyHandle, null, 0);
|
||
}
|
||
|
||
private static void SetHashValue(SafeHashHandleImpl hashHandle, byte[] hashValue)
|
||
{
|
||
uint hashLength = 0;
|
||
|
||
if (!CryptoApi.CryptGetHashParam(hashHandle, Constants.HP_HASHVAL, null, ref hashLength, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
|
||
if (hashValue.Length != hashLength)
|
||
{
|
||
throw ExceptionUtility.CryptographicException(Constants.NTE_BAD_HASH);
|
||
}
|
||
|
||
if (!CryptoApi.CryptSetHashParam(hashHandle, Constants.HP_HASHVAL, hashValue, 0))
|
||
{
|
||
throw CreateWin32Error();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
public static T DangerousAddRef<T>(this T handle) where T : SafeHandle
|
||
{
|
||
var success = false;
|
||
handle.DangerousAddRef(ref success);
|
||
|
||
return handle;
|
||
}
|
||
|
||
public static void TryDispose(this SafeHandle handle)
|
||
{
|
||
if ((handle != null) && !handle.IsClosed)
|
||
{
|
||
handle.Dispose();
|
||
}
|
||
}
|
||
|
||
private static CryptographicException CreateWin32Error()
|
||
{
|
||
return ExceptionUtility.CryptographicException(Marshal.GetLastWin32Error());
|
||
}
|
||
}
|
||
}
|