--- title: "BYOK(키 주입) Java 예제" slug: "hsm-byok-java-guide" updated: 2026-10-22T09:08:02Z published: 2026-10-22T09:08:02Z canonical: "guide-gov.ncloud-docs.com/hsm-byok-java-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. # BYOK(키 주입) Java 예제 VPC 환경에서 이용 가능합니다. 이 문서는 보유하고 계신 RSA 공개키, RSA/EC 개인키, AES-256 대칭키를 NCP Hardware Security Module(HSM) 파티션에 주입(BYOK)하고, 주입한 키로 암호화·복호화와 서명·검증을 수행하는 절차를 Java(LunaProvider) 예제로 안내합니다. 주입 원리와 지원 범위는 [BYOK(키 주입) 개요](/docs/hsm-byok-overview)에서 먼저 확인해 주십시오. :::(info) (참고) 이 문서는 위에서부터 순서대로 실행하면 전 과정이 완료되도록 구성했습니다. `HsmByok.java`와 `KwpWrap.java`를 한 번 컴파일해 두고, 명령을 바꿔가며 실행하는 방식입니다. 1. [예제 프로그램 준비](/docs/hsm-byok-java-guide#%EC%98%88%EC%A0%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%A4%80%EB%B9%84) — 프로그램 작성·컴파일, 공통 환경변수 설정 2. [샘플 키 준비](/docs/hsm-byok-java-guide#%EC%83%98%ED%94%8C%ED%82%A4%EC%A4%80%EB%B9%84) — 주입해 볼 RSA/EC 키쌍과 AES 키 생성 (보유한 키가 있으면 생략). 애플리케이션이 쓰던 키 객체를 주입하려면 [BYOK(키 주입) 키 준비](/docs/hsm-byok-key-prep)를 참고 3. [1단계 — wrapping 키쌍 생성과 공개키 반출](/docs/hsm-byok-java-guide#1%EB%8B%A8%EA%B3%84wrapping%ED%82%A4%EC%8C%8D%EC%83%9D%EC%84%B1%EA%B3%BC%EA%B3%B5%EA%B0%9C%ED%82%A4%EB%B0%98%EC%B6%9C) 4. [AES-256 대칭키 주입](/docs/hsm-byok-java-guide#AES256%EB%8C%80%EC%B9%AD%ED%82%A4%EC%A3%BC%EC%9E%85) — 주입 후 암호화·복호화 왕복 확인 5. [RSA 키 주입](/docs/hsm-byok-java-guide#RSA%ED%82%A4%EC%A3%BC%EC%9E%85) — 개인키·공개키 주입 후 서명·검증, 암호화·복호화 왕복 확인 6. [EC 키 주입](/docs/hsm-byok-java-guide#EC%ED%82%A4%EC%A3%BC%EC%9E%85) — 개인키·공개키 주입 후 서명·검증 왕복 확인 7. [주입한 키 조회](/docs/hsm-byok-java-guide#%EC%A3%BC%EC%9E%85%ED%95%9C%ED%82%A4%EC%A1%B0%ED%9A%8C) / [정리](/docs/hsm-byok-java-guide#%EC%A0%95%EB%A6%AC) 특정 키 유형만 필요하시면 1~3을 수행한 뒤 해당 절로 건너뛰셔도 됩니다. 공개키만 주입한다면 wrapping 키쌍이 필요 없으므로 1을 수행한 뒤 [RSA 공개키 주입](/docs/hsm-byok-java-guide#RSA%EA%B3%B5%EA%B0%9C%ED%82%A4%EC%A3%BC%EC%9E%85) 또는 [EC 키 주입](/docs/hsm-byok-java-guide#EC%ED%82%A4%EC%A3%BC%EC%9E%85%EB%AA%85%EB%A0%B9)의 공개키 부분으로 건너뛰십시오. **이 문서의 절차는 접근이 통제된 서버 한 대에서 실행하는 것을 전제로 합니다.** 키를 보유한 쪽의 작업과 HSM 클라이언트 서버의 작업을 같은 서버, 같은 셸에서 순서대로 실행하며, `TS` 값과 컴파일한 클래스 파일을 그대로 이어서 사용합니다. 각 명령 블록 앞에는 운영 환경에서 그 작업이 **어느 쪽에 속하는지**를 실행 위치로 표시했습니다. 운영 환경에서 두 시스템을 분리한다면, "키 보유 측 작업"으로 표시된 단계(OpenSSL 감싸기, `KwpWrap` 실행)는 키를 보유한 시스템에서 수행하고 HSM 클라이언트 서버로는 감싼 결과물(`.b64`)과 공개키(`.pub.pem`)만 전달하십시오. 평문 키 파일은 HSM 클라이언트 서버에 두지 마십시오. ::: ## 지원 환경 ### 전제 조건 연동을 시작하기 전, 다음의 전제 조건을 준수해 주십시오. - HSM 클라이언트 서버에 Java가 설치 및 구동되고 있음 - HSM 클라이언트 서버에 Luna HSM Client(LunaClient)가 설치되어 있음 — [LunaClient 설치 가이드](/docs/hsm-client) - HSM 클라이언트 서버 인스턴스와 HSM 파티션 간에 연결 생성 완료 - HSM 파티션 초기 설정 완료 - 파티션 정책 확인 완료 — [파티션 정책 요구사항](/docs/hsm-byok-overview#%ED%8C%8C%ED%8B%B0%EC%85%98%EC%A0%95%EC%B1%85%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD) ### 지원 버전 HSM 클라이언트 서버에 Luna HSM Client(LunaClient)를 설치하십시오. 설치 방법은 [LunaClient 설치 가이드](/docs/hsm-client)를 참고하십시오. 이 문서의 절차는 Linux 서버의 bash를 기준으로 작성했으며, LunaClient 10.9.3에서 확인했습니다. Java는 **JDK 17 이상**이 필요합니다. 개인키를 감쌀 때 사용하는 `AES/KWP/NoPadding` 변환이 JDK 17부터 표준 제공되기 때문입니다. 이 문서의 예제는 OpenJDK 17에서 확인했습니다. 서버에 여러 JDK가 설치되어 있으면 기본 `java`가 다른 버전을 가리킬 수 있으므로 `java -version`과 `javac -version`으로 먼저 확인하십시오. 17이 아니라면 이후 명령의 `java`/`javac`를 JDK 17 설치 경로의 것(예: `/usr/lib/jvm/java-17-openjdk/bin/java`)으로 바꾸어 실행하십시오. 키를 보유한 쪽에서 wrapped blob을 만들 때 OpenSSL을 사용합니다. ## 예제 프로그램 준비 ### `HsmByok.java` 작성 `HsmByok.java` 파일을 아래 내용으로 생성하십시오. 파티션에 로그인해 키 생성, 주입, 조회, 연산, 삭제를 명령으로 구분해 수행합니다. 코드가 길어 접어 두었습니다. 펼쳐서 전체 내용을 복사해 사용하십시오. 명령별로 어떤 메서드가 무엇을 하는지는 아래 [코드 구성](/docs/hsm-byok-java-guide#HsmByok%EC%BD%94%EB%93%9C%EA%B5%AC%EC%84%B1)에서 먼저 확인할 수 있습니다. **HsmByok.java 전체 코드 펼치기** ```java import com.safenetinc.luna.LunaAPI; import com.safenetinc.luna.LunaSlotManager; import com.safenetinc.luna.LunaTokenObject; import com.safenetinc.luna.attributes.LunaPkcs11Attributes; import com.safenetinc.luna.provider.LunaProvider; import com.safenetinc.luna.provider.key.LunaKey; import com.safenetinc.luna.provider.param.LunaPkcs11AttributesParameterSpec; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import java.util.Base64; import java.util.LinkedHashMap; import java.util.Map; public final class HsmByok { private static final String PROVIDER = "LunaProvider"; private static final String OAEP = "RSA/ECB/OAEPWithSHA256AndMGF1Padding"; private final int slot; private HsmByok(int slot, String pin) { Provider provider = new LunaProvider(); if (Security.getProvider(provider.getName()) == null) { Security.addProvider(provider); } this.slot = slot; LunaSlotManager slots = LunaSlotManager.getInstance(); slots.setDefaultSlot(slot); // 파티션 로그인 if (!slots.login(slot, pin)) { throw new IllegalStateException("Partition login failed: slot=" + slot); } } public static void main(String[] args) throws Exception { if (args.length == 0 || "help".equals(args[0])) { usage(); return; } String command = args[0]; Map opt = parseArgs(Arrays.copyOfRange(args, 1, args.length)); HsmByok app = new HsmByok(Integer.parseInt(required(opt, "--slot")), required(opt, "--pin")); switch (command) { case "show" -> app.show(opt); case "gen-rsa-wrap" -> app.genRsaWrap(opt); case "import-public" -> app.importPublic(opt); case "unwrap-aes-rsa-oaep" -> app.unwrapAesRsaOaep(opt); case "unwrap-key-aes-kwp" -> app.unwrapKeyAesKwp(opt); case "aes-cbc-roundtrip" -> app.aesCbcRoundtrip(opt); case "rsa-oaep-encrypt" -> app.rsaOaepEncrypt(opt); case "rsa-oaep-decrypt" -> app.rsaOaepDecrypt(opt); case "sign" -> app.sign(opt); case "verify" -> app.verify(opt); case "destroy" -> app.destroy(opt); default -> { System.err.println("Unknown command: " + command); usage(); System.exit(2); } } } private static void usage() { System.out.println(""" HsmByok - common options: --slot --pin show --label