Keys are loaded from PEM files when CAMELEER_JWT_PRIVATE_KEY_PATH and CAMELEER_JWT_PUBLIC_KEY_PATH are set. Falls back to ephemeral key generation for development. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
83 lines
3.0 KiB
Java
83 lines
3.0 KiB
Java
package net.siegeln.cameleer.saas.config;
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import java.io.IOException;
|
|
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.NoSuchAlgorithmException;
|
|
import java.security.PrivateKey;
|
|
import java.security.PublicKey;
|
|
import java.security.spec.InvalidKeySpecException;
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
import java.security.spec.X509EncodedKeySpec;
|
|
import java.util.Base64;
|
|
|
|
@Component
|
|
public class JwtConfig {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(JwtConfig.class);
|
|
|
|
@Value("${cameleer.jwt.expiration:86400}")
|
|
private long expirationSeconds = 86400;
|
|
|
|
@Value("${cameleer.jwt.private-key-path:}")
|
|
private String privateKeyPath = "";
|
|
|
|
@Value("${cameleer.jwt.public-key-path:}")
|
|
private String publicKeyPath = "";
|
|
|
|
private KeyPair keyPair;
|
|
|
|
@PostConstruct
|
|
public void init() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
|
if (privateKeyPath.isEmpty() || publicKeyPath.isEmpty()) {
|
|
log.warn("No Ed25519 key files configured — generating ephemeral keys (dev mode)");
|
|
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("Ed25519");
|
|
this.keyPair = keyGen.generateKeyPair();
|
|
} else {
|
|
log.info("Loading Ed25519 keys from {} and {}", privateKeyPath, publicKeyPath);
|
|
PrivateKey privateKey = loadPrivateKey(Path.of(privateKeyPath));
|
|
PublicKey publicKey = loadPublicKey(Path.of(publicKeyPath));
|
|
this.keyPair = new KeyPair(publicKey, privateKey);
|
|
}
|
|
}
|
|
|
|
private PrivateKey loadPrivateKey(Path path) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
|
String pem = Files.readString(path)
|
|
.replace("-----BEGIN PRIVATE KEY-----", "")
|
|
.replace("-----END PRIVATE KEY-----", "")
|
|
.replaceAll("\\s+", "");
|
|
byte[] decoded = Base64.getDecoder().decode(pem);
|
|
return KeyFactory.getInstance("Ed25519").generatePrivate(new PKCS8EncodedKeySpec(decoded));
|
|
}
|
|
|
|
private PublicKey loadPublicKey(Path path) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
|
String pem = Files.readString(path)
|
|
.replace("-----BEGIN PUBLIC KEY-----", "")
|
|
.replace("-----END PUBLIC KEY-----", "")
|
|
.replaceAll("\\s+", "");
|
|
byte[] decoded = Base64.getDecoder().decode(pem);
|
|
return KeyFactory.getInstance("Ed25519").generatePublic(new X509EncodedKeySpec(decoded));
|
|
}
|
|
|
|
public PrivateKey getPrivateKey() {
|
|
return keyPair.getPrivate();
|
|
}
|
|
|
|
public PublicKey getPublicKey() {
|
|
return keyPair.getPublic();
|
|
}
|
|
|
|
public long getExpirationSeconds() {
|
|
return expirationSeconds;
|
|
}
|
|
}
|