Skip to content

Commit 5b85a91

Browse files
authored
Merge pull request #7 from TeamDoubleO/KW-625/refactor/key-generation-revise
[KW-625] Kw 625/refactor/key generation revise
2 parents a1f46d1 + 8f2f40e commit 5b85a91

5 files changed

Lines changed: 135 additions & 64 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ out/
4040
.DS_Store
4141

4242
### .env ###
43-
.env
43+
.env
44+
gradle.properties

did-agent

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/main/java/com/doubleo/didagent/global/util/Ed25519KeyGenerator.java

Lines changed: 122 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,146 @@
22

33
import java.security.KeyPair;
44
import java.security.KeyPairGenerator;
5-
import java.security.interfaces.EdECPrivateKey;
6-
import java.security.interfaces.EdECPublicKey;
5+
import java.security.Security;
76
import java.util.Arrays;
87
import org.bitcoinj.core.Base58;
9-
import org.bouncycastle.jcajce.interfaces.XDHPrivateKey;
10-
import org.bouncycastle.jcajce.interfaces.XDHPublicKey;
8+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
119

1210
public class Ed25519KeyGenerator {
1311

12+
static {
13+
// BouncyCastle Provider 등록 (X25519 지원을 위해 필요)
14+
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
15+
Security.addProvider(new BouncyCastleProvider());
16+
}
17+
}
18+
1419
public static KeyMaterial generate() throws Exception {
1520

16-
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
17-
KeyPair kp = kpg.generateKeyPair();
21+
// Ed25519 키 쌍 생성
22+
KeyPairGenerator ed25519Kpg = KeyPairGenerator.getInstance("Ed25519");
23+
KeyPair ed25519Kp = ed25519Kpg.generateKeyPair();
24+
25+
// Ed25519 Public Key 처리
26+
byte[] ed25519PubSpki = ed25519Kp.getPublic().getEncoded();
27+
byte[] ed25519RawPub =
28+
Arrays.copyOfRange(
29+
ed25519PubSpki, ed25519PubSpki.length - 32, ed25519PubSpki.length);
30+
31+
// Ed25519 Private Key 처리
32+
byte[] ed25519PrivPkcs8 = ed25519Kp.getPrivate().getEncoded();
33+
byte[] ed25519RawPriv =
34+
Arrays.copyOfRange(
35+
ed25519PrivPkcs8, ed25519PrivPkcs8.length - 32, ed25519PrivPkcs8.length);
1836

19-
byte[] pubSpki = kp.getPublic().getEncoded();
20-
byte[] rawPub = Arrays.copyOfRange(pubSpki, pubSpki.length - 32, pubSpki.length);
37+
// Ed25519 Public Key multicodec encoding (0xED 0x01)
38+
byte[] ed25519PubPrefixed = new byte[ed25519RawPub.length + 2];
39+
ed25519PubPrefixed[0] = (byte) 0xED;
40+
ed25519PubPrefixed[1] = 0x01;
41+
System.arraycopy(ed25519RawPub, 0, ed25519PubPrefixed, 2, ed25519RawPub.length);
2142

22-
byte[] privPkcs8 = kp.getPrivate().getEncoded();
23-
byte[] rawPriv = Arrays.copyOfRange(privPkcs8, privPkcs8.length - 32, privPkcs8.length);
43+
String ed25519PublicKeyBase58 = "z" + Base58.encode(ed25519PubPrefixed);
44+
String ed25519PrivateKeyBase58 = Base58.encode(ed25519RawPriv);
2445

25-
byte[] prefixed = new byte[rawPub.length + 2];
26-
prefixed[0] = (byte) 0xED;
27-
prefixed[1] = 0x01;
28-
System.arraycopy(rawPub, 0, prefixed, 2, rawPub.length);
46+
// X25519 키 쌍 생성 (별도의 독립적인 키 쌍)
47+
KeyPairGenerator x25519Kpg = KeyPairGenerator.getInstance("X25519", "BC");
48+
KeyPair x25519Kp = x25519Kpg.generateKeyPair();
2949

30-
String publicKeyBase58 = "z" + Base58.encode(prefixed);
31-
String privateKeyBase58 = Base58.encode(rawPriv);
50+
// X25519 Public Key 처리
51+
byte[] x25519PubEncoded = x25519Kp.getPublic().getEncoded();
52+
byte[] x25519RawPub = extractX25519PublicKey(x25519PubEncoded);
3253

33-
KeyPairGenerator xKpg = KeyPairGenerator.getInstance("X25519", "BC");
34-
KeyPair xKp = xKpg.generateKeyPair();
54+
// X25519 Private Key 처리
55+
byte[] x25519PrivEncoded = x25519Kp.getPrivate().getEncoded();
56+
byte[] x25519RawPriv = extractX25519PrivateKey(x25519PrivEncoded);
3557

36-
byte[] xPubSpki = xKp.getPublic().getEncoded();
37-
byte[] xRawPub = Arrays.copyOfRange(xPubSpki, xPubSpki.length - 32, xPubSpki.length);
58+
// X25519 Public Key multicodec encoding (0xEC 0x01)
59+
byte[] x25519PubPrefixed = new byte[x25519RawPub.length + 2];
60+
x25519PubPrefixed[0] = (byte) 0xEC;
61+
x25519PubPrefixed[1] = 0x01;
62+
System.arraycopy(x25519RawPub, 0, x25519PubPrefixed, 2, x25519RawPub.length);
3863

39-
/* multicodec: 0xEC 0x01 = X25519 public key */
40-
byte[] xPrefixed = new byte[xRawPub.length + 2];
41-
xPrefixed[0] = (byte) 0xEC;
42-
xPrefixed[1] = 0x01;
43-
System.arraycopy(xRawPub, 0, xPrefixed, 2, xRawPub.length);
64+
String x25519PublicKeyMb58 = "z" + Base58.encode(x25519PubPrefixed);
4465

45-
String agreementKeyMb58 = "z" + Base58.encode(xPrefixed);
46-
byte[] xPrivSpki = xKp.getPrivate().getEncoded();
47-
// PKCS#8 또는 SPKI 형식으로 인코딩된 값이 넘어오므로, 끝 32바이트가 실제 raw private
48-
byte[] xRawPriv = Arrays.copyOfRange(xPrivSpki, xPrivSpki.length - 32, xPrivSpki.length);
66+
// X25519 Private Key multicodec encoding (0x82 0x26)
67+
byte[] x25519PrivPrefixed = new byte[x25519RawPriv.length + 2];
68+
x25519PrivPrefixed[0] = (byte) 0x82;
69+
x25519PrivPrefixed[1] = 0x26;
70+
System.arraycopy(x25519RawPriv, 0, x25519PrivPrefixed, 2, x25519RawPriv.length);
4971

50-
// multicodec 형식 붙이기 (0xEC 0x01 = X25519 private multicodec)
51-
byte[] xPrivPrefixed = new byte[xRawPriv.length + 2];
52-
xPrivPrefixed[0] = (byte) 0xEC;
53-
xPrivPrefixed[1] = 0x01;
54-
System.arraycopy(xRawPriv, 0, xPrivPrefixed, 2, xRawPriv.length);
72+
String x25519PrivateKeyMb58 = "z" + Base58.encode(x25519PrivPrefixed);
5573

56-
// 최종적으로 multibase58 (z-prefixed) 문자열
57-
String x25519PrivateMb58 = "z" + Base58.encode(xPrivPrefixed);
74+
// 디버깅 출력
75+
System.out.println("=== Ed25519 Keys ===");
76+
System.out.println("Ed25519 Public (hex): " + bytesToHex(ed25519RawPub));
77+
System.out.println("Ed25519 Private (hex): " + bytesToHex(ed25519RawPriv));
78+
79+
System.out.println("\n=== X25519 Keys ===");
80+
System.out.println("X25519 Public (hex): " + bytesToHex(x25519RawPub));
81+
System.out.println("X25519 Private (hex): " + bytesToHex(x25519RawPriv));
82+
83+
System.out.println("\n=== Encoded Keys ===");
84+
System.out.println("Ed25519 Public MB58: " + ed25519PublicKeyBase58);
85+
System.out.println("Ed25519 Private B58: " + ed25519PrivateKeyBase58);
86+
System.out.println("X25519 Public MB58: " + x25519PublicKeyMb58);
87+
System.out.println("X25519 Private MB58: " + x25519PrivateKeyMb58);
5888

59-
/* 반환 객체에 추가로 포함 */
6089
return new KeyMaterial(
61-
rawPub,
62-
rawPriv,
63-
publicKeyBase58,
64-
privateKeyBase58,
65-
agreementKeyMb58, // NEW: X25519 public multibase
66-
(EdECPublicKey) kp.getPublic(),
67-
(EdECPrivateKey) kp.getPrivate(),
68-
(XDHPublicKey) xKp.getPublic(),
69-
(XDHPrivateKey) xKp.getPrivate(),
70-
x25519PrivateMb58);
90+
ed25519RawPub, // rawPub (Ed25519)
91+
ed25519RawPriv, // rawPriv (Ed25519)
92+
ed25519PublicKeyBase58, // signingKeyMb58 (Ed25519 public)
93+
ed25519PrivateKeyBase58, // signingPrivBase58 (Ed25519 private)
94+
x25519PublicKeyMb58, // agreementKeyMb58 (X25519 public)
95+
x25519PrivateKeyMb58 // x25519PrivateMb58 (X25519 private)
96+
);
97+
}
98+
99+
/**
100+
* X25519 Public Key에서 원시 32바이트 추출 SPKI 형식: 30 2A 30 05 06 03 2B 65 6E 03 21 00 [32바이트 public
101+
* key]
102+
*/
103+
private static byte[] extractX25519PublicKey(byte[] encoded) {
104+
// SPKI 형식에서 마지막 32바이트가 실제 public key
105+
if (encoded.length < 32) {
106+
throw new IllegalArgumentException("Invalid X25519 public key encoding");
107+
}
108+
return Arrays.copyOfRange(encoded, encoded.length - 32, encoded.length);
109+
}
110+
111+
/** X25519 Private Key에서 원시 32바이트 추출 PKCS#8 형식에서 실제 private key 데이터 추출 */
112+
private static byte[] extractX25519PrivateKey(byte[] encoded) {
113+
// PKCS#8 형식 파싱
114+
// 30 2E 02 01 00 30 05 06 03 2B 65 6E 04 22 04 20 [32바이트 private key]
115+
116+
if (encoded.length < 32) {
117+
throw new IllegalArgumentException("Invalid X25519 private key encoding");
118+
}
119+
120+
// PKCS#8에서 private key는 OCTET STRING 내부에 있음
121+
// 일반적으로 마지막 32바이트가 실제 private key
122+
// 하지만 더 정확한 파싱을 위해 OCTET STRING을 찾음
123+
124+
for (int i = 0; i < encoded.length - 34; i++) {
125+
if (encoded[i] == 0x04
126+
&& encoded[i + 1] == 0x22
127+
&& encoded[i + 2] == 0x04
128+
&& encoded[i + 3] == 0x20) {
129+
// 0x04 0x22 = OCTET STRING (34 bytes)
130+
// 0x04 0x20 = OCTET STRING (32 bytes) - actual private key
131+
return Arrays.copyOfRange(encoded, i + 4, i + 36);
132+
}
133+
}
134+
135+
// fallback: 마지막 32바이트 사용
136+
return Arrays.copyOfRange(encoded, encoded.length - 32, encoded.length);
137+
}
138+
139+
/** 바이트 배열을 hex 문자열로 변환 */
140+
private static String bytesToHex(byte[] bytes) {
141+
StringBuilder result = new StringBuilder();
142+
for (byte b : bytes) {
143+
result.append(String.format("%02x", b));
144+
}
145+
return result.toString();
71146
}
72147
}
Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
package com.doubleo.didagent.global.util;
22

