Skip to content

Commit 449c789

Browse files
brody-0125claude
andauthored
Add comprehensive examples module for Open Badges 3.0 (#1)
* feat: add examples directory with runnable Open Badges 3.0 examples Add a Gradle subproject with 7 standalone examples demonstrating the full Signet Core API: credential building, EdDSA/ECDSA/JWS signing, key management, and structural validation. https://claude.ai/code/session_016ED9AU4xeq8w7vQTcLRyKz * refactor: use OpenBadgesContext constants instead of hardcoded URLs in examples Replace hardcoded JSON-LD context URL strings with OpenBadgesContext.VC_CONTEXT and OpenBadgesContext.OB3_CONTEXT in EdDsaSigningExample, EcdsaSigningExample, and ValidationExample. https://claude.ai/code/session_016ED9AU4xeq8w7vQTcLRyKz --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8a98820 commit 449c789

9 files changed

Lines changed: 655 additions & 0 deletions

examples/build.gradle.kts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
plugins {
2+
java
3+
application
4+
}
5+
6+
repositories {
7+
mavenCentral()
8+
}
9+
10+
dependencies {
11+
implementation(project(":"))
12+
// Jackson ObjectMapper — not transitively exposed by signet-core (implementation scope)
13+
implementation("com.fasterxml.jackson.core:jackson-databind:2.18.4")
14+
}
15+
16+
application {
17+
mainClass.set(
18+
findProperty("example")?.toString()
19+
?: "work.brodykim.signet.examples.EndToEndExample"
20+
)
21+
}
22+
23+
tasks.withType<JavaCompile> {
24+
options.release = 17
25+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package work.brodykim.signet.examples;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
5+
import work.brodykim.signet.core.BadgeAchievement;
6+
import work.brodykim.signet.core.BadgeEvidence;
7+
import work.brodykim.signet.core.BadgeIssuer;
8+
import work.brodykim.signet.credential.CredentialBuilder;
9+
import work.brodykim.signet.credential.CredentialRequest;
10+
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.UUID;
14+
15+
/**
16+
* Open Badges 3.0 크리덴셜을 빌드하는 기본 예제.
17+
*
18+
* <p>{@link CredentialBuilder}와 {@link CredentialRequest} 빌더 패턴을 사용하여
19+
* W3C Verifiable Credential 규격의 JSON-LD 문서를 생성합니다.
20+
*/
21+
public class BasicCredentialExample {
22+
23+
public static void main(String[] args) throws Exception {
24+
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
25+
26+
// 1. 발급자(Issuer) 정의
27+
BadgeIssuer issuer = new BadgeIssuer(
28+
UUID.randomUUID(),
29+
"Signet Academy",
30+
"https://signet-academy.example.com",
31+
"badges@signet-academy.example.com",
32+
"Open Badges 3.0 기반 디지털 자격증 발급 기관"
33+
);
34+
35+
// 2. 업적(Achievement) 정의
36+
BadgeAchievement achievement = new BadgeAchievement(
37+
UUID.randomUUID(),
38+
"Java Proficiency",
39+
"자바 프로그래밍 언어에 대한 전문성을 입증합니다.",
40+
"자바 인증 시험을 통과해야 합니다.",
41+
"Certification",
42+
"https://signet-academy.example.com/badges/java.png",
43+
List.of("java", "programming", "certification")
44+
);
45+
46+
// 3. 증거(Evidence) 정의
47+
List<BadgeEvidence> evidence = List.of(
48+
new BadgeEvidence(
49+
"https://signet-academy.example.com/evidence/project-1",
50+
"Final Project",
51+
"자바 기반 REST API 최종 프로젝트 제출",
52+
"Spring Boot를 활용한 RESTful API 설계 및 구현",
53+
"Portfolio"
54+
)
55+
);
56+
57+
// 4. CredentialRequest 빌더로 요청 구성
58+
CredentialRequest request = CredentialRequest.builder(
59+
UUID.randomUUID(),
60+
"recipient@example.com",
61+
achievement,
62+
issuer
63+
)
64+
.recipientName("홍길동")
65+
.description("자바 프로그래밍 전문가 인증서")
66+
.imageUrl("https://signet-academy.example.com/badges/java.png")
67+
.evidence(evidence)
68+
.build();
69+
70+
// 5. 크리덴셜 빌드
71+
CredentialBuilder builder = new CredentialBuilder(
72+
"https://signet-academy.example.com", "my-salt");
73+
Map<String, Object> credential = builder.buildCredential(request);
74+
75+
// 6. 결과 출력
76+
System.out.println("=== Basic Credential Example ===");
77+
System.out.println();
78+
System.out.println(mapper.writeValueAsString(credential));
79+
}
80+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package work.brodykim.signet.examples;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
5+
import com.nimbusds.jose.jwk.ECKey;
6+
import work.brodykim.signet.credential.CredentialSigner;
7+
import work.brodykim.signet.credential.KeyPairManager;
8+
import work.brodykim.signet.jsonld.CachedDocumentLoader;
9+
import work.brodykim.signet.jsonld.JsonLdProcessor;
10+
11+
import work.brodykim.signet.core.OpenBadgesContext;
12+
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
/**
18+
* ECDSA P-256 DataIntegrity 서명 및 검증 예제.
19+
*
20+
* <p>{@code ecdsa-rdfc-2022} cryptosuite를 사용하여 NIST P-256 곡선 기반의
21+
* Data Integrity Proof를 생성하고 검증합니다.
22+
*/
23+
public class EcdsaSigningExample {
24+
25+
public static void main(String[] args) throws Exception {
26+
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
27+
28+
// 1. P-256 키 쌍 생성
29+
ECKey keyPair = KeyPairManager.generateP256KeyPair();
30+
String multibase = KeyPairManager.toPublicKeyMultibase(keyPair);
31+
System.out.println("=== ECDSA (P-256) Signing Example ===");
32+
System.out.println();
33+
System.out.println("Public Key (multibase): " + multibase);
34+
System.out.println();
35+
36+
// 2. 샘플 크리덴셜 구성
37+
Map<String, Object> credential = buildSampleCredential();
38+
39+
// 3. CredentialSigner 초기화
40+
JsonLdProcessor jsonLdProcessor = new JsonLdProcessor(new CachedDocumentLoader());
41+
CredentialSigner signer = new CredentialSigner(mapper, jsonLdProcessor);
42+
43+
// 4. ECDSA DataIntegrity 서명 (ecdsa-rdfc-2022)
44+
Map<String, Object> signed = signer.signWithEcdsaDataIntegrity(
45+
credential, keyPair, "https://example.com/issuers/1#key-1");
46+
47+
System.out.println("[Signed Credential]");
48+
System.out.println(mapper.writeValueAsString(signed));
49+
System.out.println();
50+
51+
// 5. 서명 검증
52+
boolean valid = signer.verifyEcdsaDataIntegrity(signed, keyPair.toPublicJWK());
53+
System.out.println("Verification result: " + valid);
54+
System.out.println();
55+
56+
// 6. 변조 감지 시연
57+
Map<String, Object> tampered = new LinkedHashMap<>(signed);
58+
tampered.put("name", "Tampered Badge");
59+
boolean tamperedValid = signer.verifyEcdsaDataIntegrity(tampered, keyPair.toPublicJWK());
60+
System.out.println("Tampered document verification: " + tamperedValid);
61+
}
62+
63+
private static Map<String, Object> buildSampleCredential() {
64+
Map<String, Object> credential = new LinkedHashMap<>();
65+
credential.put("@context", List.of(
66+
OpenBadgesContext.VC_CONTEXT,
67+
OpenBadgesContext.OB3_CONTEXT));
68+
credential.put("type", List.of("VerifiableCredential", "OpenBadgeCredential"));
69+
credential.put("id", "https://example.com/credentials/2");
70+
credential.put("issuer", Map.of(
71+
"id", "https://example.com/issuers/1",
72+
"type", "Profile",
73+
"name", "Example Issuer"));
74+
credential.put("validFrom", "2026-01-01T00:00:00Z");
75+
credential.put("name", "ECDSA Signed Badge");
76+
return credential;
77+
}
78+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package work.brodykim.signet.examples;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
5+
import com.nimbusds.jose.jwk.OctetKeyPair;
6+
import work.brodykim.signet.credential.CredentialSigner;
7+
import work.brodykim.signet.credential.KeyPairManager;
8+
import work.brodykim.signet.jsonld.CachedDocumentLoader;
9+
import work.brodykim.signet.jsonld.JsonLdProcessor;
10+
11+
import work.brodykim.signet.core.OpenBadgesContext;
12+
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
/**
18+
* EdDSA (Ed25519) DataIntegrity 서명 및 검증 예제.
19+
*
20+
* <p>{@code eddsa-rdfc-2022} cryptosuite를 사용하여 RDFC-1.0 정규화 기반의
21+
* Data Integrity Proof를 생성하고 검증합니다.
22+
*/
23+
public class EdDsaSigningExample {
24+
25+
public static void main(String[] args) throws Exception {
26+
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
27+
28+
// 1. Ed25519 키 쌍 생성
29+
OctetKeyPair keyPair = KeyPairManager.generateEd25519KeyPair();
30+
String multibase = KeyPairManager.toPublicKeyMultibase(keyPair);
31+
System.out.println("=== EdDSA (Ed25519) Signing Example ===");
32+
System.out.println();
33+
System.out.println("Public Key (multibase): " + multibase);
34+
System.out.println();
35+
36+
// 2. 샘플 크리덴셜 구성
37+
Map<String, Object> credential = buildSampleCredential();
38+
39+
// 3. CredentialSigner 초기화
40+
JsonLdProcessor jsonLdProcessor = new JsonLdProcessor(new CachedDocumentLoader());
41+
CredentialSigner signer = new CredentialSigner(mapper, jsonLdProcessor);
42+
43+
// 4. DataIntegrity 서명 (eddsa-rdfc-2022)
44+
Map<String, Object> signed = signer.signWithDataIntegrity(
45+
credential, keyPair, "https://example.com/issuers/1#key-1");
46+
47+
System.out.println("[Signed Credential]");
48+
System.out.println(mapper.writeValueAsString(signed));
49+
System.out.println();
50+
51+
// 5. 서명 검증
52+
boolean valid = signer.verifyDataIntegrity(signed, keyPair.toPublicJWK());
53+
System.out.println("Verification result: " + valid);
54+
System.out.println();
55+
56+
// 6. 변조 감지 시연
57+
Map<String, Object> tampered = new LinkedHashMap<>(signed);
58+
tampered.put("name", "Tampered Badge");
59+
boolean tamperedValid = signer.verifyDataIntegrity(tampered, keyPair.toPublicJWK());
60+
System.out.println("Tampered document verification: " + tamperedValid);
61+
}
62+
63+
private static Map<String, Object> buildSampleCredential() {
64+
Map<String, Object> credential = new LinkedHashMap<>();
65+
credential.put("@context", List.of(
66+
OpenBadgesContext.VC_CONTEXT,
67+
OpenBadgesContext.OB3_CONTEXT));
68+
credential.put("type", List.of("VerifiableCredential", "OpenBadgeCredential"));
69+
credential.put("id", "https://example.com/credentials/1");
70+
credential.put("issuer", Map.of(
71+
"id", "https://example.com/issuers/1",
72+
"type", "Profile",
73+
"name", "Example Issuer"));
74+
credential.put("validFrom", "2026-01-01T00:00:00Z");
75+
credential.put("name", "Example Badge");
76+
return credential;
77+
}
78+
}

0 commit comments

Comments
 (0)