diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/KeyStorage.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/KeyStorage.java index c5f79be..a25e74d 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/KeyStorage.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/KeyStorage.java @@ -3,11 +3,19 @@ package de.srsoftware.oidc.api; import java.io.IOException; import java.util.List; +import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.PublicJsonWebKey; +import org.jose4j.lang.JoseException; public interface KeyStorage { - public KeyStorage drop(String keyId); - public List listKeys(); - public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException; - public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException; + public KeyStorage drop(String keyId); + public List listKeys(); + public default PublicJsonWebKey load(String keyId) throws IOException, JoseException { + return PublicJsonWebKey.Factory.newPublicJwk(loadJson(keyId)); + } + public String loadJson(String keyId) throws IOException; + public KeyStorage store(String keyId, String json) throws IOException; + public default KeyStorage store(PublicJsonWebKey key) throws IOException { + return store(key.getKeyId(), key.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE)); + } } diff --git a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java index a1b23e6..dac7a28 100644 --- a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java +++ b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java @@ -19,6 +19,7 @@ import de.srsoftware.oidc.api.*; import de.srsoftware.oidc.api.data.User; import de.srsoftware.oidc.backend.*; import de.srsoftware.oidc.datastore.encrypted.EncryptedClientService; +import de.srsoftware.oidc.datastore.encrypted.EncryptedKeyStore; import de.srsoftware.oidc.datastore.encrypted.EncryptedMailConfig; import de.srsoftware.oidc.datastore.encrypted.EncryptedUserService; import de.srsoftware.oidc.datastore.file.FileStoreProvider; @@ -153,12 +154,21 @@ public class Application { private static KeyStorage setupKeyStore(Configuration config, Path defaultConfigDir) throws SQLException { var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys"))); + KeyStorage keyStore = null; if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) { - return new PlaintextKeyStore(keyStorageLocation.toPath()); + keyStore = new PlaintextKeyStore(keyStorageLocation.toPath()); } else { // SQLite var conn = connectionProvider.get(keyStorageLocation); - return new SqliteKeyStore(conn); + keyStore = new SqliteKeyStore(conn); } + + Optional encryptionKey = config.get(ENCRYPTION_KEY); + + if (encryptionKey.isPresent()){ + var salt = config.getOrDefault(SALT,uuid()); + keyStore = new EncryptedKeyStore(encryptionKey.get(),salt,keyStore); + } + return keyStore; } private static Map map(String[] args) { diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/KeyStoreController.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/KeyStoreController.java index eb7c58e..4e25f20 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/KeyStoreController.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/KeyStoreController.java @@ -3,7 +3,6 @@ package de.srsoftware.oidc.backend; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.http.PathHandler; -import de.srsoftware.oidc.api.KeyManager; import de.srsoftware.oidc.api.KeyStorage; import java.io.IOException; import org.jose4j.jwk.JsonWebKey; @@ -33,9 +32,7 @@ public class KeyStoreController extends PathHandler { PublicJsonWebKey key = keyStore.load(keyId); String keyJson = key.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY); arr.put(new JSONObject(keyJson)); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (KeyManager.KeyCreationException e) { + } catch (Exception e) { throw new RuntimeException(e); } JSONObject result = new JSONObject(); diff --git a/de.srsoftware.oidc.datastore.encrypted/build.gradle b/de.srsoftware.oidc.datastore.encrypted/build.gradle index f27b074..d3adee1 100644 --- a/de.srsoftware.oidc.datastore.encrypted/build.gradle +++ b/de.srsoftware.oidc.datastore.encrypted/build.gradle @@ -16,6 +16,8 @@ dependencies { implementation 'com.sun.mail:jakarta.mail:2.0.1' implementation project(':de.srsoftware.utils') testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle") + implementation 'org.bitbucket.b_c:jose4j:0.9.6' + } test { diff --git a/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedKeyStore.java b/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedKeyStore.java new file mode 100644 index 0000000..a575669 --- /dev/null +++ b/de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedKeyStore.java @@ -0,0 +1,36 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.datastore.encrypted; + +import de.srsoftware.oidc.api.KeyStorage; +import java.io.IOException; +import java.util.List; + +public class EncryptedKeyStore extends EncryptedConfig implements KeyStorage { + private final KeyStorage backend; + + public EncryptedKeyStore(String key, String salt, KeyStorage backend) { + super(key, salt); + this.backend = backend; + } + + @Override + public KeyStorage drop(String keyId) { + return backend.drop(keyId); + } + + @Override + public List listKeys() { + return backend.listKeys(); + } + + @Override + public String loadJson(String keyId) throws IOException { + return decrypt(backend.loadJson(keyId)); + } + + @Override + public KeyStorage store(String keyId, String jsonWebKey) throws IOException { + backend.store(keyId, encrypt(jsonWebKey)); + return this; + } +} diff --git a/de.srsoftware.oidc.datastore.encrypted/src/test/java/EncryptedKeyStoreTest.java b/de.srsoftware.oidc.datastore.encrypted/src/test/java/EncryptedKeyStoreTest.java new file mode 100644 index 0000000..ca15001 --- /dev/null +++ b/de.srsoftware.oidc.datastore.encrypted/src/test/java/EncryptedKeyStoreTest.java @@ -0,0 +1,53 @@ +/* © SRSoftware 2024 */ +import static de.srsoftware.utils.Strings.uuid; + +import de.srsoftware.oidc.api.KeyStorage; +import de.srsoftware.oidc.api.KeyStoreTest; +import de.srsoftware.oidc.datastore.encrypted.EncryptedKeyStore; +import java.io.IOException; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; + + +public class EncryptedKeyStoreTest extends KeyStoreTest { + private class InMemoryKeyStore implements KeyStorage { + private HashMap store = new HashMap<>(); + @Override + public KeyStorage drop(String keyId) { + store.remove(keyId); + return this; + } + + @Override + public List listKeys() { + return List.copyOf(store.keySet()); + } + + @Override + public String loadJson(String keyId) { + return store.get(keyId); + } + + @Override + public KeyStorage store(String keyId, String jsonWebKey) throws IOException { + store.put(keyId, jsonWebKey); + return this; + } + } + private KeyStorage keyStore; + + @Override + protected KeyStorage keyStore() { + return keyStore; + } + + @BeforeEach + public void setup() throws SQLException { + var backend = new InMemoryKeyStore(); + var key = uuid(); + var salt = uuid(); + keyStore = new EncryptedKeyStore(key, salt, backend); + } +} diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/PlaintextKeyStore.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/PlaintextKeyStore.java index d2d5621..9cf5361 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/PlaintextKeyStore.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/PlaintextKeyStore.java @@ -3,23 +3,19 @@ package de.srsoftware.oidc.datastore.file; import static java.lang.System.Logger.Level.DEBUG; import static java.lang.System.Logger.Level.ERROR; -import static org.jose4j.jwk.JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE; -import de.srsoftware.oidc.api.KeyManager; import de.srsoftware.oidc.api.KeyStorage; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.List; -import org.jose4j.jwk.PublicJsonWebKey; -import org.jose4j.lang.JoseException; public class PlaintextKeyStore implements KeyStorage { public static System.Logger LOG = System.getLogger(PlaintextKeyStore.class.getSimpleName()); - private final Path dir; - private HashMap loaded = new HashMap<>(); + private final Path dir; + private HashMap loaded = new HashMap<>(); public PlaintextKeyStore(Path storageDir) { this.dir = storageDir; @@ -42,22 +38,17 @@ public class PlaintextKeyStore implements KeyStorage { } @Override - public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException { + public String loadJson(String keyId) throws IOException { var key = loaded.get(keyId); if (key != null) return key; - var json = Files.readString(filename(keyId)); - try { - key = PublicJsonWebKey.Factory.newPublicJwk(json); - loaded.put(keyId, key); - return key; - } catch (JoseException e) { - throw new KeyManager.KeyCreationException(e); - } + key = Files.readString(filename(keyId)); + loaded.put(keyId, key); + return key; } @Override - public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException { - Files.writeString(filename(jsonWebKey.getKeyId()), jsonWebKey.toJson(INCLUDE_PRIVATE)); + public KeyStorage store(String keyId, String jsonWebKey) throws IOException { + Files.writeString(filename(keyId), jsonWebKey); return this; } diff --git a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java index 6c863d9..d41888a 100644 --- a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java +++ b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java @@ -2,9 +2,6 @@ package de.srsoftware.oidc.datastore.sqlite; -import static org.jose4j.jwk.JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE; - -import de.srsoftware.oidc.api.KeyManager; import de.srsoftware.oidc.api.KeyStorage; import java.io.IOException; import java.sql.Connection; @@ -13,7 +10,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.jose4j.jwk.PublicJsonWebKey; -import org.jose4j.lang.JoseException; public class SqliteKeyStore extends SqliteStore implements KeyStorage { private static final String STORE_VERSION = "key_store_version"; @@ -99,30 +95,24 @@ public class SqliteKeyStore extends SqliteStore implements KeyStorage { } @Override - public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException { + public String loadJson(String keyId) throws IOException { try { var stmt = conn.prepareStatement(LOAD_KEY); stmt.setString(1, keyId); var rs = stmt.executeQuery(); String json = null; - if (rs.next()) { - json = rs.getString(1); - } + if (rs.next()) json = rs.getString(1); rs.close(); - return PublicJsonWebKey.Factory.newPublicJwk(json); - } catch (JoseException e) { - throw new KeyManager.KeyCreationException(e); + return json; } catch (SQLException e) { throw new RuntimeException(e); } } @Override - public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException { + public KeyStorage store(String keyId, String json) throws IOException { try { - var keyId = jsonWebKey.getKeyId(); - var json = jsonWebKey.toJson(INCLUDE_PRIVATE); - var stmt = conn.prepareStatement(SAVE_KEY); + var stmt = conn.prepareStatement(SAVE_KEY); stmt.setString(1, keyId); stmt.setString(2, json); stmt.setString(3, json); diff --git a/de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js b/de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js index 1818fd7..69e8cda 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/scripts/index.js @@ -24,4 +24,6 @@ function handleDash(response){ }); } -fetch(client_controller+"/dash").then(handleDash) \ No newline at end of file +document.addEventListener("DOMContentLoaded", function(event) { // wait until page loaded + fetch(client_controller+"/dash").then(handleDash) +}); diff --git a/de.srsoftware.oidc.web/src/main/resources/en/todo.html b/de.srsoftware.oidc.web/src/main/resources/en/todo.html index da6db37..a77a232 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/todo.html +++ b/de.srsoftware.oidc.web/src/main/resources/en/todo.html @@ -14,8 +14,16 @@

to do…

  • implement token refresh
  • -
  • Verschlüsselung im config-File
  • Configuration im Frontend
  • +
  • + On-Load events: manchmal feuern Requests, die User-Daten benötigen bevor der entsprechende User geladen wurde +
      +
    • Settings
    • +
    • Dashboard
    • +
    • Navigation
    • +
    • Clients
    • +
    +