КриптоПро CSP  

CPCImportKey

Функция CPCImportKey() используется для импорта криптографического ключа из ключевого блоба в контейнер криптопровайдера, для диверсификации ключа с использованием данных, передаваемых через ключевой блоб в соответствии с RFC 4357, п. 7., а так же для диверсификации ключа в соответствии с алгоритмами, описанным в п. 4.4, 4.5 рекомендаций по стандартизации "Криптографические алгоритмы, сопутствующие применению алгоритмов электронной цифровой подписи и функции хэширования".

DWORD CPCAPI CPCImportKey(
  HCRYPTMODULE hCSP,
  HCRYPTPROV hProv,
  CONST BYTE * pbData,
  DWORD dwDataLen,
  HCRYPTKEY hImpKey,
  DWORD dwFlags,
  HCRYPTKEY * phKey
);

Аргументы

hCSP
[in] Указатель на таблицу функций криптопровайдера. Получается при помощи функции CPCCreateProvider()
hProv
[in] Дескриптор криптопровайдера. Получается при помощи функции CPCAcquireContext().
pbData
[in] Указатель на буфер, содержащий ключевой блоб, произведенный с иcпользованием функции CPExportKey() данным или другим криптопровайдером, функционирующим на удалённом компьютере, или блоб диверсификации, используемый для диверсификации ключа на основе данных, передаваемых через блоб в соответствии с RFC 4357, п. 7. или в соответствии с п. 4.4, 4.5 рекомендаций по стандартизации "Криптографические алгоритмы, сопутствующие применению алгоритмов электронной цифровой подписи и функции хэширования"
dwDataLen
[in] Длина ключевого блоба в байтах.
hImpKey
[in] Дескриптор ключа, на котором осуществляется снятие криптографической защиты импортируемого ключа. Значение этого параметра должно соответствовать значению hExpKey, определённому для функции CPCExportKey() при создании ключевого блоба. После зашифрования ключевого блоба на сессионном ключе этот параметр содержит дескриптор на сессионный ключ. Если ключевой блоб не зашифрован (например, PUBLICKEYBLOB), этот параметр не используется и должен быть равен нулю. В алгоритме диверсификации hImpKey - исходный ключ, из которого вырабатываются диверсифицированные ключи.
dwFlags
[in] Значение флага. Этот параметр в настоящее время используется, когда предполагается выработка ключа согласования по алгоритму Диффи-Хеллмана, либо при импорте ключевой пары в формате PRIVATEKEYBLOB. В первом случае должен быть выставлен флаг CP_FORCE_GOST_DH, иначе в качестве алгоритма согласования ключа будет выбран VKO. Во втором случае, если импортируемый ключ будет заново экспортироваться, в этот параметр помещается флаг CRYPT_EXPORTABLE. Если этот флаг не выставлен, последующие вызовы функции CryptExportKey в MS CryptoAPI 2.0 World Wide Web link с дескриптором ключа будут терпеть неудачу. Также этот параметр используется при импорте открытого ключа из структуры PUBLICKEYBLOB, не содержащей информации о параметрах импортируемого ключа. В этом случае выставляется флаг CP_PRIMITIVE_PUBLICKEYBLOB. При использовании данного флага параметр hImpKey должен быть задан, т.е. использование данного флага возможно только в рамках операций Диффи-Хеллмана.
phKey
[out] Адрес, по которому функция копирует дескриптор импортированного либо диверсифицированного ключа. В алгоритме диверсификации ключ phKey наследует параметры ключа hImpKey: KP_OID, KP_MODE, KP_MIXMODE, KP_PADDING. При импорте ключей CALG_GR3410EL, CALG_DH_EL_SF, CALG_DH_GR3410_12_256_SF, CALG_GR3410_12_256, CALG_DH_GR3410_12_512_SF, CALG_GR3410_12_512, CALG_UECSYMMETRIC, если ключевой контейнер не содержит соответствующих ключей AT_SIGNATURE, AT_KEYEXCHANGE, AT_UECSYMMETRICKEY, импортируемые ключи устанавливаются в ключевой контейнер

Возвращаемые значения

