diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java
index b90ad463..d1b9240d 100644
--- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java
+++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/Ed25519SigningServiceImpl.java
@@ -3,44 +3,48 @@ package com.cameleer3.server.app.security;
import com.cameleer3.server.core.security.Ed25519SigningService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.jdbc.core.JdbcTemplate;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
+import java.security.SecureRandom;
import java.security.Signature;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
+import java.security.spec.NamedParameterSpec;
import java.util.Base64;
-import java.util.List;
-import java.util.Map;
/**
* JDK 17 Ed25519 signing implementation.
*
- * Persists the keypair to PostgreSQL {@code server_config} table so it survives
- * server restarts. Agents cache the public key at registration — if the key
- * changes, all agents reject commands until they re-register.
+ * Derives the keypair deterministically from the JWT secret via HMAC-SHA256,
+ * so the same key pair is produced on every server startup. No database storage
+ * of private key material is needed.
*/
public class Ed25519SigningServiceImpl implements Ed25519SigningService {
private static final Logger log = LoggerFactory.getLogger(Ed25519SigningServiceImpl.class);
- private static final String CONFIG_KEY = "ed25519_signing_key";
+ private static final String DERIVATION_INFO = "cameleer3-ed25519-signing";
private final PrivateKey privateKey;
private final PublicKey publicKey;
- public Ed25519SigningServiceImpl(JdbcTemplate jdbcTemplate) {
- KeyPair keyPair = loadOrGenerate(jdbcTemplate);
- this.privateKey = keyPair.getPrivate();
- this.publicKey = keyPair.getPublic();
+ public Ed25519SigningServiceImpl(String jwtSecret) {
+ try {
+ byte[] seed = deriveSeed(jwtSecret);
+ KeyPair keyPair = generateFromSeed(seed);
+ this.privateKey = keyPair.getPrivate();
+ this.publicKey = keyPair.getPublic();
+ log.info("Ed25519 signing key derived from JWT secret");
+ } catch (GeneralSecurityException e) {
+ throw new IllegalStateException("Failed to derive Ed25519 keypair", e);
+ }
}
- /** Ephemeral key pair — for tests only, not persisted. */
+ /** Ephemeral random key pair — for tests only. */
public static Ed25519SigningServiceImpl ephemeral() {
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("Ed25519");
@@ -56,59 +60,18 @@ public class Ed25519SigningServiceImpl implements Ed25519SigningService {
this.publicKey = keyPair.getPublic();
}
- private static KeyPair loadOrGenerate(JdbcTemplate jdbc) {
- try {
- List