

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

# 使用用戶端 SDK 3 AWS CloudHSM 與 Java Keytool 和 Jarsigner 整合
<a name="keystore-third-party-tools"></a>

AWS CloudHSM 金鑰存放區是特殊用途的 JCE 金鑰存放區，透過 `keytool`和 等第三方工具，利用與硬體安全模組 (HSM) 上金鑰相關聯的憑證`jarsigner`。 AWS CloudHSM 不會在 HSM 上存放憑證，因為憑證是公有、非機密資料。 AWS CloudHSM 金鑰存放區會將憑證存放在本機檔案中，並將憑證映射至 HSM 上的對應金鑰。

當您使用 AWS CloudHSM 金鑰存放區產生新的金鑰時，本機金鑰存放區檔案中不會產生任何項目 – 金鑰會在 HSM 上建立。同樣的，當您使用 AWS CloudHSM 金鑰存放區搜尋金鑰時，搜尋會傳遞至 HSM。當您將憑證存放在 AWS CloudHSM 金鑰存放區時，提供者會驗證 HSM 上是否存在具有對應別名的金鑰對，然後關聯與對應金鑰對一起提供的憑證。

**Topics**
+ [先決條件](keystore-prerequisites.md)
+ [搭配 keytool 使用金鑰存放區](using_keystore_with_keytool.md)
+ [搭配 jarsigner 使用金鑰存放區](using_keystore_jarsigner.md)
+ [已知問題](known-issues-keytool-jarsigner.md)
+ [向金鑰存放區註冊預先存在的金鑰](register-pre-existing-keys-with-keystore.md)

# 使用用戶端 SDK 3 AWS CloudHSM 與 Java Keytool 和 Jarsigner 整合的先決條件
<a name="keystore-prerequisites"></a>

若要使用 AWS CloudHSM 金鑰存放區，您必須先初始化和設定 AWS CloudHSM JCE 開發套件。若要這樣做，請使用下列步驟。

## 步驟 1：安裝 JCE
<a name="prereq-step-one"></a>

若要安裝 JCE，包括 AWS CloudHSM 用戶端先決條件，請遵循[安裝 Java 程式庫](java-library-install.md)的步驟。

## 步驟 2：將 HSM 登入憑證新增至環境變數
<a name="prereq-step-two"></a>

設定環境變數以包含您的 HSM 登入憑證。

```
export HSM_PARTITION=PARTITION_1
export HSM_USER=<HSM user name> 
export HSM_PASSWORD=<HSM password>
```

**注意**  
CloudHSM JCE 提供多種登入選項。若要搭配第三方應用程式使用 AWS CloudHSM 金鑰存放區，您必須搭配環境變數使用隱含登入。如果您想要透過應用程式碼使用明確登入，您必須使用 AWS CloudHSM 金鑰存放區建置自己的應用程式。如需詳細資訊，請參閱有關[使用 AWS CloudHSM 金鑰存放區的](alternative-keystore.md)文章。

## 步驟 3：註冊 JCE 供應商
<a name="prereq-step-three"></a>

若要註冊 JCE 提供商，請前往 Java CloudProvider 組態。

1. 在 Java 安裝中開啟 java.security 組態檔案進行編輯。

1. 在 java.security 組態檔案中，新增 `com.cavium.provider.CaviumProvider` 為最後一個提供商。例如，如果 java.security 檔案中有九個供應商，請將下列提供商新增為區段中的最後一個供應商。如果將 Cavium 提供商新增為優先順序較高的提供商，可能會對您的系統效能帶來負面影響。

   `security.provider.10=com.cavium.provider.CaviumProvider`
**注意**  
進階使用者可能習慣在使用 keytool 時指定 `-providerName`、`-providerclass` 和 `-providerpath` 命令列選項，而不是更新安全性組態檔案。如果您在使用金鑰存放區產生 AWS CloudHSM 金鑰時嘗試指定命令列選項，則會導致錯誤。

# 使用用戶端 SDK 3 搭配 keytool 使用 AWS CloudHSM 金鑰存放區
<a name="using_keystore_with_keytool"></a>