При успешном завершении функция возвращает 0 (S_OK), в противном случае возвращается соответствующий код ошибки (см. таблицу).
Коды возвратаОписание
ERROR_INVALID_PARAMETERОдин из параметров содержит некорректное значение. Чаще всего это некорректный указатель.
NTE_BAD_DATAНе прошёл контроль целостности импортируемого ключевого блоба. Ошибочная длина блоба.
NTE_BAD_FLAGSПараметр dwFlags содержит ошибочную величину.
NTE_BAD_KEYОдин или оба из ключей, указанных hKey и hImpKey, не действительны. Байты в элементах заголовка ключевого блоба типа WORD, DWORD имеют обратный порядок по отношению к данной платформе.
NTE_BAD_TYPEТип ключевого блоба не поддерживается этим криптопровайдером и, возможно, ошибочен.
NTE_PERMПопытка импорта ключа, когда право импорта криптопровайдером не предоставлено.
NTE_NO_MEMORYКриптопровайдер во время операции исчерпал память.
NTE_FAILНарушение целостности ключей в ОЗУ. см. Дополнительные параметры и определения .
SCARD_W_CANCELLED_BY_USERПользователь прервал операцию нажатием клавиши Cancel
SCARD_W_WRONG_CHVПользователь ввёл неправильный пароль или пароль, установленный функцией SetProvParam(), неправильный
SCARD_E_INVALID_CHVПользователь ввёл пароль с нарушением формата или пароль, установленный функцией SetProvParam(), имеет неправильный формат. Например, пароль имеет недопустимую длину или содержит недопустимые символы.
SCARD_W_CHV_BLOCKEDВвод Pin-кода был заблокирован смарт-картой, т.к. исчерпалось количество попыток, разрешенное картой для ввода.
ERROR_BAD_USERNAMEВведенный login доступа к ключевому сервису неверен.
ERROR_INVALID_PASSWORDВведенный пароль доступа к ключевому сервису неверен.
NTE_TOKEN_KEYSET_STORAGE_FULLНедостаточно места на носителе для сохранения информации.
NTE_SILENT_CONTEXTОперация не может быть выполнена без пользовательского интерфейса.
SCARD_W_REMOVED_CARDНоситель контейнера был удален из считывателя
NTE_EXISTSПопытка импортировать ключ в случае, когда ключ соответствующего типа в контейнере существует.
NTE_NOT_SUPPORTEDДанный носитель не поддерживает импорт ключа.
ERROR_FUNCTION_FAILEDнет лицензии, дающей право на выполнение функции импорта ключа
SCARD_E_NO_KEY_CONTAINERПопытка выполнения операции в контексте смарт-карты или токена, а не контейнера.
SCARD_W_SECURITY_VIOLATIONФукции безопасности токена или смарт-карты работают некорректно.
ERROR_PASSWORD_EXPIREDПароль данного носителя истек, необходимо его сменить.
SCARD_E_READ_ONLY_CARDСмарт-карта недоступна для использования из-за ограничений безопасности.

Примечания

Обычно для согласования (экспорта/импорта) сессионного ключа применяют алгоритм Диффи-Хеллмана. В этом случае ключ парной связи (ключ экспорта/импорта сессионного ключа) порождается операцией импорта открытого ключа получателя (отправителя) на ключевой паре отправителя (получателя). Т.е. для импорта сессионного ключа следует выполнить следующие шаги:

Для ключей 2012 в качестве алгоритма согласования сессионного ключа используется алгоритм VKO GOST R 34.10-2012-256 (на основе функции хэширования ГОСТ Р 34.11-2012-256), параметр KP_CIPHEROID результирующего ключа принимает значение, установленное в параметрах импортируемого открытого ключа (если не указано, то "1.2.643.7.1.2.5.1.1", вариант ТК26 Z). Режимы шифрования, преобразования ключа и дополнения открытого текста до кратности блока для получаемого ключа устанавливаются аналогично вновь создаваемому ключу (см. CPCSetKeyParam()).

Для ключей 2001 используется алгоритм VKO GOST R 34.10-2001 (на основе функции хэширования ГОСТ Р 34.11-94). При использовании данного алгоритма с долговременным ключом параметр KP_CIPHEROID полученного ключа принимает oid параметров шифрования, установленный в соответствующем долговременному ключу контейнере; при использовании с эфемерным ключом параметр KP_CIPHEROID результирующего ключа принимает установленное для провайдера значение PP_CIPHEROID. Режимы шифрования, преобразования ключа и дополнения открытого текста до кратности блока для получаемого ключа устанавливаются аналогично вновь создаваемому ключу (см. CPCSetKeyParam()).