3-
import java.security.interfaces.EdECPrivateKey;
4-
import java.security.interfaces.EdECPublicKey;
5-
import org.bouncycastle.jcajce.interfaces.XDHPrivateKey;
6-
import org.bouncycastle.jcajce.interfaces.XDHPublicKey;
7-
83
public record KeyMaterial(
94
byte[] rawEd25519Public, // 32-byte 원본 Ed25519 공개키
105
byte[] rawEd25519Private, // 32-byte 원본 Ed25519 비밀키
116
String signingKeyMb58, // 멀티코덱+멀티베이스(“z…”) Ed25519 공개키
127
String signingPrivBase58, // Base58 인코딩된 Ed25519 비밀키(원본 32 바이트)
138
String agreementKeyMb58, // 멀티코덱+멀티베이스(“z…”) X25519 공개키
14-
EdECPublicKey signingPublic, // JCA Ed25519 PublicKey (서명용)
15-
EdECPrivateKey signingPrivate, // JCA Ed25519 PrivateKey (서명용)
16-
XDHPublicKey agreementPublic, // JCA X25519 PublicKey (암호화·키합의용)
17-
XDHPrivateKey agreementPrivate, // JCA X25519 PrivateKey (암호화·키합의용)
9+
// EdECPublicKey signingPublic, // JCA Ed25519 PublicKey (서명용)
10+
// EdECPrivateKey signingPrivate, // JCA Ed25519 PrivateKey (서명용)
11+
// XDHPublicKey agreementPublic, // JCA X25519 PublicKey (암호화·키합의용)
12+
// XDHPrivateKey agreementPrivate, // JCA X25519 PrivateKey (암호화·키합의용)
1813
String x25519PrivateMb58) {}