[ Keytool](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html) 是 Linux 系統中常見的金鑰和憑證任務的常用命令列執行程序。完整的 Keytool 教學並不在 AWS CloudHSM 文件範圍之內。本文說明透過金鑰存放區使用 AWS CloudHSM 作為信任根時，應搭配各種 AWS CloudHSM keytool 函數使用的特定參數。

搭配 AWS CloudHSM 金鑰存放區使用 keytool 時，請為任何 keytool 命令指定下列引數：

```
-storetype CLOUDHSM \
		-J-classpath '-J/opt/cloudhsm/java/*' \
		-J-Djava.library.path=/opt/cloudhsm/lib
```

如果您想要使用金鑰存放區建立新的 AWS CloudHSM 金鑰存放區檔案，請參閱 [使用 AWS CloudHSM 用戶端 SDK 3 的 AWS CloudHSM KeyStore](alternative-keystore.md#using_cloudhsm_keystore)。若要要使用現有的金鑰存放區，請使用金鑰存放區引數指定其名稱 (包含路徑) 至 Keytool。如果您在 keytool 命令中指定不存在的金鑰存放區檔案， AWS CloudHSM 金鑰存放區會建立新的金鑰存放區檔案。

# 使用 keytool 建立新的 AWS CloudHSM 金鑰
<a name="create_key_keytool"></a>

您可以使用 keytool 來產生 AWS CloudHSM JCE SDK 支援的任何類型的金鑰。請參閱 Java 程式庫的[支援金鑰](java-lib-supported.md#java-keys)文章中金鑰和長度的完整清單。

**重要**  
透過 keytool 產生的金鑰會在軟體中產生，然後匯入 AWS CloudHSM 做為可擷取的持久性金鑰。

直接在硬體安全模組 (HSM) 上建立不可擷取金鑰，然後將其與 keytool 或 Jarsigner 搭配使用的指示，會顯示在[向 AWS CloudHSM Key Store 註冊既有金鑰](register-pre-existing-keys-with-keystore.md)的程式碼範例中。我們強烈建議您不要在 Keytool 中產生不可匯出的金鑰，然後又匯入相對應的憑證至金鑰存放區。如果您透過 keytool 和 jarsigner 使用可擷取的 RSA 或 EC 金鑰，提供者會從 匯出金鑰， AWS CloudHSM 然後在本機使用金鑰進行簽署操作。

如果您有多個用戶端執行個體連接到 CloudHSM 叢集，請注意，在一個用戶端執行個體的金鑰存放區上匯入憑證不會自動讓憑證可於其他用戶端執行個體使用。若要在每個用戶端執行個體上註冊金鑰和相關憑證，您必須執行 Java 應用程式，如[使用 Keytool 產生 CSR](generate_csr_using_keytool.md)中所述。或者，您可以在一個用戶端上進行必要的變更，並將產生的金鑰存放區檔案複製到其他每個用戶端執行個體。

**範例 1：**產生對稱 AES-256 金鑰，並將其儲存在工作目錄中名為 "example\$1keystore.store" 的金鑰存放區檔案中。將 *<secret label>* 取代為唯一的標籤。

```
keytool -genseckey -alias <secret label> -keyalg aes \
		-keysize 256 -keystore example_keystore.store \
		-storetype CloudHSM -J-classpath '-J/opt/cloudhsm/java/*' \
		-J-Djava.library.path=/opt/cloudhsm/lib/
```

**範例 2：**產生 RSA 2048 金鑰對，並將其儲存在工作目錄中名為 "example\$1keystore.store" 的金鑰存放區檔案中。將 *<RSA key pair label>* 取代為唯一的標籤。

```
keytool -genkeypair -alias <RSA key pair label> \
        -keyalg rsa -keysize 2048 \
        -sigalg sha512withrsa \
        -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

**範例 3：**產生 p256 ED 金鑰並將其儲存在工作目錄中名為 "example\$1keystore.store" 的金鑰存放區檔案中。將 *<ec key pair label>* 取代為唯一的標籤。

```
keytool -genkeypair -alias <ec key pair label> \
        -keyalg ec -keysize 256 \
        -sigalg SHA512withECDSA \
        -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

您可以在 Java 程式庫中找到[支援的簽章演算法](java-lib-supported.md#java-sign-verify)清單。

# 使用 AWS CloudHSM keytool 刪除金鑰
<a name="delete_key_using_keytool"></a>

 AWS CloudHSM 金鑰存放區不支援刪除金鑰。若要刪除金鑰，您必須使用 AWS CloudHSM命令列工具 的 `deleteKey`函數[使用 KMU 刪除 AWS CloudHSM 金鑰](key_mgmt_util-deleteKey.md)。

# 使用 keytool 產生 AWS CloudHSM CSR
<a name="generate_csr_using_keytool"></a>

如果您使用 [適用於 AWS CloudHSM 用戶端 SDK 5 的 OpenSSL 動態引擎](openssl-library.md)，您可以在產生憑證簽署要求 (CSR) 時獲得最大的彈性。以下命令使用 Keytool 來產生具有別名 `example-key-pair` 的金鑰對適用 CSR。

```
keytool -certreq -alias <key pair label> \
        -file example_csr.csr \
        -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

**注意**  
若要使用 Keytool 的金鑰對，該金鑰對必須再指定的金鑰存放區檔案中有項目。如果您要使用非 Keytool 產生的金鑰對，則必須將金鑰和憑證中繼資料匯入至金鑰存放區。如需匯入金鑰存放區資料的指示，請參閱[使用 AWS CloudHSM Keytool 將中繼憑證和根憑證匯入金鑰存放區](import_cert_using_keytool.md)。

# 使用 keytool 將中繼憑證和根憑證匯入 AWS CloudHSM 金鑰存放區
<a name="import_cert_using_keytool"></a>

若要將 CA 憑證匯入 AWS CloudHSM，您必須在新匯入的憑證上啟用完整憑證鏈的驗證。以下為命令的範例。

```
keytool -import -trustcacerts -alias rootCAcert \
        -file rootCAcert.cert -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

如果您將多個用戶端執行個體連接到 AWS CloudHSM 叢集，在某個用戶端執行個體的金鑰存放區上匯入憑證不會自動讓憑證在其他用戶端執行個體上可用。您必須在每個用戶端執行個體上匯入憑證。

# 使用 keytool 從 AWS CloudHSM 金鑰存放區刪除憑證
<a name="delete_cert_using_keytool"></a>

下列命令顯示如何從 Java keytool 金鑰存放區刪除 AWS CloudHSM 憑證的範例。

```
keytool -delete -alias mydomain -keystore \
        -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

如果您將多個用戶端執行個體連接到 AWS CloudHSM 叢集，刪除某個用戶端執行個體金鑰存放區上的憑證不會自動從其他用戶端執行個體移除憑證。您必須在每個用戶端執行個體上刪除憑證。

# 使用 AWS CloudHSM keytool 將工作憑證匯入金鑰存放區
<a name="import_working_cert_using_keytool"></a>

憑證簽署要求 (CSR) 簽署後，您就可以將其匯入 AWS CloudHSM 金鑰存放區，並與適當的金鑰對建立關聯。以下是命令範例。

```
keytool -importcert -noprompt -alias <key pair label> \
        -file example_certificate.crt \
        -keystore example_keystore.store
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

別名應為金鑰存放區中具有關聯憑證的金鑰對。如果金鑰是非 Keytool 產生，或是在不同的用戶端執行個體上產生，您就必須先將金鑰和憑證中繼資料匯入金鑰存放區。如需匯入憑證中繼資料的指示，請參閱[使用金鑰存放區註冊既有 AWS CloudHSM 金鑰](register-pre-existing-keys-with-keystore.md)中的程式碼範例。

憑證鏈必須是可驗證的。如果您無法驗證憑證，則可能需要將簽署 (憑證授權單位) 憑證匯入金鑰存放區，以便驗證該鏈結。

# AWS CloudHSM 使用 keytool 從 匯出憑證
<a name="export_cert_using_keytool"></a>

下列範例會產生二進位 X.509 格式的憑證。若要從 匯出人類可讀憑證 AWS CloudHSM，請將 `-rfc`新增至 `-exportcert`命令。

```
keytool -exportcert -alias <key pair label> \
        -file example_exported_certificate.crt \
        -keystore example_keystore.store \
        -storetype CLOUDHSM \
        -J-classpath '-J/opt/cloudhsm/java/*' \
        -J-Djava.library.path=/opt/cloudhsm/lib/
```

# 使用用戶端 SDK 3 搭配 Jarsigner 使用 AWS CloudHSM 金鑰存放區
<a name="using_keystore_jarsigner"></a>

Jarsigner 是常用的命令列公用程式，用於使用安全地存放在硬體安全模組 (HSM) 上的金鑰簽署 JAR 檔案。Jarsigner 的完整教學課程超出 AWS CloudHSM 文件的範圍。本節說明您應該用來簽署和驗證簽章的 Jarsigner 參數，並以 AWS CloudHSM 做為透過 AWS CloudHSM 金鑰存放區的信任根。

# 使用 Jarsigner 設定 AWS CloudHSM 金鑰和憑證
<a name="jarsigner_set_up_certificates"></a>

在使用 Jarsigner 簽署 AWS CloudHSM JAR 檔案之前，請確定您已設定或完成下列步驟：

1. 遵循 [AWS CloudHSM 金鑰存放區必要條件](keystore-prerequisites.md)中的指引。

1. 設定您的簽署金鑰以及關聯的憑證和憑證鏈，這些憑證和憑證鏈應存放在目前伺服器或用戶端執行個體的 AWS CloudHSM 金鑰存放區中。在 上建立金鑰， AWS CloudHSM 然後將相關聯的中繼資料匯入至您的 AWS CloudHSM 金鑰存放區。使用[向金鑰存放區註冊預先存在的 AWS CloudHSM 金鑰](register-pre-existing-keys-with-keystore.md)中的程式碼範例，將中繼資料匯入金鑰存放區。如果您要使用 Keytool 設定金鑰和憑證，請參閱 [使用 keytool 建立新的 AWS CloudHSM 金鑰](create_key_keytool.md)。如果您使用多個用戶端執行個體來簽署 JAR，請建立金鑰並匯入憑證鏈。然後將生成的金鑰存放區文件複製到每個用戶端執行個體。如果您經常產生新的金鑰，您可能會發現將憑證個別匯入至每個用戶端執行個體更容易。

1. 整個憑證鏈必須是可驗證的。若要驗證憑證鏈，您可能需要將 CA 憑證和中繼憑證新增至 AWS CloudHSM 金鑰存放區。如需使用 Java 程式碼驗證憑證鏈的說明[，請參閱使用 AWS CloudHSM 和 Jarsigner 簽署 JAR 檔案](jarsigner_sign_jar_using_hsm_jarsigner.md)中的程式碼片段。如果需要，您也可以使用 Keytool 匯入憑證。如需使用 Keytool 的指示，請參閱[使用 Keytool 將中繼憑證和根憑證匯入 AWS CloudHSM Key Store](import_cert_using_keytool.md)。

# 使用 AWS CloudHSM 和 Jarsigner 簽署 JAR 檔案
<a name="jarsigner_sign_jar_using_hsm_jarsigner"></a>

使用以下命令，使用 AWS CloudHSM 和 jarsigner 簽署 JAR 檔案：

```
jarsigner -keystore example_keystore.store \
        -signedjar signthisclass_signed.jar \
        -sigalg sha512withrsa \
        -storetype CloudHSM \
        -J-classpath '-J/opt/cloudhsm/java/*:/usr/lib/jvm/java-1.8.0/lib/tools.jar' \
        -J-Djava.library.path=/opt/cloudhsm/lib \
        signthisclass.jar <key pair label>
```

使用以下命令來驗證已簽署的 JAR：

```
jarsigner -verify \
        -keystore example_keystore.store \
        -sigalg sha512withrsa \
        -storetype CloudHSM \
        -J-classpath '-J/opt/cloudhsm/java/*:/usr/lib/jvm/java-1.8.0/lib/tools.jar' \
        -J-Djava.library.path=/opt/cloudhsm/lib \
        signthisclass_signed.jar <key pair label>
```

# 使用用戶端 SDK 3 AWS CloudHSM 整合 Java Keytool 和 Jarsigner 的已知問題
<a name="known-issues-keytool-jarsigner"></a>

下列清單提供使用用戶端 SDK 3 與 AWS CloudHSM 和 Java Keytool 和 Jarsigner 整合的目前已知問題清單。
+ 使用 Keytool 產生金鑰時，提供商組態中的第一個提供商不能是 CaviumProvider。
+ 使用 Keytool 產生金鑰時，系統會使用安全性組態檔案中的第一個 (受支援的) 提供商來產生金鑰。這通常是軟體提供商。產生的金鑰接著會獲得別名，並在金鑰新增程序期間以持久性 （金鑰） 金鑰的形式匯入 AWS CloudHSM HSM。
+  搭配 AWS CloudHSM 金鑰存放區使用 keytool 時，請勿在命令列上指定 `-providerclass`、 `-providerName`或 `-providerpath`選項。按照[金鑰存放區先決條件](keystore-prerequisites.md)中的說明，在安全提供商檔案中指定這些選項。
+ 當透過 Keytool 和 Narsigner 使用不可擷取的 EC 金鑰時，SunEC 提供商必須從 java.security 檔案的提供商清單中移除/停用。如果您透過 keytool 和 Jarsigner 使用可擷取的 EC 金鑰，提供者會從 AWS CloudHSM HSM 匯出金鑰位元，並在本機使用金鑰進行簽署操作。我們不建議您搭配 Keytool 或 Jarsigner 使用可匯出的金鑰。

# 向金鑰存放區註冊預先存在的 AWS CloudHSM 金鑰
<a name="register-pre-existing-keys-with-keystore"></a>

為了最大限度地提高屬性和標籤的安全性和靈活性，我們建議您使用 [key\$1mgmt\$1util](generate-keys.md) 產生 AWS CloudHSM 簽署金鑰。您也可以使用 Java 應用程式來產生 AWS CloudHSM中的金鑰。

下一節提供程式碼範例，示範如何在 HSM 上產生新的金鑰對，並使用匯入至 AWS CloudHSM 金鑰存放區的現有金鑰進行註冊。匯入的金鑰可以與第三方工具 (如 Keytool 和 Jarsigner) 搭配使用。

若要使用既有的金鑰，請修改程式碼範例以依標籤查詢金鑰，而不是產生新金鑰。依標籤尋找金鑰的範例程式碼可在 GitHub 的 [KeyUtilitiesRunner.java 範例](https://github.com/aws-samples/aws-cloudhsm-jce-examples/blob/master/src/main/java/com/amazonaws/cloudhsm/examples/KeyUtilitiesRunner.java)中取得。

**重要**  
 AWS CloudHSM 向本機金鑰存放區註冊存放在 的金鑰不會匯出金鑰。註冊金鑰時，金鑰存放區會註冊金鑰的別名 (或標籤)，並建立本機儲存憑證物件與 AWS CloudHSM金鑰對之間的關聯。只要金鑰對建立為不可匯出，金鑰位元就不會離開 HSM。

```
                      	
                      	
                      	//
 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
 // software and associated documentation files (the "Software"), to deal in the Software
 // without restriction, including without limitation the rights to use, copy, modify,
 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so.
 //
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
package com.amazonaws.cloudhsm.examples;

import com.cavium.key.CaviumKey;
import com.cavium.key.parameter.CaviumAESKeyGenParameterSpec;
import com.cavium.key.parameter.CaviumRSAKeyGenParameterSpec;
import com.cavium.asn1.Encoder;
import com.cavium.cfm2.Util;

import javax.crypto.KeyGenerator;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

import java.math.BigInteger;

import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.Entry;

import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;

//
// KeyStoreExampleRunner demonstrates how to load a keystore, and associate a certificate with a
// key in that keystore.
//
// This example relies on implicit credentials, so you must setup your environment correctly.
//
// https://docs.aws.amazon.com/cloudhsm/latest/userguide/java-library-install.html#java-library-credentials
//

public class KeyStoreExampleRunner {

     private static byte[] COMMON_NAME_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x03 };
     private static byte[] COUNTRY_NAME_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x06 };
     private static byte[] LOCALITY_NAME_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x07 };
     private static byte[] STATE_OR_PROVINCE_NAME_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x08 };
     private static byte[] ORGANIZATION_NAME_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x0A };
     private static byte[] ORGANIZATION_UNIT_OID = new byte[] { (byte) 0x55, (byte) 0x04, (byte) 0x0B };

     private static String helpString = "KeyStoreExampleRunner%n" +
            "This sample demonstrates how to load and store keys using a keystore.%n%n" +
            "Options%n" +
            "\t--help\t\t\tDisplay this message.%n" +
            "\t--store <filename>\t\tPath of the keystore.%n" +
            "\t--password <password>\t\tPassword for the keystore (not your CU password).%n" +
            "\t--label <label>\t\t\tLabel to store the key and certificate under.%n" +
            "\t--list\t\t\tList all the keys in the keystore.%n%n";

    public static void main(String[] args) throws Exception {
        Security.addProvider(new com.cavium.provider.CaviumProvider());
        KeyStore keyStore = KeyStore.getInstance("CloudHSM");

        String keystoreFile = null;
        String password = null;
        String label = null;
        boolean list = false;
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            switch (args[i]) {
                case "--store":
                    keystoreFile = args[++i];
                    break;
                case "--password":
                    password = args[++i];
                    break;
                case "--label":
                    label = args[++i];
                    break;
                case "--list":
                    list = true;
                    break;
                case "--help":
                    help();
                    return;
            }
        }

        if (null == keystoreFile || null == password) {
            help();
            return;
        }

        if (list) {
            listKeys(keystoreFile, password);
            return;
        }

        if (null == label) {
            label = "Keystore Example Keypair";
        }

        //
        // This call to keyStore.load() will open the pkcs12 keystore with the supplied
        // password and connect to the HSM. The CU credentials must be specified using
        // standard CloudHSM login methods.
        //
        try {
            FileInputStream instream = new FileInputStream(keystoreFile);
            keyStore.load(instream, password.toCharArray());
        } catch (FileNotFoundException ex) {
            System.err.println("Keystore not found, loading an empty store");
            keyStore.load(null, null);
        }

        PasswordProtection passwd = new PasswordProtection(password.toCharArray());
        System.out.println("Searching for example key and certificate...");

        PrivateKeyEntry keyEntry = (PrivateKeyEntry) keyStore.getEntry(label, passwd);
        if (null == keyEntry) {
            //
            // No entry was found, so we need to create a key pair and associate a certificate.
            // The private key will get the label passed on the command line. The keystore alias
            // needs to be the same as the private key label. The public key will have ":public"
            // appended to it. The alias used in the keystore will We associate the certificate
            // with the private key.
            //
            System.out.println("No entry found, creating...");
            KeyPair kp = generateRSAKeyPair(2048, label + ":public", label);
            System.out.printf("Created a key pair with the handles %d/%d%n", ((CaviumKey) kp.getPrivate()).getHandle(), ((CaviumKey) kp.getPublic()).getHandle());

            //
            // Generate a certificate and associate the chain with the private key.
            //
            Certificate self_signed_cert = generateCert(kp);
            Certificate[] chain = new Certificate[1];
            chain[0] = self_signed_cert;
            PrivateKeyEntry entry = new PrivateKeyEntry(kp.getPrivate(), chain);

            //
            // Set the entry using the label as the alias and save the store.
            // The alias must match the private key label.
            //
            keyStore.setEntry(label, entry, passwd);

            FileOutputStream outstream = new FileOutputStream(keystoreFile);
            keyStore.store(outstream, password.toCharArray());
            outstream.close();

            keyEntry = (PrivateKeyEntry) keyStore.getEntry(label, passwd);
        }

        long handle = ((CaviumKey) keyEntry.getPrivateKey()).getHandle();
        String name = keyEntry.getCertificate().toString();
        System.out.printf("Found private key %d with certificate %s%n", handle, name);
    }

    private static void help() {
        System.out.println(helpString);
    }

    //
    // Generate a non-extractable / non-persistent RSA keypair.
    // This method allows us to specify the public and private labels, which
    // will make KeyStore aliases easier to understand.
    //
    public static KeyPair generateRSAKeyPair(int keySizeInBits, String publicLabel, String privateLabel)
            throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {

        boolean isExtractable = false;
        boolean isPersistent = false;
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("rsa", "Cavium");
        CaviumRSAKeyGenParameterSpec spec = new CaviumRSAKeyGenParameterSpec(keySizeInBits, new BigInteger("65537"), publicLabel, privateLabel, isExtractable, isPersistent);

        keyPairGen.initialize(spec);

        return keyPairGen.generateKeyPair();
    }

    //
    // Generate a certificate signed by a given keypair.
    //
    private static Certificate generateCert(KeyPair kp) throws CertificateException {
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        PublicKey publicKey = kp.getPublic();
        PrivateKey privateKey = kp.getPrivate();
        byte[] version = Encoder.encodeConstructed((byte) 0, Encoder.encodePositiveBigInteger(new BigInteger("2"))); // version 1
        byte[] serialNo = Encoder.encodePositiveBigInteger(new BigInteger(1, Util.computeKCV(publicKey.getEncoded())));

        // Use the SHA512 OID and algorithm.
        byte[] signatureOid = new byte[] {
            (byte) 0x2A, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xF7, (byte) 0x0D, (byte) 0x01, (byte) 0x01, (byte) 0x0D };
        String sigAlgoName = "SHA512WithRSA";

         byte[] signatureId = Encoder.encodeSequence(
                                         Encoder.encodeOid(signatureOid),
                                         Encoder.encodeNull());

         byte[] issuer = Encoder.encodeSequence(
                                     encodeName(COUNTRY_NAME_OID, "<Country>"),
                                     encodeName(STATE_OR_PROVINCE_NAME_OID, "<State>"),
                                     encodeName(LOCALITY_NAME_OID, "<City>"),
                                     encodeName(ORGANIZATION_NAME_OID, "<Organization>"),
                                     encodeName(ORGANIZATION_UNIT_OID, "<Unit>"),
                                     encodeName(COMMON_NAME_OID, "<CN>")
                                 );

         Calendar c = Calendar.getInstance();
         c.add(Calendar.DAY_OF_YEAR, -1);
         Date notBefore = c.getTime();
         c.add(Calendar.YEAR, 1);
         Date notAfter = c.getTime();
         byte[] validity = Encoder.encodeSequence(
                                         Encoder.encodeUTCTime(notBefore),
                                         Encoder.encodeUTCTime(notAfter)
                                     );
         byte[] key = publicKey.getEncoded();

         byte[] certificate = Encoder.encodeSequence(
                                         version,
                                         serialNo,
                                         signatureId,
                                         issuer,
                                         validity,
                                         issuer,
                                         key);
         Signature sig;
         byte[] signature = null;
         try {
             sig = Signature.getInstance(sigAlgoName, "Cavium");
             sig.initSign(privateKey);
             sig.update(certificate);
             signature = Encoder.encodeBitstring(sig.sign());

         } catch (Exception e) {
             System.err.println(e.getMessage());
             return null;
         }

         byte [] x509 = Encoder.encodeSequence(
                         certificate,
                         signatureId,
                         signature
                         );
         return cf.generateCertificate(new ByteArrayInputStream(x509));
    }

     //
     // Simple OID encoder.
     // Encode a value with OID in ASN.1 format
     //
     private static byte[] encodeName(byte[] nameOid, String value) {
         byte[] name = null;
         name = Encoder.encodeSet(
                     Encoder.encodeSequence(
                             Encoder.encodeOid(nameOid),
                             Encoder.encodePrintableString(value)
                     )
                 );
         return name;
     }

    //
    // List all the keys in the keystore.
    //
    private static void listKeys(String keystoreFile, String password) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("CloudHSM");

        try {
            FileInputStream instream = new FileInputStream(keystoreFile);
            keyStore.load(instream, password.toCharArray());
        } catch (FileNotFoundException ex) {
            System.err.println("Keystore not found, loading an empty store");
            keyStore.load(null, null);
        }

        for(Enumeration<String> entry = keyStore.aliases(); entry.hasMoreElements();) {
            System.out.println(entry.nextElement());
        }
    }

}
```