Однако, в ядре ОС отсутствует поддержка долговременных ключей. Поэтому ключ сессии (на файл или ассоциацию) или ключевая пара для ЭП обычно передаются из поддерживающего сервиса (демона). Для этого сначала вырабатывается совместный ключ (hDrvKey):

	    
      ...
      // Инициализация работы с драйвером
      CPCGetProvParam(hCSP, hProv, PP_RANDOM, pbRandomBlobToDriver, cbRandomBlobToDriver);
      ...
      // Передать инициализатор ДСЧ (pbRandomBlobToDriver) в поддерживающий сервис и 
      // получить от него случайную последовательность (pbRandomFromDriver).
      ...
      CPCCreateHash(hCSP, hProv, CALG_GR3411, 0, 0, &hDrvHash);
      CPCHashData(hCSP, hProv, hDrvHash, "DriverDaemon", sizeof("DriverDaemon"), 0);
      CPCHashData(hCSP, hProv, hDrvHash, pbRandomBlobToDriver, cbRandomBlobToDriver, 0);
      CPCHashData(hCSP, hProv, hDrvHash, pbRandomFromDriver, cbRandomFromDriver, 0);
      CPCDeriveKey(hCSP, hProv, hDrvHash, &hDrvKey);
      CPCDestroyHash(hCSP, hProv, hDrvHash);
      ...
	

На этом ключе сервис может экспортировать ключевую пару для подписи:

	    
      ...
      // Получение дескриптора ключевой пары:
      CPCGetUserKey(hCSP, hProv, AT_SIGNATURE, &hUserKey);

      // Подготовка ключевой пары для передачи в драйвер:
      CPCExportKey(hCSP, hProv, hUserKey, hDrvKey, PRIVATEKEYBLOB, 0, pbPrivateKeyForDriver, &cbPrivateKeyForDriver);
      ...
      // Передать pbPrivateKeyForDriver в драйвер
      ...
	

Аналогичным образом можно экспортировать ключ сессии:

	    
      ...
      // Импорт ключа сессии (файла, ассоциации) от отправителя:
      CPCImportKey(hCSP, hProv, pbSenderPublicKey, cbSenderPublicKey, hUserKey, 0, &hExchKey);
      ALG_ID ke_alg = CALG_PRO_EXPORT;
      CPCSetKeyParam(hCSP, hProv, hExchKey, KP_ALGID, (LPBYTE)&ke_alg, 0);
      CPCImportKey(hCSP, hProv, pbSessionKeyFromSender, &cbSessionKeyFromSender, hExchKey, 0, &hSessionKey);

      // Подготовка его для передачи в драйвер
      CPCExportKey(hCSP, hProv, hSessionKey, hDrvKey, SIMPLEBLOB, 0, pbSessionKeyForDriver, &cbSessionKeyForDriver);
      ...
      // Передать pbSessionKeyForDriver в драйвер
      ...
	

