CNG Key 相关操作

Posted by Useless Programmer on March 9, 2019

    CNG 关于 Key 相关的操作

章节目录

  1. 简介
  2. 创建 Key
  3. 查看系统中的 Key
  4. 从 Windows Store 导出 key
  5. 导入 Key 到 Windows Store

作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢!

简介##

CNG 全称 Cryptography API: Next Generation (CNG). 是 windows 平台下一代加密相关 API 的总称, 它是 Crypto API 的替代. 比较与 CryptoAPI, 它的功能更加强大.

本文将以实例的形式给出使用 CNG 操作 Key 的方法以及相关的 API.

请注意, 文章中我们使用 RSA key 作为示例, DSA 和 ECC key 也是同样适用的.

创建 Key##

CNG 当前版本支持了 RSA, DSA, ECC 算法, 这里我们来创建一个 key 长度 为 2048 bits 的 RSA key. 注意, 当你尝试创建 2048 的 DSA key 时, 请确保你的系统是否支持, 本人的 Win7 是不支持的, 而 Win10 是支持的(亲测).

这里我们依次使用了以下API:
NCryptOpenStorageProvider 该方法用于加载和初始化一个已经存在 CNG Key Storage Provider. 当前 Windows 系统中内置了两个 KSP:MS_KEY_STORAGE_PROVIDER,MS_SMART_CARD_KEY_STORAGE_PROVIDER. 我们例子中使用 MS_KEY_STORAGE_PROVIDER.
NCryptCreatePersistedKey 该方法用于常见一个特定算法的 Key, 并把它存在指定的 KSP 中. 注意, 这个方法创建的 key 会以文件形式存储在 Windows 系统中.
NCryptSetProperty 该方法用于设置 key 的属性. 例子中我们将使用它设置 key 的长度为 2048 bits.
NCryptFinalizeKey 该方法用于通知 Windows 我们创建 key 的操作已经完成, 调用该方法后, 我们创建的 Key 将可以被用于签名加密等操作, 否则不能.
NCryptFreeObject 该方法用于释放前面操作中我们所使用到系统 Handle.

int Create2048RSAKey() {
  int errCode = 0;

  NCRYPT_PROV_HANDLE prov = NULL;
  NCRYPT_KEY_HANDLE  key = NULL;
  DWORD keyLength = 2048;
  DWORD policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

  if (errCode = NCryptOpenStorageProvider(&prov, MS_KEY_STORAGE_PROVIDER, 0)) goto done;
  if (errCode = NCryptCreatePersistedKey(prov, &key, NCRYPT_RSA_ALGORITHM, L"TestRSAKey", 0, NCRYPT_OVERWRITE_KEY_FLAG)) goto done;
  if (errCode = NCryptSetProperty(key, NCRYPT_LENGTH_PROPERTY, (PBYTE)(&keyLength), sizeof(keyLength), NCRYPT_PERSIST_FLAG)) goto done;
  if (errCode = NCryptSetProperty(key, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)(&policy), sizeof(policy), NCRYPT_PERSIST_FLAG)) goto done;
  if (errCode = NCryptFinalizeKey(key, 0)) goto done;

done:
  if (prov) NCryptFreeObject(prov);
  if (key) NCryptFreeObject(key);
  return errCode;
}

当你在你的 Windows 电脑上执行了该方法之后, 你的系统中就会存有我们刚刚创建的 Key 了.
下一节, 我们将使用 CNG API 来查看我们创建的 Key 的信息.

查看系统中的 Key##

这里我们将会使用到的 CNG API 有:
NCryptEnumKeys 该方法用于遍历保存在当前 KSP 中的所有 Key.
这里我们解释一下它的几个参数:
NCryptKeyName **ppKeyName 该参数作为方法的返回值, 其中保存了当前 Key 的名称, key 算法等重要信息. name 尤为重要, 比如我们例子中会使用 name 来获取该 key 的 handle, 依次来使用该 key 做更多的操作.
PVOID *ppEnumState 该参数也作为方法的返回值. 但是它的值对于调用者来说是没有任何意义的. 它存储了遍历 key 的中间信息, 是我们下一次调用 NCryptEnumKeys 方法的入参. 如果将该参数设置为 NULL, 遍历总是会返回当前 KSP 的第一个 Key 的信息.
return value 当该方法返回值为 NTE_NO_MORE_ITEMS, 代表已经遍历完了当前 KSP 中所有的 Key.
NCryptOpenKey 该方法用于获取执行 KSP 中指定名称的 Key Handle.
NCryptGetProperty 该方法用于获取 Key 的属性.