src/main/java/com/doubleo/didagent/global/util/PeerDidUtil.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ public static String createPeerDid2(
1919
String enc2 = "E" + agreementKeyMb; // key-agreement key
2020
List<String> routingKeysDidUrl = PeerDidUtil.convertRoutingKeys(routingKeys);
2121
JSONObject svc = new JSONObject();
22-
svc.put("id", "#didcomm-0");
23-
svc.put("t", "did-communication");
24-
svc.put("p", 0);
22+
23+
svc.put("type", "DIDComm");
24+
svc.put("priority", 0);
25+
svc.put("id", "#service-0");
2526
svc.put("recipientKeys", new JSONArray().put("#key-1"));
2627
if (routingKeys != null && !routingKeys.isEmpty()) {
27-
svc.put("r", new JSONArray(routingKeysDidUrl)); // routingKeys → r
28+
svc.put("routingKeys", new JSONArray(routingKeysDidUrl)); // routingKeys → r
2829
}
29-
svc.put("s", serviceEndpoint); // serviceEndpoint → s
30+
svc.put("serviceEndpoint", serviceEndpoint); // serviceEndpoint → s
3031

3132
String enc3 =
3233
"S"
@@ -45,7 +46,7 @@ private static String rawVerkeyToDidKey(String verkeyBase58) {
4546
System.arraycopy(raw, 0, prefixed, 2, raw.length);
4647

4748
String multibase = "z" + Base58.encode(prefixed);
48-
return "did:key:" + multibase + "#" + multibase;
49+
return "did:key:" + multibase;
4950
}
5051

5152
private static List<String> convertRoutingKeys(List<String> rawKeys) {

0 commit comments

Comments
 (0)