На основе ключа сессии можно получить ключи блоков (пакетов) с помощью CPCGenKey(), CPCDeriveKey() или алгоритма диверсификации ключа в функции CPCImportKey() (CRYPT_DIVERSBLOB ).

	    
      ...
      // Инициализация работы с демоном (сервисом)
      ...
      // Получить инициализатор ДСЧ (pbRandomBlobFromDaemon) в драйвер.
      ...
      CPCSetProvParam(hCSP, hProv, PP_RANDOM, pbRandomBlobFromDaemon);
      CPCGenRandom(hCSP, hProv, cbRandomToDaemon, pbRandomToDaemon);
      CPCCreateHash(hCSP, hProv, CALG_GR3411, 0, 0, &hDmnHash);
      CPCHashData(hCSP, hProv, hDmnHash, "DriverDaemon", sizeof("DriverDaemon"), 0);
      CPCHashData(hCSP, hProv, hDmnHash, pbRandomBlobFromDaemon, cbRandomBlobFromDaemon, 0);
      CPCHashData(hCSP, hProv, hDmnHash, pbRandomToDaemon, cbRandomToDaemon, 0);
      CPCDeriveKey(hCSP, hProv, hDmnHash, &hDmnKey);
      CPCDestroyHash(hCSP, hProv, hDmnHash);
      ...
      // Передать случайную последовательность демону (сервису).
      ...
      // Получить pbSessionKeyFromDaemon из демона (сервиса)
      ...
      CPCImportKey(hCSP, hProv, pbSessionKeyFromDaemon, &cbSessionKeyFromDaemon, hDmnKey, 0, &hSessionKey);
      ...
      // Вариант получения ключа блока (пакета) на основе ключа сесcии (файла, ассоциации) 
      // с помощью CPCGenKey.
      CPCImportKey(hCSP, hProv, pbBlkBlob, cbBlkBlob, hSessionKey, 0, &hBlkKey);
      ...
      // Вариант получения ключа блока (пакета) на основе ключа сесcии (файла, ассоциации) 
      // с помощью CPCDeriveKey.
      // bBlockNumber - указатель на байтовую последовательность, диверсифицирующая ключ.
      CPCCreateHash(hCSP, hProv, CALG_GR3411, 0, 0, &hHash);
      CPCHashData(hCSP, hProv, hHash, "DeriveBlkKey", sizeof("DeriveBlkKey"), 0);
      CPCHashSessionKey(hCSP, hProv, hHash, hSessionKey, 0);
      CPCHashData(hCSP, hProv, hHash, bBlockNumber, sizeof(dwBlockNumber), 0);
      CPCDeriveKey(hCSP, hProv, CALG_G28147, hHash, 0, &hBlkKey);
      CPCDestroyHash(hCSP, hProv, hHash);
      ...
      // Пример получения ключа блока (пакета) на основе ключа сесcии (файла, ассоциации)
      // с помощью возможности диверсификации ключа функцией CPCImportKey в соответствии с
      // алгоритмом, описанным в п.7 RFC 4357.
      // bBlockNumber - байтовая последовательность, диверсифицирующая ключ.
      Blob.DiversBlobHeader.BlobHeader.bType = DIVERSKEYBLOB;
      Blob.DiversBlobHeader.BlobHeader.bVersion = 0x20;
      Blob.DiversBlobHeader.BlobHeader.reserverd = 0;
      Blob.DiversBlobHeader.BlobHeader.aiKeyAlg = CALG_G28147;
      Blob.DiversBlobHeader.aiDiversAlgId = CALG_PRO_DIVERS;
      Blob.DiversBlobHeader.dwDiversMagic = 0x31564944;
      Blob.DiversBlobHeader.cbDiversData = sizeof(bBlockNumber);
      CopyMemory((LPBYTE)Blob.bDiversData,bBlockNumber,sizeof(bBlockNumber));
      CPCImportKey(hCSP, hProv, &Blob, sizeof(Blob), hSessionKey, 0, &hBlkKey);
      // Диверсификация в соответствии с алгоритмом KDF_GOSTR3411_2012_256
      // проводится аналогично с тем отличием, что в поле aiDiversAlgId
      // записывается константа CALG_PRO12_DIVERS
	

