

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 加密 API：下一代 (CNG) 和密钥存储提供商 (KSP) AWS CloudHSM
<a name="ksp-v3-library"></a>

Windows AWS CloudHSM 客户端包括 CNG 和 KSP 提供商。

*密钥存储提供程序* (KSPs) 支持密钥存储和检索。例如，如果您向 Windows Server 添加了 Microsoft Active Directory 证书服务 (AD CS)，并且选择为证书颁发机构 (CA) 创建新的私有密钥，则可以选择将管理密钥存储的 KSP。当您配置 AD CS 角色时，您可以选择此 KSP。有关更多信息，请参阅 [创建 Windows Server CA](win-ca-overview-sdk5.md#win-ca-setup-sdk5)。

*加密 API：下一代 (CNG)* 是一种特定于 Microsoft Windows 操作系统的加密 API。CNG 使开发人员能够使用加密技术来保护基于 Windows 的应用程序。简而言之，CNG 的 AWS CloudHSM 实现提供了以下功能：
+ **加密基元** - 使您能够执行基本加密操作。
+ **密钥导入和导出** - 使您能够导入和导出非对称密钥。
+ **数据保护 API（CNG DPAPI）**- 使您能够轻松加密和解密数据。
+ **密钥存储和检索** - 使您能够安全地存储和隔离非对称密钥对的私有密钥。

**Topics**
+ [验证 KSP 和 CNG 提供商 AWS CloudHSM](ksp-v3-library-install.md)
+ [使用 AWS CloudHSM Windows 客户端的先决条件](ksp-library-prereq.md)
+ [将密 AWS CloudHSM 钥与证书关联](ksp-library-associate-key-certificate.md)
+ [压缩天然气提供商的代码示例 AWS CloudHSM](ksp-library-sample.md)

# 验证 KSP 和 CNG 提供商 AWS CloudHSM
<a name="ksp-v3-library-install"></a>

KSP 和 CNG 提供程序是在你安装 Windows AWS CloudHSM 客户端时安装的。您可以按照 [安装客户端 (Windows)](kmu-install-and-configure-client-win.md) 中的下列步骤安装客户端。

使用以下部分来验证提供程序的安装。

## 配置并运行 Windows AWS CloudHSM 客户端
<a name="ksp-configure-client-windows"></a>

您必须先满足 [先决条件](ksp-library-prereq.md)，然后才能启动 Windows CloudHSM 客户端。然后，更新提供程序使用的配置文件，并通过完成以下步骤启动客户端。在首次使用 KSP 和 CNG 提供商时，以及在集群 HSMs 中添加或删除之后，您需要执行这些步骤。这样， AWS CloudHSM 就可以同步集群 HSMs 中所有数据并保持一致性。

### 步骤 1：停止 AWS CloudHSM 客户端
<a name="ksp-stop-cloudhsm-client"></a>

在更新提供程序使用的配置文件之前，请停止 AWS CloudHSM 客户端。如果客户端已停止，运行停止命令也不会产生影响。
+ 对于 Windows 客户端 1.1.2 以上版本：

  ```
  C:\Program Files\Amazon\CloudHSM>net.exe stop AWSCloudHSMClient
  ```
+ 对于 Windows 客户端 1.1.1 及更低版本：

  在启动 AWS CloudHSM 客户端的命令窗口中使用 **C **trl** \$1 C**。

### 步骤 2：更新 AWS CloudHSM 配置文件
<a name="ksp-config-a"></a>

此步骤使用[配置工具](configure-tool.md)的`-a`参数将集群中其中一个的弹性网络接口 (ENI) IP 地址添加到配置文件 HSMs 中。

```
PS C:\> & "C:\Program Files\Amazon\CloudHSM\configure.exe" -a <HSM ENI IP>
```

要获取集群中 HSM 的 ENI IP 地址，请导航到 AWS CloudHSM 控制台，选择**集群**，然后选择所需的集群。您也可以使用[DescribeClusters](https://docs.aws.amazon.com/cloudhsm/latest/APIReference/API_DescribeClusters.html)操作、desc [ribe-clust](https://docs.aws.amazon.com/cli/latest/reference/cloudhsmv2/describe-clusters.html) ers 命令或 cmdlet。[Get-HSM2Cluster](https://docs.aws.amazon.com/powershell/latest/reference/items/Get-HSM2Cluster.html) PowerShell仅输入一个 ENI IP 地址。您使用哪个 HSM ENI IP 地址并不重要。

### 步骤 3：启动 AWS CloudHSM 客户端
<a name="ksp-start-cloudhsm-client"></a>

接下来，启动或重新启动 AWS CloudHSM 客户端。当 AWS CloudHSM 客户端启动时，它使用其配置文件中的 ENI IP 地址来查询集群。然后，它将集群 HSMs 中所有的 ENI IP 地址添加到集群信息文件中。
+ 对于 Windows 客户端 1.1.2 以上版本：

  ```
  C:\Program Files\Amazon\CloudHSM>net.exe start AWSCloudHSMClient
  ```
+ 对于 Windows 客户端 1.1.1 及更低版本：

  ```
  C:\Program Files\Amazon\CloudHSM>start "cloudhsm_client" cloudhsm_client.exe C:\ProgramData\Amazon\CloudHSM\data\cloudhsm_client.cfg
  ```

## 检查 KSP 和 CNG 提供程序
<a name="ksp-check-providers"></a>

您可以使用以下任一命令来确定您的系统上所安装的提供程序。命令列出了已注册的 KSP 和 CNG 提供程序。 AWS CloudHSM 客户端无需运行。

```
PS C:\> & "C:\Program Files\Amazon\CloudHSM\ksp_config.exe" -enum
```

```
PS C:\> & "C:\Program Files\Amazon\CloudHSM\cng_config.exe" -enum
```

要验证您的 Windows Server EC2 实例上是否安装了 KSP 和 CNG 提供程序，您应该在列表中查看以下条目：

```
Cavium CNG Provider
Cavium Key Storage Provider
```

如果缺少 CNG 提供程序，请运行以下命令。

```
PS C:\> & "C:\Program Files\Amazon\CloudHSM\cng_config.exe" -register
```

如果缺少 KSP 提供程序，请运行以下命令。

```
PS C:\> & "C:\Program Files\Amazon\CloudHSM\ksp_config.exe" -register
```

# 使用 AWS CloudHSM Windows 客户端的先决条件
<a name="ksp-library-prereq"></a>

在启动 Windows AWS CloudHSM 客户端并使用 KSP 和 CNG 提供商之前，您必须在系统上设置 HSM 的登录凭据。您可以通过 Windows Credentials Manager 或系统环境变量设置凭证。我们建议您使用 Windows Credential Manager 存储凭证。此选项适用于 AWS CloudHSM 客户端版本 2.0.4 及更高版本。使用环境变量更易于设置，但安全性低于使用 Windows Credential Manager。

## Windows Credential Manager
<a name="wcm"></a>

您可以使用 `set_cloudhsm_credentials` 实用工具或 Windows Credentials Manager 界面。
+ **使用 `set_cloudhsm_credentials` 实用工具**：

  `set_cloudhsm_credentials` 实用工具包含在您的 Windows 安装程序中。您可以使用此实用工具方便地将 HSM 登录凭证传递到 Windows Credential Manager。如果要从源代码编译此实用工具，可以使用安装程序中包含的 Python 代码。

  1. 转到 `C:\Program Files\Amazon\CloudHSM\tools\` 文件夹。

  1. 使用 CU 用户名和密码参数运行 `set_cloudhsm_credentials.exe` 文件。

     ```
     set_cloudhsm_credentials.exe --username <CU USER> --password <CU PASSWORD>
     ```
+ **使用 Credential Manager 界面**：

  您可以使用 Credential Manager 界面来手动管理您的凭证。

  1. 要打开 Credential Manager，请在任务栏上的搜索框中键入 `credential manager`，然后选择 **Credential Manager**。

  1. 选择 **Windows 凭证**来管理 Windows 凭证。

  1. 选择**添加通用凭证**，并填写详细信息，如下所示：
     + 在 **Internet 或网络地址** 中，输入目标名称作为 `cloudhsm_client`。
     + 在 **用户名 ** 和 **密码** 中，输入 CU 凭证。
     + 单击 **确定**。

## 系统环境变量
<a name="enviorn-var"></a>

您可以为 Windows 应用程序设置标识 HSM 和[加密用户](understanding-users-cmu.md#crypto-user-cmu) (CU) 的系统环境变量。您可以使用 [**setx** 命令](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/setx)设置系统环境变量，或通过[编程方式](https://msdn.microsoft.com/en-us/library/system.environment.setenvironmentvariable(v=vs.110).aspx)或在 Windows **系统属性** 控制面板的 **高级** 选项卡中设置永久系统环境变量。

**警告**  
当您通过系统环境变量设置凭证时，密码在用户系统上以纯文本形式可用。要克服此问题，请使用 Windows Credential Manager。

设置以下系统环境变量：

**`n3fips_password=<CU USERNAME>:<CU PASSWORD>`**  
在 HSM 中确定[加密用户](understanding-users-cmu.md#crypto-user-cmu) (CU)，并提供所有必需的登录信息。您的应用程序以此 CU 的身份进行身份验证和运行。该应用程序具有此 CU 的权限，只能查看和管理 CU 拥有和共享的密钥。要创建新 CU，请使用 [createUser](cloudhsm_mgmt_util-createUser.md)。要查找现有用户 CUs，请使用[列表用户](cloudhsm_mgmt_util-listUsers.md)。  
例如：  

```
setx /m n3fips_password test_user:password123
```

# 将密 AWS CloudHSM 钥与证书关联
<a name="ksp-library-associate-key-certificate"></a>

在将 AWS CloudHSM 密钥用于第三方工具（例如 Microsoft 的工具）之前 [SignTool](https://docs.microsoft.com/en-us/windows/win32/seccrypto/signtool)，必须将密钥的元数据导入本地证书存储区，并将元数据与证书相关联。要导入密钥的元数据，请使用包含在 CloudHSM 3.0 及更高版本中的 import\$1key.exe 实用程序。以下步骤提供其他信息和示例输出。

## 步骤 1：导入证书
<a name="import-cert"></a>

在 Windows 上，您应该能够双击证书将其导入到本地证书存储。

但是，如果双击不起作用，请使用 [Microsoft Certreq 工具](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn296456%28v%3dws.11%29)将证书导入证书管理器中。例如：

```
certreq -accept <certificatename>
```

如果此操作失败，并且您收到错误 `Key not found`，请继续执行步骤 2。如果证书显示在密钥库中，则表示您已完成任务，无需进一步操作。

## 步骤 2：收集证书识别信息
<a name="cert-identifier"></a>

如果上一步未成功，则需要将您的私有密钥与证书关联。但是，您必须先找到证书的唯一容器名称和序列号，然后才能创建关联。使用实用程序（例如 **certutil**）显示所需的证书信息。**certutil** 的以下示例输出显示了容器名称和序列号。

```
================ Certificate 1 ================ Serial Number:
			72000000047f7f7a9d41851b4e000000000004Issuer: CN=Enterprise-CANotBefore: 10/8/2019 11:50
			AM NotAfter: 11/8/2020 12:00 PMSubject: CN=www.example.com, OU=Certificate Management,
			O=Information Technology, L=Seattle, S=Washington, C=USNon-root CertificateCert
			Hash(sha1): 7f d8 5c 00 27 bf 37 74 3d 71 5b 54 4e c0 94 20 45 75 bc 65No key provider
			information Simple container name: CertReq-39c04db0-6aa9-4310-93db-db0d9669f42c Unique
			container name: CertReq-39c04db0-6aa9-4310-93db-db0d9669f42c
```



## 步骤 3：将 AWS CloudHSM 私钥与证书关联
<a name="associate-key-certificate"></a>

要将密钥与证书关联，请首先确保[启动 AWS CloudHSM 客户端守护程序](key_mgmt_util-setup.md#key_mgmt_util-start-cloudhsm-client)。然后，使用 import\$1key.exe（包含在 CloudHSM 版本 3.0 及更高版本中）将私有密钥与证书关联。指定证书时，请使用其简单容器名称。以下示例显示了命令和响应。此操作仅复制密钥的元数据；密钥保留在 HSM 上。

```
$> import_key.exe –RSA CertReq-39c04db0-6aa9-4310-93db-db0d9669f42c

Successfully opened Microsoft Software Key Storage Provider : 0NCryptOpenKey failed : 80090016
```

## 步骤 4：更新证书存储
<a name="update-certificate-store"></a>

请确保 AWS CloudHSM 客户端守护程序仍在运行。然后，使用 **certutil** 动词 **-repairstore** 更新证书序列号。以下示例显示了命令和输出。有关 [**-repairstore** 动词](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc732443(v=ws.11)?redirectedfrom=MSDN#-repairstore)的信息，请参阅 Microsoft 文档。

```
C:\Program Files\Amazon\CloudHSM>certutil -f -csp "Cavium Key Storage Provider"-repairstore my "72000000047f7f7a9d41851b4e000000000004"
my "Personal"
================ Certificate 1 ================
Serial Number: 72000000047f7f7a9d41851b4e000000000004
Issuer: CN=Enterprise-CA
NotBefore: 10/8/2019 11:50 AM
NotAfter: 11/8/2020 12:00 PM
Subject: CN=www.example.com, OU=Certificate Management, O=Information Technology, L=Seattle, S=Washington, C=US
Non-root CertificateCert Hash(sha1): 7f d8 5c 00 27 bf 37 74 3d 71 5b 54 4e c0 94 20 45 75 bc 65       
SDK Version: 3.0 
Key Container = CertReq-39c04db0-6aa9-4310-93db-db0d9669f42c 
Provider = "Cavium Key Storage Provider"
Private key is NOT exportableEncryption test passedCertUtil: -repairstore command completed successfully.
```

更新证书序列号后，您可以在 Windows 上的任何第三方签名工具中使用此证书和相应的 AWS CloudHSM 私钥。

# 压缩天然气提供商的代码示例 AWS CloudHSM
<a name="ksp-library-sample"></a>

****  
\$1\$1 只是示例代码 – 不用于生产用途 \$1\$1  
本示例代码仅用于说明。请勿在生产环境中运行此代码。

下面的示例显示了如何枚举系统中已注册的加密提供程序，以查找 Windows 版 CloudHSM 客户端安装的 CNG 提供程序。该示例还显示了如何创建非对称密钥对以及如何使用该密钥对来签署数据。

**重要**  
在运行此示例之前，必须按照先决条件中的说明设置 HSM 凭证。有关详细信息，请参阅[使用 AWS CloudHSM Windows 客户端的先决条件](ksp-library-prereq.md)。

```
// CloudHsmCngExampleConsole.cpp : Console application that demonstrates CNG capabilities.
// This example contains the following functions.
//
//   VerifyProvider()          - Enumerate the registered providers and retrieve Cavium KSP and CNG providers.
//   GenerateKeyPair()         - Create an RSA key pair.
//   SignData()                - Sign and verify data.
//

#include "stdafx.h"
#include <Windows.h>

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif

#define CAVIUM_CNG_PROVIDER L"Cavium CNG Provider"
#define CAVIUM_KEYSTORE_PROVIDER L"Cavium Key Storage Provider"

// Enumerate the registered providers and determine whether the Cavium CNG provider
// and the Cavium KSP provider exist.
//
bool VerifyProvider()
{
  NTSTATUS status;
  ULONG cbBuffer = 0;
  PCRYPT_PROVIDERS pBuffer = NULL;
  bool foundCng = false;
  bool foundKeystore = false;

  // Retrieve information about the registered providers.
  //   cbBuffer - the size, in bytes, of the buffer pointed to by pBuffer.
  //   pBuffer - pointer to a buffer that contains a CRYPT_PROVIDERS structure.
  status = BCryptEnumRegisteredProviders(&cbBuffer, &pBuffer);

  // If registered providers exist, enumerate them and determine whether the
  // Cavium CNG provider and Cavium KSP provider have been registered.
  if (NT_SUCCESS(status))
  {
    if (pBuffer != NULL)
    {
      for (ULONG i = 0; i < pBuffer->cProviders; i++)
      {
        // Determine whether the Cavium CNG provider exists.
        if (wcscmp(CAVIUM_CNG_PROVIDER, pBuffer->rgpszProviders[i]) == 0)
        {
          printf("Found %S\n", CAVIUM_CNG_PROVIDER);
          foundCng = true;
        }

        // Determine whether the Cavium KSP provider exists.
        else if (wcscmp(CAVIUM_KEYSTORE_PROVIDER, pBuffer->rgpszProviders[i]) == 0)
        {
          printf("Found %S\n", CAVIUM_KEYSTORE_PROVIDER);
          foundKeystore = true;
        }
      }
    }
  }
  else
  {
    printf("BCryptEnumRegisteredProviders failed with error code 0x%08x\n", status);
  }

  // Free memory allocated for the CRYPT_PROVIDERS structure.
  if (NULL != pBuffer)
  {
    BCryptFreeBuffer(pBuffer);
  }

  return foundCng == foundKeystore == true;
}

// Generate an asymmetric key pair. As used here, this example generates an RSA key pair 
// and returns a handle. The handle is used in subsequent operations that use the key pair. 
// The key material is not available.
//
// The key pair is used in the SignData function.
//
NTSTATUS GenerateKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE *hKey)
{
  NTSTATUS status;

  // Generate the key pair.
  status = BCryptGenerateKeyPair(hAlgorithm, hKey, 2048, 0);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptGenerateKeyPair failed with code 0x%08x\n", status);
    return status;
  }

  // Finalize the key pair. The public/private key pair cannot be used until this 
  // function is called.
  status = BCryptFinalizeKeyPair(*hKey, 0);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptFinalizeKeyPair failed with code 0x%08x\n", status);
    return status;
  }

  return status;
}

