Java SDK
kr.signox:signox-sdk — Java(Spring 서버·데스크탑)용 공식 SDK입니다. Java 8 이상에서 동작합니다.
- 모든 서버 응답을 제품 Ed25519 공개키로 서명검증한 뒤에만 신뢰합니다(위변조·재생 방어).
- 기기 식별자(hwid)는 SDK가 내부에서 자동 생성합니다 — 호출자가 다룰 필요가 없습니다.
- 판정은 예외가 아니라
LicenseResult코드로 전달됩니다. 네트워크 실패와 무효 판정은 코드로 구분됩니다.
설치 (Maven)
섹션 제목: “설치 (Maven)”<dependency> <groupId>kr.signox</groupId> <artifactId>signox-sdk</artifactId> <version>0.1.0</version></dependency>런타임 의존성은 BouncyCastle bcprov-jdk18on 하나뿐입니다(MIT, 전이 의존 0). Java 8~14는 JDK에 Ed25519가 없어 필수이며, JDK 15 이상에서도 동일 경로로 동작합니다.
빠른 시작
섹션 제목: “빠른 시작”import kr.signox.sdk.*;
// 제품 공개키(SPKI PEM)는 반드시 코드에 임베드하세요 (아래 "공개키 임베드" 참고)String productPublicKey = "-----BEGIN PUBLIC KEY-----\n....\n-----END PUBLIC KEY-----\n";
SignoxClient client = new SignoxClient( SignoxClient.builder(productPublicKey) .baseUrl("https://api.signox.kr") // 기본값 .timeoutMs(10_000) // 기본값 .build());
// 1) 디바이스 활성화 (최초 1회) — 멱등LicenseResult activated = client.activate("S4K2MB-7IWQ3F-...", ActivateOptions.builder().name("홍길동의 노트북").build());if (!activated.isValid()) { System.out.println("활성화 실패: " + activated.getCode());}
// 2) 라이센스 검증 (앱 시작·주기적)LicenseResult r = client.validate("S4K2MB-7IWQ3F-...");if (r.isValid()) { boolean pro = r.getFeatureBool("pro_export", false); long seats = r.getFeatureInt("max_seats", 1); // ... 기능 활성화} else if (r.getCode() == ValidationCode.NETWORK_ERROR) { // 네트워크 문제 — 무효 판정이 아님. 재시도 또는 유예 처리} else { // EXPIRED / REVOKED / DEVICE_NOT_ACTIVATED ...}SignoxClient 인스턴스는 불변 설정 + 스레드 안전이므로 앱 전역에서 하나만 만들어 공유하면 됩니다.
공개 API
섹션 제목: “공개 API”| 메서드 | 용도 |
|---|---|
validate(licenseKey) |
라이센스+디바이스 검증(캐시·오프라인 폴백 포함) |
activate(licenseKey[, opts]) |
디바이스 활성화(hwid·anomalies 자동 첨부) |
deactivate(licenseKey) |
디바이스 해제(슬롯 반환) |
heartbeat(licenseKey) |
생존 신호 + 가벼운 재검증 |
reportUsage(licenseKey, feature[, value]) |
메터링 사용량 보고 |
requestOfflineFile(licenseKey) |
.lic 파일 발급(내용은 getFileContent()) |
validateOffline(licContent) |
.lic 완전 로컬 검증(네트워크 불필요) |
getHwid() |
진단용 — 이 기기의 hwid |
판정 코드 (ValidationCode)
섹션 제목: “판정 코드 (ValidationCode)”- 상태:
VALID,IN_GRACE_PERIOD,EXPIRED,SUSPENDED,REVOKED - 요청/디바이스:
NOT_FOUND,PRODUCT_INACTIVE,HWID_REQUIRED,DEVICE_NOT_ACTIVATED,DEVICE_LIMIT_REACHED,VM_NOT_ALLOWED,FEATURE_NOT_FOUND,USAGE_LIMIT_REACHED - SDK 로컬 판정:
SIGNATURE_INVALID(서명검증 실패),NONCE_MISMATCH(재생 의심),OFFLINE_FILE_INVALID(.lic형식 오류),HWID_MISMATCH(.lic기기 불일치),NETWORK_ERROR(네트워크 실패 + 유효 캐시 없음 — 무효 판정과 구분)
isValid()가 true인 코드는 VALID, IN_GRACE_PERIOD 둘뿐입니다. 코드별 권장 UX는 온라인 활성화 가이드를 참고하세요.
캐시와 오프라인 폴백
섹션 제목: “캐시와 오프라인 폴백”validate성공 응답은 서버가 준cache.ttl동안 신뢰되어 재검증을 생략합니다(rate limit 내 자연 수렴).- 캐시에는 서명된 응답 원문(raw) 만 저장되며 읽을 때마다 서명을 재검증합니다(변조 방어).
- 네트워크 실패 시 오프라인 유예 창(
offlineGraceDays, 기본 7일 — 캐시 저장 시점 기준, 0 = 폴백 안 함) 내 유효 캐시가 있으면 그 판정으로 폴백하며isStale()이true가 됩니다. cacheDir를 지정하면 디스크에도 저장되어 프로세스 재시작 후에도 유지됩니다. 미지정 시 인메모리 캐시만 사용합니다.
SignoxClient.builder(productPublicKey) .cacheDir(new File(System.getProperty("user.home"), ".myapp/signox-cache")) .offlineGraceDays(14) // 오프라인 유예 14일 .build();오프라인 검증 (.lic)
섹션 제목: “오프라인 검증 (.lic)”인터넷이 없는 기기는 포털·대시보드 또는 활성 디바이스에서 .lic 파일을 발급받아 완전 로컬로 검증합니다. 전체 플로우는 오프라인 활성화를 참고하세요.
// 발급 (온라인 1회)LicenseResult issued = client.requestOfflineFile("S4K2MB-7IWQ3F-...");if (issued.getFileContent() != null) { Files.write(Paths.get("license.lic"), issued.getFileContent().getBytes(StandardCharsets.UTF_8));}
// 검증 (오프라인, 반복)String lic = new String(Files.readAllBytes(Paths.get("license.lic")), StandardCharsets.UTF_8);LicenseResult r = client.validateOffline(lic);// VALID / IN_GRACE_PERIOD / EXPIRED / HWID_MISMATCH / SIGNATURE_INVALID / OFFLINE_FILE_INVALID공개키 임베드 (중요)
섹션 제목: “공개키 임베드 (중요)”제품 공개키는 반드시 애플리케이션에 임베드하세요. SDK는 의도적으로 공개키 조회 엔드포인트를 제공하지 않습니다 — 런타임에 키를 받아오면 중간자가 자신의 키로 바꿔치기하는 위조 벡터가 생기기 때문입니다. 비밀키는 절대 클라이언트에 넣지 마세요(서명은 서버 전용).
BouncyCastle 클래스패스 충돌 (bcprov-jdk15on)
섹션 제목: “BouncyCastle 클래스패스 충돌 (bcprov-jdk15on)”기존 앱이 구버전 bcprov-jdk15on을 쓰면 클래스가 충돌할 수 있습니다. 앱 쪽 구버전을 최신 bcprov-jdk18on으로 통일하거나, 부득이하면 SDK의 전이 의존을 제외하고 앱이 제공하는 BC(Ed25519 지원 1.70+)를 사용하세요.
<dependency> <groupId>kr.signox</groupId> <artifactId>signox-sdk</artifactId> <version>0.1.0</version> <exclusions> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> </exclusion> </exclusions></dependency>