--- title: "Java 연동" slug: "java-quickstart-guide" updated: 2026-10-22T09:08:02Z published: 2026-10-22T09:08:02Z canonical: "guide-gov.ncloud-docs.com/java-quickstart-guide" --- > ## Documentation Index > Fetch the complete documentation index at: https://guide-gov.ncloud-docs.com/llms.txt > Use this file to discover all available pages before exploring further. # Java 연동 Hardware Security Module(HSM) 상품은 Thales Luna HSM을 기반으로 하며, Java Security Provider(LunaProvider)를 통해 표준 JCE(Java Cryptography Extension) API로 사용할 수 있습니다. 이 문서에서는 Java 애플리케이션에서 LunaProvider로 HSM 파티션에 접속해 AES/RSA 같은 기본 알고리즘으로 키 생성, 암호화·복호화, 서명·검증을 수행하는 최소 예제를 안내합니다. 참고 CLI로 먼저 만져보려면 [CLI 도구 활용 (lunacm/ckdemo/multitoken)](/docs/hsm-cli-tools-guide)을 참고해 주십시오. LunaClient를 설치하면 `KeyStoreLunaDemo`, `SignatureECDSADemo` 같은 JSP(Java Security Provider) 샘플 코드도 함께 설치됩니다(`lunajspsamples` 패키지, 보통 `/usr/safenet/lunaclient/jsp/samples` 아래). 이 문서에서 다루지 않는 사용법이 필요하면 먼저 이 샘플들을 찾아보십시오. ## 지원 환경 지원 환경을 설명합니다. ### 전제 조건 연동을 시작하기 전, 다음의 전제 조건을 준수해 주십시오. - HSM 클라이언트 서버에 Java가 설치 및 구동되고 있음 - HSM 클라이언트 서버에 Luna HSM Client(LunaClient) 10.9.3이 설치되어 있음 — [LunaClient 설치 가이드](/docs/hsm-client) - HSM 클라이언트 서버 인스턴스와 HSM 파티션 간에 연결 생성 완료 - HSM 파티션 초기 설정 완료 ### 지원 버전 | 구성 요소 | 버전 | 비고 | | --- | --- | --- | | HSM 클라이언트(Luna Client) | 10.9.3 | [LunaClient 설치 가이드](/docs/hsm-client) | | JDK | 8 이상 (이 가이드는 17 기준으로 작성) | 이 문서의 예제는 표준 JCE API(`KeyGenerator`, `KeyPairGenerator`, `Cipher`, `Signature`)만 사용하므로 JDK 8 이상이면 동작합니다. 다만 이 문서의 컴파일·실행 명령과 출력 결과는 JDK 17 기준으로 작성 및 확인했습니다. | ## Luna HSM을 위한 Java 설정 Java 애플리케이션에서 LunaProvider를 사용하려면 `java.security` 설정 파일에 LunaProvider를 등록해야 합니다. ``` security.provider.1=SUN security.provider.2=com.safenetinc.luna.provider.LunaProvider security.provider.3=SunRsaSign security.provider.4=SunEC security.provider.5=SunJSSE security.provider.6=SunJCE security.provider.7=SunJGSS security.provider.8=SunSASL security.provider.9=XMLDSig security.provider.10=SunPCSC security.provider.11=JdkLDAP security.provider.12=JdkSASL security.provider.13=SunPKCS11 ``` 또는 애플리케이션 코드에서 직접 Provider를 추가해도 됩니다. 아래 예제들은 모두 이 방식을 사용합니다. ``` Provider lunaProvider = new LunaProvider(); Security.addProvider(lunaProvider); ``` ### 파티션 로그인 Provider를 추가한 것만으로는 HSM 파티션에 로그인되지 않습니다. 아래처럼 `KeyStore`를 로드하는 것이 곧 로그인 절차이며, 아래 모든 예제 코드에 포함되어 있습니다. ``` KeyStore luna = KeyStore.getInstance("Luna"); luna.load(new ByteArrayInputStream(("slot:" + SLOT).getBytes()), PARTITION_PASSWORD.toCharArray()); ``` 슬롯 번호는 `lunacm:> slot list`로 확인할 수 있습니다. ([CLI 도구 활용](/docs/hsm-cli-tools-guide) 참고) ### `javac`/`java`로 컴파일·실행하기 ``` # 1. 컴파일 — LunaProvider.jar를 클래스패스에 추가 javac -cp /usr/safenet/lunaclient/jsp/lib/LunaProvider.jar KeyGenExample.java # 2. 실행 — 컴파일된 클래스(.)와 LunaProvider.jar를 classpath에 넣고, # -Djava.library.path는 java 옵션으로 직접 지정 java -cp .:/usr/safenet/lunaclient/jsp/lib/LunaProvider.jar \ -Djava.library.path=/usr/safenet/lunaclient/jsp/lib/ \ KeyGenExample ``` 다른 예제도 클래스 이름만 바꾸면 동일합니다. ## 키 생성 `KeyGenExample.java` 파일을 아래 내용으로 생성하십시오. AES 대칭키와 RSA 키쌍을 모두 HSM 파티션 내부에서 생성하며, 개인키/비밀키는 파티션 밖으로 반출되지 않습니다. ``` import java.io.ByteArrayInputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.Provider; import java.security.Security; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import com.safenetinc.luna.provider.LunaProvider; public class KeyGenExample { // 실제 파티션의 슬롯 번호와 비밀번호로 교체하십시오. private static final int SLOT = 0; private static final String PARTITION_PASSWORD = "{partition-password}"; public static void main(String[] args) throws Exception { Provider lunaProvider = new LunaProvider(); Security.addProvider(lunaProvider); // 파티션 로그인 (KeyStore 로드 = 로그인) KeyStore luna = KeyStore.getInstance("Luna"); luna.load(new ByteArrayInputStream(("slot:" + SLOT).getBytes()), PARTITION_PASSWORD.toCharArray()); // AES-256 대칭키 생성 KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES", lunaProvider); aesKeyGen.init(256); SecretKey aesKey = aesKeyGen.generateKey(); // RSA-2048 키쌍 생성 KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA", lunaProvider); rsaKeyGen.initialize(2048); KeyPair rsaKeyPair = rsaKeyGen.generateKeyPair(); System.out.println("AES key algorithm: " + aesKey.getAlgorithm()); System.out.println("RSA public key algorithm: " + rsaKeyPair.getPublic().getAlgorithm()); } } ``` [컴파일·실행](/docs/java-quickstart-guide#javac/java%EB%A1%9C%EC%BB%B4%ED%8C%8C%EC%9D%BC%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0) 후 다음과 같은 결과가 출력되면 정상입니다. ``` AES key algorithm: AES RSA public key algorithm: RSA ``` ### 생성한 키를 다음 실행에서도 다시 쓰려면 위 예제처럼 `KeyGenerator`/`KeyPairGenerator`로 생성만 한 키는 **세션(session) 객체**로 만들어집니다. 즉 프로그램(JVM 세션)이 종료되면 HSM에서도 함께 사라지므로, 다음번 실행에서 같은 키를 다시 쓸 수 없습니다. 같은 키를 다음 실행에서도 찾아 쓰려면, alias(이름)를 붙여 `KeyStore`에 저장해야 합니다. 파일 기반 `KeyStore`(JKS 등)와 달리 Luna의 `KeyStore`는 `setKeyEntry`/`setEntry` 호출 즉시 HSM에 **영구(token) 객체**로 기록되며, alias는 그대로 해당 객체의 PKCS#11 `CKA_LABEL`이 됩니다. 별도로 `store()`를 호출할 필요는 없습니다. ``` // 저장 (alias: "my-aes-key") luna.setKeyEntry("my-aes-key", aesKey, null, null); // 다음 실행(또는 다른 프로세스)에서 재사용 — 같은 파티션에 로그인한 뒤 SecretKey reloadedKey = (SecretKey) luna.getKey("my-aes-key", null); ``` 참고 `setKeyEntry(alias, key, password, chain)`의 `chain` 인자는 `PrivateKey`를 저장할 때만 요구되는 표준 JCA `KeyStore` API 제약입니다. AES 같은 `SecretKey`는 `chain`을 `null`로 두면 되지만, RSA 개인키를 영구 저장하려면 인증서 체인이 있어야 합니다. `SignatureECDSADemo`, `KeyStoreLunaDemo` 같은 JSP 샘플 코드를 참고해 주십시오. ## AES로 암호화·복호화 수행 `EncryptExample.java` 파일을 아래 내용으로 생성하십시오. ``` import java.io.ByteArrayInputStream; import java.security.KeyStore; import java.security.Provider; import java.security.Security; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import com.safenetinc.luna.provider.LunaProvider; public class EncryptExample { // 실제 파티션의 슬롯 번호와 비밀번호로 교체하십시오. private static final int SLOT = 0; private static final String PARTITION_PASSWORD = "{partition-password}"; public static void main(String[] args) throws Exception { Provider lunaProvider = new LunaProvider(); Security.addProvider(lunaProvider); // 파티션 로그인 KeyStore luna = KeyStore.getInstance("Luna"); luna.load(new ByteArrayInputStream(("slot:" + SLOT).getBytes()), PARTITION_PASSWORD.toCharArray()); KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES", lunaProvider); aesKeyGen.init(256); SecretKey aesKey = aesKeyGen.generateKey(); byte[] plaintext = "HSM Java Quickstart test data".getBytes("UTF-8"); // 암호화 Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", lunaProvider); encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey); byte[] ciphertext = encryptCipher.doFinal(plaintext); IvParameterSpec iv = new IvParameterSpec(encryptCipher.getIV()); // 복호화 Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", lunaProvider); decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, iv); byte[] decrypted = decryptCipher.doFinal(ciphertext); System.out.println("Decryption matches original: " + Arrays.equals(plaintext, decrypted)); } } ``` [컴파일·실행](/docs/java-quickstart-guide#javac/java%EB%A1%9C%EC%BB%B4%ED%8C%8C%EC%9D%BC%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0) 후 다음과 같은 결과가 출력되면 정상입니다. ``` Decryption matches original: true ``` ## RSA로 전자서명 생성 및 검증 `SignatureExample.java` 파일을 아래 내용으로 생성하십시오. ``` import java.io.ByteArrayInputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.Provider; import java.security.Security; import java.security.Signature; import com.safenetinc.luna.provider.LunaProvider; public class SignatureExample { // 실제 파티션의 슬롯 번호와 비밀번호로 교체하십시오. private static final int SLOT = 0; private static final String PARTITION_PASSWORD = "{partition-password}"; public static void main(String[] args) throws Exception { Provider lunaProvider = new LunaProvider(); Security.addProvider(lunaProvider); // 파티션 로그인 KeyStore luna = KeyStore.getInstance("Luna"); luna.load(new ByteArrayInputStream(("slot:" + SLOT).getBytes()), PARTITION_PASSWORD.toCharArray()); KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA", lunaProvider); rsaKeyGen.initialize(2048); KeyPair rsaKeyPair = rsaKeyGen.generateKeyPair(); byte[] message = "HSM Java Quickstart signature test".getBytes("UTF-8"); // 서명 Signature signer = Signature.getInstance("SHA256withRSA", lunaProvider); signer.initSign(rsaKeyPair.getPrivate()); signer.update(message); byte[] signature = signer.sign(); // 검증 Signature verifier = Signature.getInstance("SHA256withRSA", lunaProvider); verifier.initVerify(rsaKeyPair.getPublic()); verifier.update(message); boolean verified = verifier.verify(signature); System.out.println("Signature verification result: " + verified); } } ``` [컴파일·실행](/docs/java-quickstart-guide#javac/java%EB%A1%9C%EC%BB%B4%ED%8C%8C%EC%9D%BC%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0) 후 다음과 같은 결과가 출력되면 정상입니다. ``` Signature verification result: true ``` ## 문제 해결 ### 로그인 실패 `com.safenetinc.luna.exception.LunaException: No logged in tokens available` 오류가 발생합니다. #### 원인 `SLOT`/`PARTITION_PASSWORD`를 실제 값으로 바꾸지 않았거나, 슬롯 번호나 비밀번호가 틀린 경우 발생합니다. #### 해결 방법 `lunacm:> slot list`로 슬롯 번호를, 파티션 비밀번호를 다시 확인하십시오. ### LunaProvider로드실패 `ClassNotFoundException: com.safenetinc.luna.provider.LunaProvider` 또는 `UnsatisfiedLinkError` 오류가 발생합니다. #### 원인 LunaClient가 설치되어 있지 않거나, `javac`/`java` 명령의 `-cp`/`-Djava.library.path` 경로가 실제 설치 경로(`/usr/safenet/lunaclient/jsp/lib/`)와 다른 경우 발생합니다. #### 해결 방법 LunaClient 설치 여부와 경로를 확인하십시오. ### 연결오류 파티션 로그인은 되는데 키 생성/암복호화 호출에서 응답이 없거나 연결 오류가 발생합니다. #### 원인 HSM 클라이언트 서버와 HSM 파티션 간의 네트워크 연결(Connection)이나 파티션 초기 설정이 완료되지 않은 경우 발생할 수 있습니다. #### 해결 방법 [전제 조건](/docs/java-quickstart-guide#%EC%A0%84%EC%A0%9C%EC%A1%B0%EA%B1%B4)의 HSM 커넥션 생성·파티션 초기 설정이 완료됐는지 확인하십시오. ## 다음 단계 - CLI 확인: [CLI 도구 활용 (lunacm/ckdemo/multitoken)](/docs/hsm-cli-tools-guide) - C++(Cryptoki) 사용: [C++(Cryptoki) 연동 Quickstart](/docs/cpp-quickstart-guide)