// Sign and verify data using the RSA key pair. The data in this function is hardcoded
// and is for example purposes only.
//
NTSTATUS SignData(BCRYPT_KEY_HANDLE hKey)
{
  NTSTATUS status;
  PBYTE sig;
  ULONG sigLen;
  ULONG resLen;
  BCRYPT_PKCS1_PADDING_INFO pInfo;

  // Hardcode the data to be signed (for demonstration purposes only).
  PBYTE message = (PBYTE)"d83e7716bed8a20343d8dc6845e57447";
  ULONG messageLen = strlen((char*)message);

  // Retrieve the size of the buffer needed for the signature.
  status = BCryptSignHash(hKey, NULL, message, messageLen, NULL, 0, &sigLen, 0);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptSignHash failed with code 0x%08x\n", status);
    return status;
  }

  // Allocate a buffer for the signature.
  sig = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sigLen);
  if (sig == NULL)
  {
    return -1;
  }

  // Use the SHA256 algorithm to create padding information.
  pInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;

  // Create a signature.
  status = BCryptSignHash(hKey, &pInfo, message, messageLen, sig, sigLen, &resLen, BCRYPT_PAD_PKCS1);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptSignHash failed with code 0x%08x\n", status);
    return status;
  }

  // Verify the signature.
  status = BCryptVerifySignature(hKey, &pInfo, message, messageLen, sig, sigLen, BCRYPT_PAD_PKCS1);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptVerifySignature failed with code 0x%08x\n", status);
    return status;
  }

  // Free the memory allocated for the signature.
  if (sig != NULL)
  {
    HeapFree(GetProcessHeap(), 0, sig);
    sig = NULL;
  }

  return 0;
}