int ListKeys() {
  int errCode = 0;
  NCRYPT_PROV_HANDLE prov        = NULL;
  NCRYPT_KEY_HANDLE  key         = NULL;
  NCryptKeyName*     pKeyName    = NULL;
  PVOID              pEnumState = NULL;
  
  if (errCode = NCryptOpenStorageProvider(&prov, MS_KEY_STORAGE_PROVIDER, 0)) goto done;
  do {
    errCode = NCryptEnumKeys(prov, 0, &pKeyName, &pEnumState, 0);
    if (errCode == NTE_NO_MORE_ITEMS) break; // all the keys are enumerated
    if (!pKeyName) break; // should not happen
    
    LPCWSTR name = pKeyName->pszName;
    LPCWSTR alg  = pKeyName->pszAlgid;
    
    // get key length property
    DWORD keyLength = 0,  outLen = 0;
    if (errCode = NCryptOpenKey(prov, &key, name, 0, 0)) goto done;
    if (errCode = NCryptGetProperty(key, NCRYPT_LENGTH_PROPERTY, (PBYTE) (&keyLength), sizeof(keyLength), &outLen, 0)) goto done;

    wprintf(L"KeyName: %s, KeyAlgorithm: %s, KeyLength: %d", name, alg, keyLength);
  } while (!errCode);
  
done:
  if (prov) NCryptFreeObject(prov);
  if (key) NCryptFreeObject(key);
  return errCode;
}

如果你在你的电脑上运行了第一小节的函数, 此时执行 ListKeys 函数, 你的命令行中肯定会包含这样的输出:

KeyName: TestRSAKey, KeyAlgorithm: RSA, KeyLength: 2048

从 Windows Store 导出 key##

通过上述两个小节的描述, 我们已经成功的在 Windows 系统中创建了 Key, 并且可以查看 Key 的信息, 获取 Key handle. 使用 Key Handle 我们可以完成很多的工作.但是, 很多时候我们需要把 Key 到处到文件, 和同伴分享.

这一小节, 我们就来尝试把 Key 导出来.

NCryptExportKey 该方法用于将系统中的 Key 导出到 memory blob 中.
它有一个非常重要的参数:
LPCWSTR pszBlobType 用来控制导出的 memory blob 的格式. 例子中我们使用 NCRYPT_PKCS8_PRIVATE_KEY_BLOB. 相信我,这个格式将会是最方便我们做进一步处理的格式. 其他的格式, 比如 BCRYPT_DSA_PUBLIC_BLOB, LEGACY_DSA_PRIVATE_BLOB 等格式看起来很友善, 但是并不总是像你想象的那样工作. 他们的格式并不总是和 CryptExportKey 导出时的一致,当 Key 的长度变为 2048 或者更大时, 而且很难找到相应的文档来描述此时它的格式到底是什么样的. 而 PKCS8 工作的非常好.

int ExportKey() {
  int errCode = 0;

  NCRYPT_PROV_HANDLE prov = NULL;
  NCRYPT_KEY_HANDLE  key = NULL;
  BYTE blob[0x1000] = {0};
  DWORD blobLen = 0x1000;

  if (errCode = NCryptOpenStorageProvider(&prov, MS_KEY_STORAGE_PROVIDER, 0)) goto done;
  if (errCode = NCryptOpenKey(prov, &key, L"TestRSAKey", 0, 0)) goto done;
  if (errCode = NCryptExportKey(key, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, NULL, blob, blobLen, &blobLen, 0)) goto done;

done:
  if (prov) NCryptFreeObject(prov);
  if (key) NCryptFreeObject(key);
  return errCode;
}

当你执行该函数与某个 Key Handle 时, 如果你得到错误码 0x80090029, 请确认你的 Key 的 NCRYPT_EXPORT_POLICY_PROPERTY 属性的值包含 NCRYPT_ALLOW_EXPORT_FLAG 和 NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG.(这就是为什么, 第一节中的代码中, 我们明确的将创建的 Key 属性设置为该值的原因了.)

如果你的 Key 不包含 NCRYPT_ALLOW_EXPORT_FLAG 和 NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, 那意味着你的 Key 无法导出, 你只能使用该 Key 的 Handle 做其他的工作.

