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
{
///
/// Вспомогательные методы для работы с Microsoft CryptoAPI
///
[SecurityCritical]
public static class CryptoApiHelper
{
///
/// Возвращает , если заданный провайдер установлен
///
[SecurityCritical]
public static bool IsInstalled(ProviderType providerType)
{
try
{
var providerHandle = GetProviderHandle(providerType);
return !providerHandle.IsInvalid;
}
catch
{
return false;
}
}
///
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2001
///
/// Провайдер не установлен
[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);
}
///
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2012/512
///
/// Провайдер не установлен
[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);
}
///
/// Возвращает доступный провайдер для ключей ГОСТ Р 34.10-2012/1024
///
/// Провайдер не установлен
[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 _providerHandles = new Dictionary();
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 _randomNumberGenerators = new Dictionary();
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;
}
///
/// Возвращает список имен контейнеров указанного криптопровайдера
///
/// Тип криптопровайдера
/// Вернуть полное имя контейнера
/// Область поиска контейнеров
public static IEnumerable GetContainers(ProviderType providerType, bool fullContainerName = true, StoreLocation storeLocation = StoreLocation.LocalMachine)
{
var containers = new List();
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;
}
///
/// Возвращает сертификат X.509 для указанного ключа
///
/// Дескриптор ключа сертификата
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(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(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(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());
}
}
}