// Main function.
//
int main()
{
  NTSTATUS status;
  BCRYPT_ALG_HANDLE hRsaAlg;
  BCRYPT_KEY_HANDLE hKey = NULL;

  // Enumerate the registered providers.
  printf("Searching for Cavium providers...\n");
  if (VerifyProvider() == false) {
    printf("Could not find the CNG and Keystore providers\n");
    return 1;
  }

  // Get the RSA algorithm provider from the Cavium CNG provider.
  printf("Opening RSA algorithm\n");
  status = BCryptOpenAlgorithmProvider(&hRsaAlg, BCRYPT_RSA_ALGORITHM, CAVIUM_CNG_PROVIDER, 0);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptOpenAlgorithmProvider RSA failed with code 0x%08x\n", status);
    return status;
  }

  // Generate an asymmetric key pair using the RSA algorithm.
  printf("Generating RSA Keypair\n");
  GenerateKeyPair(hRsaAlg, &hKey);
  if (hKey == NULL)
  {
    printf("Invalid key handle returned\n");
    return 0;
  }
  printf("Done!\n");

  // Sign and verify [hardcoded] data using the RSA key pair.
  printf("Sign/Verify data with key\n");
  SignData(hKey);
  printf("Done!\n");

  // Remove the key handle from memory.
  status = BCryptDestroyKey(hKey);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptDestroyKey failed with code 0x%08x\n", status);
    return status;
  }

  // Close the RSA algorithm provider.
  status = BCryptCloseAlgorithmProvider(hRsaAlg, NULL);
  if (!NT_SUCCESS(status))
  {
    printf("BCryptCloseAlgorithmProvider RSA failed with code 0x%08x\n", status);
    return status;
  }

  return 0;
}
```