如果你的 key 的属性仅仅包含 NCRYPT_ALLOW_EXPORT_FLAG, 那意味着你不能将你的证书导出为明文. 此时,你需要将 NCryptExportKey 的第二个参数设置为一个非 NULL 的值. 个人觉得很麻烦, 如果您有相应的 code, 谢谢你分享给我!

一点儿个人经验, 如果你的 Key 是用户通过 CryptAPI 或者手工方式导入的 PFX, 在 Win10 平台上, 你的 Key 很可能只有 NCRYPT_ALLOW_EXPORT_FLAG 标记, 而不支持导出为明文. 如果我错了, 请告诉我!

导入 Key 到 Windows Store##

很多时候, 我们不总是直接使用系统创建 Key, 而是使用我们现有的 Key. 那么, 当你想让你的 Key 和 CNG API 一起工作的时候, 导入你的 Key 到系统中将是一个必须的工作. 注意, 我们这里给出的例子会将 Key 保存到系统中. 如果你仅仅想要获取 Key Handle 而不将 Key 导入到 KSP 中, 只需要不设置 Key name 就行了.

这里我们将会将 PKCS8 格式的 Key 导入到系统中, 如果你的 Key 是另外的格式, 你有两个选择: 将他转换成 PKCS8 格式然后使用下面方法导入 或者 寻找其他可用的方法.

这里有两点值得一提:
NCryptBufferDesc *pParameterList 该参数可以用来设置 Key 的参数信息. 这里我们使用该参数来指定将要导入到系统中的 Key 的名称. 如果专门设置 Key 名称, 该 Key 将会是一个临时的 Key, 就不会被存储在 Window 中.
DWORD dwFlags 如果需要在导入 Key 之后设置 Key 的属性, 那你需要在这个参数中包含 NCRYPT_DO_NOT_FINALIZE_FLAG 标记. 例子中我们在导入 Key 之后, 需要设置 Key 的 NCRYPT_EXPORT_POLICY_PROPERTY 属性, 因此我们使用了 NCRYPT_DO_NOT_FINALIZE_FLAG 标记. 如果你未使用 NCRYPT_DO_NOT_FINALIZE_FLAG, 就不要额外的调用 NCryptFinalizeKey 方法, 否则, 请记得调用 NCryptFinalizeKey 方法.

