1 package de.dlr.shepard.common.util;
2
3 import io.quarkus.logging.Log;
4 import jakarta.enterprise.context.RequestScoped;
5 import java.io.IOException;
6 import java.nio.file.Files;
7 import java.nio.file.Path;
8 import java.security.KeyFactory;
9 import java.security.KeyPairGenerator;
10 import java.security.NoSuchAlgorithmException;
11 import java.security.PrivateKey;
12 import java.security.PublicKey;
13 import java.security.spec.InvalidKeySpecException;
14 import java.security.spec.PKCS8EncodedKeySpec;
15 import java.security.spec.X509EncodedKeySpec;
16 import lombok.Getter;
17
18 @RequestScoped
19 public class PKIHelper {
20
21 private static final String RSA = "RSA";
22
23 private Path keysDir = Path.of(System.getProperty("user.home"), ".shepard/keys");
24 private Path pubKey = Path.of(keysDir.toString(), "public.key");
25 private Path privKey = Path.of(keysDir.toString(), "private.key");
26
27 @Getter
28 private PublicKey publicKey;
29
30 @Getter
31 private PrivateKey privateKey;
32
33 public void init() {
34 generateKeyPairIfNecessary();
35
36 try {
37 publicKey = importPublicKey();
38 } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
39 Log.errorf("Exception while reading public key specification: %s", e);
40 }
41
42 try {
43 privateKey = importPrivateKey();
44 } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
45 Log.errorf("Exception while reading private key specification: %s", e);
46 }
47 }
48
49 private void generateKeyPairIfNecessary() {
50 if (Files.isRegularFile(keysDir)) {
51 Log.error("keys directory is a file, cannot create keys");
52 return;
53 }
54 if (Files.notExists(keysDir)) {
55 Log.info("keys directory does not exist, creating...");
56 try {
57 Files.createDirectories(keysDir);
58 } catch (IOException e) {
59 Log.errorf("Error while generating keys directory: %s", e.toString());
60 return;
61 }
62 }
63 if (!Files.isReadable(keysDir) || !Files.isWritable(keysDir)) {
64 Log.error("insufficient permissions for the keys directory");
65 return;
66 }
67
68 if (Files.exists(pubKey) && Files.isRegularFile(pubKey) && Files.exists(privKey) && Files.isRegularFile(privKey)) {
69 Log.info("keys found, importing...");
70 return;
71 }
72 Log.info("No keys available. Generating...");
73 try {
74 generateKeyPair();
75 } catch (NoSuchAlgorithmException | IOException e) {
76 Log.errorf("Error while generating keys: %s", e.toString());
77 }
78 }
79
80 private PublicKey importPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
81 byte[] key;
82 try (var fis = Files.newInputStream(pubKey)) {
83 key = fis.readAllBytes();
84 }
85
86 var keyFactory = KeyFactory.getInstance(RSA);
87 var spec = new X509EncodedKeySpec(key);
88 return keyFactory.generatePublic(spec);
89 }
90
91 private PrivateKey importPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
92 byte[] key;
93 try (var fis = Files.newInputStream(privKey)) {
94 key = fis.readAllBytes();
95 }
96
97 var keyFactory = KeyFactory.getInstance(RSA);
98 var spec = new PKCS8EncodedKeySpec(key);
99 return keyFactory.generatePrivate(spec);
100 }
101
102 private void generateKeyPair() throws NoSuchAlgorithmException, IOException {
103 var kpg = KeyPairGenerator.getInstance(RSA);
104 kpg.initialize(2048);
105 var kp = kpg.generateKeyPair();
106
107 try (var pubFos = Files.newOutputStream(pubKey); var privFos = Files.newOutputStream(privKey)) {
108 pubFos.write(kp.getPublic().getEncoded());
109 privFos.write(kp.getPrivate().getEncoded());
110 }
111 }
112 }