С помощью этой же функции в режиме диверсификации ключа (CRYPT_KDF_TREE_DIVERSBLOB ) можно создавать ключи блоков (пакетов) на основе процедуры диверсификации КриптоПро CSP, предусматривающей возможность выработки нескольких ключей.

	    
      ...
      // Пример выработки нескольких ключей на основе ключа диверсификации
      // с помощью алгоритма CALG_KDF_TREE_GOSTR3411_2012_256, описанного
      // в п.4.5 рекомендаций по стандартизации "Криптографические алгоритмы,
      // сопутствующие применению алгоритмов электронной цифровой подписи и функции хэширования"
      // bSeedData - байтовая последовательность Seed, диверсифицирующая ключ.
      // bLabelData - байтовая последовательность Label, диверсифицирующая ключ.
      dwBlobLen = sizeof(CRYPT_KDF_TREE_DIVERSBLOB_HEADER) + sizeof(CRYPT_KDF_TREE_DIVERS_INFO) +
          sizeof(bSeedData) + sizeof(bLabelData);
      DiversBlob.KdfTreeBlobHeader.BlobHeader.aiKeyAlg = CALG_G28147;
      DiversBlob.KdfTreeBlobHeader.BlobHeader.bType = KDF_TREE_DIVERSBLOB;
      DiversBlob.KdfTreeBlobHeader.BlobHeader.bVersion = BLOB_VERSION;
      DiversBlob.KdfTreeBlobHeader.BlobHeader.reserved = 0;
      DiversBlob.KdfTreeBlobHeader.aiDiversAlgId = CALG_KDF_TREE_GOSTR3411_2012_256;
      DiversBlob.KdfTreeBlobHeader.dwIterNum = 1;
      DiversBlob.KdfTreeDiversInfo.dwLabelLen = sizeof(bLabelData);
      DiversBlob.KdfTreeDiversInfo.dwSeedLen = sizeof(bSeedData);
      DiversBlob.KdfTreeDiversInfo.L_value = 1024;
      DiversBlob.KdfTreeDiversInfo.R_value = 2;
      CopyMemory((LPBYTE)DiversBlob.bDiversData, bSeedData, sizeof(bSeedData));
      CopyMemory((LPBYTE)DiversBlob.bDiversData + sizeof(bSeedData), bLabelData, sizeof(bLabelData));
      // Первичный вызов с передачей всех параметров диверсификации
      CPCImportKey(hCSP, hProv, &DiversBlob, dwBlobLen, hSessionKey, 0, &hDivKey[0]);
      // Для последующих вызовов достаточно передачи хэдера с нужным номером ключа
      // Вызовы с различными номерами результирующих ключей могут идти не по порядку
      DiversBlob.KdfTreeBlobHeader.dwIterNum = 3;
      CPCImportKey(hCSP, hProv, &DiversBlob.KdfTreeBlobHeader,
          sizeof(CRYPT_KDF_TREE_DIVERSBLOB_HEADER), hSessionKey, 0, &hDivKey[1]);
      ...
	

В качестве алгоритма согласования сессионного ключа может быть использован алгоритм Диффи-Хеллмана. Для этого при импорте открытого ключа получателя (отправителя) на ключевой паре отправителя (получателя) требуется указать флаг CP_FORCE_GOST_DH.

В случае импорта SIMPLEBLOB, PUBLICKEYBLOB или DIVERSKEYBLOB (при использовании алгоритмов диверсификации CALG_PRO_DIVERS и CALG_PRO12_DIVERS) разрешается многопоточная работа функции. Для этого в соответствующем вызове импорта необходимо передавать флаг CP_CRYPT_NOKEYWLOCK, при этом ключ hImpKey блокируется только на чтение. Корректность работы функции и целостность ключа импорта гарантируются криптопровайдером.

В случае импорта SIMPLEBLOB набор узлов замены устанавливается в соответствии с указанным в блобе. Режимы шифрования, преобразования ключа и дополнения открытого текста до кратности блока для получаемого ключа устанавливаются аналогично вновь создаваемому ключу (см. CPCSetKeyParam()).

В случае импорта OPAQUEKEYBLOB набор узлов замены, режимы шифрования, преобразования ключа и дополнения открытого текста до кратности блока устанавливаются в соответствии с параметрами, указанными в блобе.

Полученный дескриптор ключа должен в обязательном порядке быть удалён с помощью вызова функции CPCDestroyKey()  до вызова функции CPCReleaseContext() для рабочего дескриптора криптопровайдера.

Пример согласования общего ключа с использованием флага CP_PRIMITIVE_PUBLICKEYBLOB:

	    
      ...
      CryptExportKey(hEphemKey, 0, PUBLICKEYBLOB, CP_PRIMITIVE_PUBLICKEYBLOB, pbBlob, &dwBlobLen);
      CryptImportKey(hProv, pbBlob, dwBlobLen, hSecretKey, CP_PRIMITIVE_PUBLICKEYBLOB, &hCommonKey);
      ...
	

Требования:

Ядро AIX: 6/7.
Ядро FreeBSD: 11/12.
Ядро Linux: 2.6.x и выше.
Ядро Solaris: 10/11.
Ядро Windows: 7/8/8.1/10, Server 2008/2008R2/2012/2012R2/2016/2019.

См. также

CRYPT_PRIVATEKEYBLOB ,CRYPT_PUBLICKEYBLOB ,CRYPT_SIMPLEBLOB ,CRYPT_OPAQUEBLOB ,CRYPT_DIVERSBLOB ,CPCExportKey() ,CPCGenKey() ,CPImportKey в MS CSP World Wide Web link ,CryptImportKey в MS CryptoAPI 2.0 World Wide Web link