

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 密碼編譯 API： 的新一代 (CNG) 和金鑰儲存供應商 (KSP) AWS CloudHSM
<a name="ksp-v3-library"></a>

適用於 Windows 的 AWS CloudHSM 用戶端包含 CNG 和 KSP 提供者。

*金鑰儲存提供者* (KSP) 可讓您儲存和擷取金鑰。例如，如果您將 Microsoft Active Directory Certificate Services (AD CS) 角色新增到您的 Windows 伺服器，並選擇為憑證授權機制 (CA) 建立新的私有金鑰，您可以選擇 KSP 來管理金鑰儲存。當您設定 AD CS 角色時，您可以選擇 KSP。如需詳細資訊，請參閱[建立 Windows Server CA](win-ca-overview-sdk5.md#win-ca-setup-sdk5)。

*Cryptography API: Next Generation (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)
+ [的 CNG 提供者程式碼範例 AWS CloudHSM](ksp-library-sample.md)

# 驗證 的 KSP 和 CNG 提供者 AWS CloudHSM
<a name="ksp-v3-library-install"></a>

安裝 Windows AWS CloudHSM 用戶端時會安裝 KSP 和 CNG 提供者。您可以依照在 [安裝用戶端 (Windows)](kmu-install-and-configure-client-win.md) 的步驟安裝用戶端。

使用下列各節來驗證供應商的安裝。

## 設定和執行 Windows AWS CloudHSM 用戶端
<a name="ksp-configure-client-windows"></a>

若要啟動 Windows CloudHSM 用戶端，您必須先符合[先決條件](ksp-library-prereq.md)。然後，透過完成以下步驟，更新提供者使用的組態檔案並啟動用戶端。在您首次使用 KSP 和 CNG 提供者時和在叢集中新增或移除 HSM 之後，您需要執行以下步驟。如此一來， AWS CloudHSM 就能同步資料並維持叢集中所有 HSMs一致性。

### 步驟 1：停止 AWS CloudHSM 用戶端
<a name="ksp-stop-cloudhsm-client"></a>

更新提供者使用的組態檔案之前，請先停止 AWS CloudHSM 用戶端。如果用戶端已停止，執行停止命令並不會造成影響。
+ 用於 Windows 用戶端 1.1.2\$1：

  ```
  C:\Program Files\Amazon\CloudHSM>net.exe stop AWSCloudHSMClient
  ```
+ 用於 Windows 用戶端 1.1.1 和更早版本：

  在您啟動 AWS CloudHSM 用戶端的命令視窗中使用 **Ctrl**\$1**C**。

### 步驟 2：更新 AWS CloudHSM 組態檔案
<a name="ksp-config-a"></a>

此步驟使用`-a`設定工具[的 ](configure-tool.md) 參數，將叢集的其中一個 HSM 的彈性網路界面 (ENI) IP 地址新增至組態檔案。

```
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) 操作、[describe-clusters](https://docs.aws.amazon.com/cli/latest/reference/cloudhsmv2/describe-clusters.html) 命令或 [Get-HSM2Cluster](https://docs.aws.amazon.com/powershell/latest/reference/items/Get-HSM2Cluster.html) PowerShell cmdlet。僅輸入一個 ENI IP 地址。無論您使用哪個 ENI IP 地址都是如此。

### 步驟 3：啟動 AWS CloudHSM 用戶端
<a name="ksp-start-cloudhsm-client"></a>

接著，啟動或重新啟動 AWS CloudHSM 用戶端。當 AWS CloudHSM 用戶端啟動時，它會使用其組態檔案中的 ENI IP 地址來查詢叢集。接著會將叢集的所有 HSM 的 ENI IP 地址，新增至叢集資訊檔案。
+ 用於 Windows 用戶端 1.1.2\$1：

  ```
  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
```

如要驗證 KSP 和 CNG 提供者是否已安裝在 Windows Server EC2 執行個體上，您會在清單中看到下列項目：

```
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 認證管理員或系統環境變數來設定登入資料。建議您使用 Windows 認證管理員來存放登入資料。此選項適用於 AWS CloudHSM 用戶端 2.0.4 版及更新版本。使用環境變數較容易設定，但比起使用 Windows 認證管理員較不安全。

## Windows 認證管理員
<a name="wcm"></a>

您可以使用 `set_cloudhsm_credentials` 公用程式或 Windows 認證管理員界面。
+ **使用 `set_cloudhsm_credentials` 公用程式**：

  `set_cloudhsm_credentials` 公用程式包含在您的 Windows 安裝程式中。您可以使用此公用程式，輕鬆地將 HSM 登入資料傳遞到 Windows 認證管理員。如果您想要從來源編譯此公用程式，您可以使用安裝程式中包含的 Python 程式碼。

  1. 前往 `C:\Program Files\Amazon\CloudHSM\tools\` 資料夾。

  1. 使用 CU 使用者名稱和密碼參數執行 `set_cloudhsm_credentials.exe` 檔案。

     ```
     set_cloudhsm_credentials.exe --username <CU USER> --password <CU PASSWORD>
     ```
+ **使用認證管理員界面**：

  您可以使用認證管理員界面來手動管理您的登入資料。

  1. 若要開啟認證管理員，請在工作列的搜尋方塊中輸入 `credential manager`，然後選取**認證管理員**。

  1. 選取 **Windows 認證**以管理 Windows 認證。

  1. 選取**新增一般認證**並填寫詳細資訊，如下所示：
     + 在**網際網路或網路位址**中，輸入目標名稱為 `cloudhsm_client`。
     + 在**使用者名稱**和**密碼**中，輸入 CU 登入資料。
     + 按一下 **OK (確定)**。

## 系統環境變數
<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 認證管理員。

設定下列系統環境變數：

**`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)。若要尋找現有 CU，請使用 [listUsers](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 私有金鑰與任何第三方簽署工具搭配使用。

# 的 CNG 提供者程式碼範例 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;
}
```