int ImportKey() {
  BYTE blob[] = { 0x30,0x82,0x04,0xbd,0x02,0x01,0x00,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x04,0x82,0x04,0xa7,0x30,0x82,0x04,0xa3,0x02,0x01,
0x00,0x02,0x82,0x01,0x01,0x00,0xad,0xaa,0x8c,0x75,0x51,0x15,0x23,0x55,0xc7,0xec,0x7f,0x0b,0x85,0xf9,0x4b,0x18,0x10,0x60,0xdb,0x3d,0xc2,0xd2,0xcf,0xa6,0x8d,0x09,
0x8a,0xc9,0x7a,0x0e,0x80,0xb8,0x60,0x5a,0x20,0xe7,0xcb,0x52,0x33,0xc0,0xa3,0xc8,0x33,0x05,0xb0,0x45,0x34,0x49,0x59,0x0d,0xff,0xed,0xe5,0xe9,0x42,0xc4,0x05,0x82,
0x1a,0x92,0x67,0x24,0xea,0xc6,0x2a,0x07,0x67,0x07,0x65,0xa9,0xb8,0xe8,0x25,0xa2,0x16,0xe5,0x47,0xf2,0x79,0x98,0xcd,0x7c,0xce,0xbc,0x28,0x95,0x5f,0x9a,0xcd,0x70,
0x30,0xbb,0x7c,0xc9,0xc1,0x55,0xac,0x06,0x42,0xa8,0x86,0x78,0x47,0x69,0xa0,0xfb,0x5e,0x10,0x01,0xe4,0x3a,0xbc,0x09,0xb3,0xfd,0x3f,0x3d,0x3c,0x47,0xe7,0xd2,0x8f,
0xaf,0x2f,0x04,0x38,0x74,0xdc,0x3b,0x74,0x31,0xfb,0x55,0x39,0x94,0xeb,0xe3,0x7c,0x17,0x8d,0x51,0x50,0xa2,0x25,0x87,0x4b,0xfb,0xd2,0x3c,0xb6,0x70,0x45,0xd1,0x55,
0xb0,0x8e,0x9d,0x24,0xcb,0xd7,0xa0,0xc2,0xdd,0xfa,0x7b,0xa4,0x3f,0xd3,0xbe,0x7e,0x09,0x87,0x46,0x37,0xda,0x29,0x7c,0x95,0x85,0x0d,0x6d,0xfc,0xbf,0xbf,0x41,0xbe,
0x79,0x2c,0x83,0x87,0x1b,0x6f,0x24,0x8a,0x60,0x85,0x36,0xf6,0xcb,0x63,0x0c,0x66,0x16,0x0b,0x97,0x4a,0xdc,0xdb,0x91,0x0d,0xf9,0x1d,0x7e,0xae,0xbc,0xd9,0x77,0x39,
0x38,0x3e,0x7b,0x04,0x92,0x71,0x94,0x18,0x66,0xc8,0x77,0x5e,0x5c,0x42,0x16,0xc7,0x18,0x61,0x5a,0xdd,0x4f,0xaf,0x77,0x9a,0x14,0xba,0x39,0x98,0x22,0x73,0x49,0xfa,
0x73,0x2b,0x6d,0x0c,0x3b,0x27,0x02,0x03,0x01,0x00,0x01,0x02,0x82,0x01,0x00,0x2b,0xd2,0xef,0xe2,0xe2,0xf5,0x2c,0x97,0x86,0xfd,0xdd,0x09,0x71,0x63,0x79,0x59,0xb7,
0x38,0x59,0xda,0xfa,0x00,0xec,0xb9,0xa0,0xb9,0x99,0xb6,0x42,0x00,0xca,0xe6,0xbc,0x19,0xc2,0x57,0xfb,0xec,0xe0,0x76,0x6a,0x5e,0x28,0xd1,0xf4,0xab,0x62,0x08,0x68,
0x5a,0xaa,0x0c,0xc4,0x89,0xda,0x79,0x50,0xd2,0x86,0x16,0x0f,0xc5,0x37,0x21,0x8e,0x95,0x93,0xa0,0x40,0x3d,0x56,0x15,0xee,0xf9,0x3a,0x41,0xdd,0xa8,0x08,0x9b,0x50,
0xaf,0x80,0x13,0xe7,0x41,0xda,0x80,0x5b,0xfb,0x45,0xb0,0xea,0x4a,0x97,0x69,0x21,0x21,0xeb,0x4c,0x4f,0xb2,0xa6,0x82,0xb7,0x46,0xf7,0x73,0x9e,0xa6,0x93,0x53,0xc4,
0x37,0x11,0x5f,0x15,0xfa,0xd2,0x42,0x63,0xd6,0x32,0x64,0xf0,0xf1,0xb6,0x3b,0x3a,0xb2,0xc8,0x25,0xc3,0x80,0xa1,0xa3,0xe7,0xb4,0x35,0xab,0x13,0xa9,0x3a,0x40,0xd1,
0x16,0xf6,0x63,0x62,0x62,0xeb,0x7c,0x58,0x4f,0x56,0x94,0xde,0x28,0x8a,0x48,0xc6,0xf0,0x3b,0x88,0x01,0xd3,0x8b,0xc6,0x0b,0x17,0x01,0x10,0x28,0x00,0x9c,0xd0,0x80,
0xc2,0xe8,0x00,0xdc,0x6b,0x37,0x39,0x1c,0xef,0x78,0x34,0x1d,0x49,0x12,0xd4,0x66,0x88,0x20,0x87,0xe2,0x1e,0x2b,0x05,0x26,0x8a,0xa7,0xd0,0x3d,0xe7,0xd0,0x47,0xed,
0xbe,0x1f,0xec,0xd6,0xa1,0xd3,0xe2,0x0e,0xb5,0xe5,0x3c,0xc2,0x83,0x69,0x77,0x36,0x9d,0x80,0x2b,0xe9,0xe1,0x14,0xe0,0xd5,0x27,0x62,0x35,0xc1,0x40,0xc6,0xf2,0x6e,
0xdb,0xc1,0x58,0xb8,0x9c,0xbc,0x83,0x36,0x81,0xfb,0x70,0xae,0x0c,0x5c,0xc1,0x02,0x81,0x81,0x00,0xd2,0x5f,0x19,0x94,0x11,0xb8,0xdc,0x77,0x2e,0x93,0x15,0x50,0x09,
0xa9,0x20,0xf0,0x1b,0xd1,0x3d,0x4a,0x1d,0x2c,0xb6,0x98,0xec,0x11,0xf1,0xe8,0xa0,0x85,0x4c,0xf8,0x14,0x27,0x80,0x26,0xff,0x78,0xa1,0x17,0x52,0xa3,0xbf,0xf6,0xc6,
0x86,0xab,0xca,0x5f,0x1e,0x5e,0x8f,0x78,0x5e,0x71,0x7d,0x2c,0x57,0xbe,0xda,0x80,0xa2,0x8b,0x6a,0x88,0xab,0xcd,0x78,0xc3,0x67,0xf6,0xb5,0x60,0x96,0x5c,0x24,0xac,
0x8c,0x4b,0x77,0x36,0xe2,0x97,0x16,0x87,0x6a,0x03,0xa7,0x36,0x4a,0xdf,0x5d,0xc3,0x6f,0x1f,0x60,0xfd,0x40,0x60,0x0e,0xca,0xb9,0x25,0x37,0xfa,0x50,0xc3,0x82,0x2f,
0x3a,0xeb,0x1b,0xd5,0xe0,0x6f,0x13,0x17,0x7d,0xa4,0xd2,0x97,0xe7,0xd7,0xbb,0x32,0x84,0x1e,0x77,0x02,0x81,0x81,0x00,0xd3,0x55,0x62,0x53,0x35,0x29,0xa5,0x68,0x65,
0x28,0x74,0x16,0x30,0x66,0x3e,0x95,0x8e,0xc6,0xb1,0xdd,0x37,0xae,0x69,0xe7,0xc8,0x62,0x81,0x11,0x57,0x57,0x92,0x2b,0xd8,0x21,0x98,0xf4,0x3f,0xe6,0x6e,0xd1,0x5d,
0xff,0x6f,0x90,0x6b,0x9e,0x68,0x47,0x50,0x6e,0xdf,0x5a,0x12,0x49,0x04,0xe9,0x42,0x7f,0xe1,0x3e,0x05,0x0a,0xa3,0xee,0xca,0xeb,0x40,0x4b,0x89,0x2a,0xa1,0x3c,0x2e,
0xa5,0xad,0xa8,0xc9,0x5e,0xa7,0x70,0x55,0x3f,0x04,0x1d,0x17,0xcb,0xca,0x21,0xf5,0xc3,0x68,0x68,0xb9,0x50,0x98,0xd3,0x3a,0x2c,0x29,0x21,0x3b,0x42,0x12,0x92,0x34,
0xda,0xc5,0xbb,0x0c,0xb2,0xb4,0x86,0x6a,0x4e,0x3f,0x43,0x99,0xc4,0xb7,0x85,0x97,0x36,0x75,0x61,0x28,0xd6,0x84,0xd1,0x02,0x81,0x80,0x36,0x9b,0xa1,0x14,0x22,0x95,
0x8e,0x05,0x11,0xfd,0xf4,0x26,0x56,0x9b,0xa4,0x85,0x2f,0x73,0x5d,0x29,0x83,0xf1,0x3b,0x64,0xee,0xc2,0xa9,0xbc,0xed,0x0e,0x2d,0x30,0xa8,0x6a,0xa8,0x85,0x77,0x03,
0x65,0x2d,0x9c,0xb2,0x0c,0xfe,0x8e,0x02,0x1f,0x4d,0xe4,0xeb,0x09,0x38,0xcc,0xd2,0x17,0x3b,0x9a,0xeb,0x1c,0x0b,0xb4,0x20,0x7d,0x78,0x26,0x0a,0x12,0xc8,0x2a,0x51,
0x2e,0x65,0x5e,0xb1,0x29,0x32,0x0b,0xe8,0x4f,0x1e,0x9f,0x0c,0xaa,0x93,0x9a,0x8b,0x16,0x6f,0xad,0x54,0x3a,0x2f,0x28,0xb0,0x0b,0xc1,0x2d,0x7c,0x2c,0x73,0x2a,0x84,
0x6c,0xf8,0xde,0xed,0x60,0x12,0xc7,0x17,0xd0,0x37,0xe5,0x88,0xe0,0xcc,0x5f,0xe8,0xa9,0x84,0x51,0x12,0x49,0x99,0xba,0x3f,0x39,0x79,0x02,0x81,0x80,0x74,0x65,0x92,
0xc2,0x41,0x85,0xae,0x94,0xd1,0x22,0x76,0xcd,0xc1,0xda,0x8e,0x9d,0xd1,0x05,0x9e,0xf1,0x38,0xb9,0xd7,0x9a,0xd1,0xc3,0x6f,0x53,0x1d,0xc8,0x1d,0xba,0x08,0x50,0x78,
0xee,0x0e,0x43,0xdc,0xc5,0x74,0x00,0x3c,0x72,0x4e,0xd3,0xf0,0x9b,0x56,0xb1,0xba,0x52,0xe1,0xbf,0x55,0xf5,0x23,0xab,0x4b,0x63,0xdd,0x62,0xfe,0xe7,0x86,0xdc,0x0b,
0x8d,0x8e,0xfe,0xeb,0xcf,0x39,0xe2,0x06,0xc3,0xf0,0x25,0x32,0x13,0xac,0xe0,0x08,0x63,0xfd,0xb7,0x40,0x9d,0x73,0xbf,0x2c,0xc2,0x81,0x4e,0xe2,0xdd,0x74,0x2c,0xde,
0x7a,0x6f,0x28,0xf5,0x11,0x92,0x0a,0xec,0xdd,0x19,0x21,0x54,0x4c,0xb4,0x40,0x64,0x97,0xd7,0x19,0x81,0xfb,0x12,0x4d,0xec,0x58,0x97,0x25,0x90,0x01,0x02,0x81,0x81,
0x00,0x9d,0xf3,0x78,0x3e,0x39,0xb6,0x45,0x97,0x2d,0xb2,0xf2,0xdb,0xe3,0xa8,0xe9,0x8e,0x58,0xd7,0x6b,0xfb,0x11,0x25,0x77,0x6b,0xd5,0xd6,0x61,0x39,0xbb,0x04,0x15,
0xc2,0x7d,0xf9,0xc7,0xf1,0x1a,0xce,0xe5,0x40,0x84,0x26,0x83,0x13,0xb4,0x41,0x49,0xa2,0x42,0x71,0x3c,0xed,0x72,0x17,0xa9,0x2a,0x85,0x25,0xea,0x5b,0xf3,0x6e,0xd6,
0x5b,0x49,0xc8,0xda,0xe4,0xaa,0xa4,0xa5,0x96,0x31,0xaa,0x7e,0x10,0xd7,0x8a,0xf8,0x16,0xe0,0xe0,0x21,0x64,0x22,0xfb,0x1c,0x74,0x27,0xbe,0xf7,0x0f,0x0c,0x94,0x4b,
0x47,0xf4,0xfe,0x58,0xe1,0x72,0xb1,0x08,0xac,0x93,0xb5,0x06,0x29,0x78,0xb8,0xb3,0xf8,0xd4,0x2f,0x4e,0x43,0x3a,0x0c,0x14,0x8b,0x00,0xd2,0xd5,0x21,0x93,0x8c,0x5d,
0x85,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 
  };
  DWORD blobLen = 0x4c1;

  int errCode = 0;

  NCRYPT_PROV_HANDLE prov = NULL;
  NCRYPT_KEY_HANDLE  key = NULL;
  DWORD policy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

  LPCWSTR name = L"ImportedKey";
  BCryptBuffer cb[1];
  cb[0].BufferType = NCRYPTBUFFER_PKCS_KEY_NAME;
  cb[0].pvBuffer = (void*)name;
  cb[0].cbBuffer = lstrlenW(name) * 2 + 2;
  NCryptBufferDesc desc;
  desc.ulVersion = 0;
  desc.pBuffers = cb;
  desc.cBuffers = 1;
  if (errCode = NCryptOpenStorageProvider(&prov, MS_KEY_STORAGE_PROVIDER, 0)) goto done;
  if (errCode = NCryptImportKey(prov, NULL, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, &desc, &key, blob, blobLen, NCRYPT_DO_NOT_FINALIZE_FLAG)) goto done;
  if (errCode = NCryptSetProperty(key, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)(&policy), sizeof(policy), NCRYPT_PERSIST_FLAG)) goto done;
  if (errCode = NCryptFinalizeKey(key, 0)) goto done;

done:
  if (prov) NCryptFreeObject(prov);
  if (key) NCryptFreeObject(key);
  return errCode;
}

导入完成之后, 可以使用 ListKeys 检查你的导入是否成功了.

END!