

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 클라이언트 SDK 3을 사용하여 Java Keytool 및 Jarsigner AWS CloudHSM 와 통합
<a name="keystore-third-party-tools"></a>

AWS CloudHSM 키 스토어는 `keytool` 및와 같은 타사 도구를 통해 하드웨어 보안 모듈(HSM)의 키와 연결된 인증서를 활용하는 특수 용도의 JCE 키 스토어입니다`jarsigner`. 인증서는 기밀이 아닌 공개 데이터이므로는 HSM에 인증서를 저장하지 AWS CloudHSM 않습니다. 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을 사용하여 Java Keytool 및 Jarsigner AWS CloudHSM 와 통합하기 위한 사전 조건
<a name="keystore-prerequisites"></a>

 AWS CloudHSM 키 스토어를 사용하려면 먼저 AWS CloudHSM JCE SDK를 초기화하고 구성해야 합니다. 이렇게 하려면 다음의 절차를 따르세요.

## 1단계: JCE 설치
<a name="prereq-step-one"></a>

 AWS CloudHSM 클라이언트 사전 조건을 포함하여 JCE를 설치하려면 [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>

Java CloudProvider 구성에서 JCE 공급자를 등록하려면 

1. 편집을 위해, Java 설치에서 java.security 구성 파일을 엽니다.

1. java.security 구성 파일에서 마지막 공급자로 `com.cavium.provider.CaviumProvider`를 추가합니다. 예를 들어, java.security 파일에 9개의 공급자가 있는 경우 섹션의 마지막 공급자로 다음 공급자를 추가합니다. Cavium 공급자를 더 높은 우선 순위로 추가하면 시스템의 성능에 부정적인 영향을 줄 수 있습니다.

   `security.provider.10=com.cavium.provider.CaviumProvider`
**참고**  
파워 유저는 보안 구성 파일을 업데이트하는 대신, keytool 사용 시 `-providerName`, `-providerclass` 및 `-providerpath` 명령줄 옵션을 지정하는 데 익숙할 수 있습니다. AWS CloudHSM 키 스토어로 키를 생성할 때 명령줄 옵션을 지정하려고 하면 오류가 발생합니다.

# 클라이언트 SDK 3을 사용하여 AWS CloudHSM keytool과 함께 키 스토어 사용
<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 키 도구 함수와 함께 사용해야 하는 특정 파라미터에 대해 설명합니다.

키 스토어와 함께 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에 대한 keystore 인수를 사용하여 이름(경로 포함)을 지정합니다. 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 키 스토어에 기존 키 등록의 코드 샘플에 나와 있습니다](register-pre-existing-keys-with-keystore.md). Keytool 외부에서 내보낼 수 없는 키를 생성한 다음 해당 인증서를 키 스토어로 가져오는 것이 좋습니다. keytool 및 jarsigner를 통해 추출 가능한 RSA 또는 EC 키를 사용하는 경우 공급자는에서 키를 내보낸 AWS CloudHSM 다음 서명 작업에 로컬로 키를 사용합니다.

CloudHSM 클러스터에 연결된 클라이언트 인스턴스가 여러 개 있는 경우, 한 클라이언트 인스턴스의 키 스토어에서 인증서를 가져오더라도 다른 클라이언트 인스턴스에서 자동으로 인증서를 사용할 수 없다는 점을 알아두십시오. 각 클라이언트 인스턴스에서 키 및 연결된 인증서를 등록하려면 [Keytool을 사용하여 CSR 생성](generate_csr_using_keytool.md)에 설명된 대로 Java 애플리케이션을 실행해야 합니다. 또는, 한 클라이언트에서 필요한 사항을 변경하고 결과 키 스토어 파일을 다른 모든 클라이언트 인스턴스에 복사할 수 있습니다.

**예제 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)의 목록을 찾을 수 있습니다.

# keytool을 사용하여 AWS CloudHSM 키 삭제
<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 Dynamic Engine](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 외부에서 생성된 키 페어를 사용하려면 키 및 인증서 메타데이터를 키 스토어로 가져와야 합니다. 키 스토어 데이터 가져오기에 대한 지침은 [Keytool을 사용하여 AWS CloudHSM 키 스토어로 중간 및 루트 인증서 가져오기를 참조하세요](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).

인증서 체인은 검증 가능해야 합니다. 인증서를 검증할 수 없는 경우, 체인을 검증할 수 있도록 서명(인증 기관) 인증서를 키 스토어로 가져와야 할 수 있습니다.

# keytool을 AWS CloudHSM 사용하여에서 인증서 내보내기
<a name="export_cert_using_keytool"></a>

다음 예에서는 이진수 X.509 형식으로 인증서를 생성합니다. 사람이 읽을 수 있는 인증서를 내보내려면 `-exportcert` 명령`-rfc`에를 AWS CloudHSM추가합니다.

```
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 설명서의 범위를 벗어납니다. 이 섹션에서는 AWS CloudHSM 키 스토어를 통해 신뢰의 루트 AWS CloudHSM 로를 사용하여 서명에 서명하고 확인하는 데 사용해야 하는 Jarsigner 파라미터에 대해 설명합니다.

# 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 키 스토어로 가져오기](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을 사용하는 경우 명령줄에 `-providerName``-providerclass`, 또는 `-providerpath` 옵션을 지정하지 마십시오. [키 스토어 사전 조건](keystore-prerequisites.md)에 설명된 대로 보안 공급자 파일에서 이러한 옵션을 지정합니다.
+ keytool 및 Jarsigner를 통해 추출할 수 없는 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());
        }
    }

}
```