implemented EncryptedKeyStore
for this to work, the KeyStorage interface had to be extended Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -3,11 +3,19 @@ package de.srsoftware.oidc.api;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.jose4j.jwk.JsonWebKey;
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
|
|
||||||
public interface KeyStorage {
|
public interface KeyStorage {
|
||||||
public KeyStorage drop(String keyId);
|
public KeyStorage drop(String keyId);
|
||||||
public List<String> listKeys();
|
public List<String> listKeys();
|
||||||
public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException;
|
public default PublicJsonWebKey load(String keyId) throws IOException, JoseException {
|
||||||
public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException;
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import de.srsoftware.oidc.api.*;
|
|||||||
import de.srsoftware.oidc.api.data.User;
|
import de.srsoftware.oidc.api.data.User;
|
||||||
import de.srsoftware.oidc.backend.*;
|
import de.srsoftware.oidc.backend.*;
|
||||||
import de.srsoftware.oidc.datastore.encrypted.EncryptedClientService;
|
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.EncryptedMailConfig;
|
||||||
import de.srsoftware.oidc.datastore.encrypted.EncryptedUserService;
|
import de.srsoftware.oidc.datastore.encrypted.EncryptedUserService;
|
||||||
import de.srsoftware.oidc.datastore.file.FileStoreProvider;
|
import de.srsoftware.oidc.datastore.file.FileStoreProvider;
|
||||||
@@ -153,12 +154,21 @@ public class Application {
|
|||||||
|
|
||||||
private static KeyStorage setupKeyStore(Configuration config, Path defaultConfigDir) throws SQLException {
|
private static KeyStorage setupKeyStore(Configuration config, Path defaultConfigDir) throws SQLException {
|
||||||
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys")));
|
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys")));
|
||||||
|
KeyStorage keyStore = null;
|
||||||
if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) {
|
if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) {
|
||||||
return new PlaintextKeyStore(keyStorageLocation.toPath());
|
keyStore = new PlaintextKeyStore(keyStorageLocation.toPath());
|
||||||
} else { // SQLite
|
} else { // SQLite
|
||||||
var conn = connectionProvider.get(keyStorageLocation);
|
var conn = connectionProvider.get(keyStorageLocation);
|
||||||
return new SqliteKeyStore(conn);
|
keyStore = new SqliteKeyStore(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<String> 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<String, Object> map(String[] args) {
|
private static Map<String, Object> map(String[] args) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package de.srsoftware.oidc.backend;
|
|||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.http.PathHandler;
|
import de.srsoftware.http.PathHandler;
|
||||||
import de.srsoftware.oidc.api.KeyManager;
|
|
||||||
import de.srsoftware.oidc.api.KeyStorage;
|
import de.srsoftware.oidc.api.KeyStorage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.jose4j.jwk.JsonWebKey;
|
import org.jose4j.jwk.JsonWebKey;
|
||||||
@@ -33,9 +32,7 @@ public class KeyStoreController extends PathHandler {
|
|||||||
PublicJsonWebKey key = keyStore.load(keyId);
|
PublicJsonWebKey key = keyStore.load(keyId);
|
||||||
String keyJson = key.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
|
String keyJson = key.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
|
||||||
arr.put(new JSONObject(keyJson));
|
arr.put(new JSONObject(keyJson));
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (KeyManager.KeyCreationException e) {
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ dependencies {
|
|||||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
||||||
implementation project(':de.srsoftware.utils')
|
implementation project(':de.srsoftware.utils')
|
||||||
testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle")
|
testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle")
|
||||||
|
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -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<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String, String> store = new HashMap<>();
|
||||||
|
@Override
|
||||||
|
public KeyStorage drop(String keyId) {
|
||||||
|
store.remove(keyId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.DEBUG;
|
||||||
import static java.lang.System.Logger.Level.ERROR;
|
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 de.srsoftware.oidc.api.KeyStorage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
|
||||||
import org.jose4j.lang.JoseException;
|
|
||||||
|
|
||||||
public class PlaintextKeyStore implements KeyStorage {
|
public class PlaintextKeyStore implements KeyStorage {
|
||||||
public static System.Logger LOG = System.getLogger(PlaintextKeyStore.class.getSimpleName());
|
public static System.Logger LOG = System.getLogger(PlaintextKeyStore.class.getSimpleName());
|
||||||
|
|
||||||
private final Path dir;
|
private final Path dir;
|
||||||
private HashMap<String, PublicJsonWebKey> loaded = new HashMap<>();
|
private HashMap<String, String> loaded = new HashMap<>();
|
||||||
|
|
||||||
public PlaintextKeyStore(Path storageDir) {
|
public PlaintextKeyStore(Path storageDir) {
|
||||||
this.dir = storageDir;
|
this.dir = storageDir;
|
||||||
@@ -42,22 +38,17 @@ public class PlaintextKeyStore implements KeyStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException {
|
public String loadJson(String keyId) throws IOException {
|
||||||
var key = loaded.get(keyId);
|
var key = loaded.get(keyId);
|
||||||
if (key != null) return key;
|
if (key != null) return key;
|
||||||
var json = Files.readString(filename(keyId));
|
key = Files.readString(filename(keyId));
|
||||||
try {
|
loaded.put(keyId, key);
|
||||||
key = PublicJsonWebKey.Factory.newPublicJwk(json);
|
return key;
|
||||||
loaded.put(keyId, key);
|
|
||||||
return key;
|
|
||||||
} catch (JoseException e) {
|
|
||||||
throw new KeyManager.KeyCreationException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException {
|
public KeyStorage store(String keyId, String jsonWebKey) throws IOException {
|
||||||
Files.writeString(filename(jsonWebKey.getKeyId()), jsonWebKey.toJson(INCLUDE_PRIVATE));
|
Files.writeString(filename(keyId), jsonWebKey);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
package de.srsoftware.oidc.datastore.sqlite;
|
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 de.srsoftware.oidc.api.KeyStorage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@@ -13,7 +10,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
import org.jose4j.lang.JoseException;
|
|
||||||
|
|
||||||
public class SqliteKeyStore extends SqliteStore implements KeyStorage {
|
public class SqliteKeyStore extends SqliteStore implements KeyStorage {
|
||||||
private static final String STORE_VERSION = "key_store_version";
|
private static final String STORE_VERSION = "key_store_version";
|
||||||
@@ -99,30 +95,24 @@ public class SqliteKeyStore extends SqliteStore implements KeyStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException {
|
public String loadJson(String keyId) throws IOException {
|
||||||
try {
|
try {
|
||||||
var stmt = conn.prepareStatement(LOAD_KEY);
|
var stmt = conn.prepareStatement(LOAD_KEY);
|
||||||
stmt.setString(1, keyId);
|
stmt.setString(1, keyId);
|
||||||
var rs = stmt.executeQuery();
|
var rs = stmt.executeQuery();
|
||||||
String json = null;
|
String json = null;
|
||||||
if (rs.next()) {
|
if (rs.next()) json = rs.getString(1);
|
||||||
json = rs.getString(1);
|
|
||||||
}
|
|
||||||
rs.close();
|
rs.close();
|
||||||
return PublicJsonWebKey.Factory.newPublicJwk(json);
|
return json;
|
||||||
} catch (JoseException e) {
|
|
||||||
throw new KeyManager.KeyCreationException(e);
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException {
|
public KeyStorage store(String keyId, String json) throws IOException {
|
||||||
try {
|
try {
|
||||||
var keyId = jsonWebKey.getKeyId();
|
var stmt = conn.prepareStatement(SAVE_KEY);
|
||||||
var json = jsonWebKey.toJson(INCLUDE_PRIVATE);
|
|
||||||
var stmt = conn.prepareStatement(SAVE_KEY);
|
|
||||||
stmt.setString(1, keyId);
|
stmt.setString(1, keyId);
|
||||||
stmt.setString(2, json);
|
stmt.setString(2, json);
|
||||||
stmt.setString(3, json);
|
stmt.setString(3, json);
|
||||||
|
|||||||
@@ -24,4 +24,6 @@ function handleDash(response){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(client_controller+"/dash").then(handleDash)
|
document.addEventListener("DOMContentLoaded", function(event) { // wait until page loaded
|
||||||
|
fetch(client_controller+"/dash").then(handleDash)
|
||||||
|
});
|
||||||
|
|||||||
@@ -14,8 +14,16 @@
|
|||||||
<h1>to do…</h1>
|
<h1>to do…</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>implement token refresh</li>
|
<li>implement token refresh</li>
|
||||||
<li>Verschlüsselung im config-File</li>
|
|
||||||
<li>Configuration im Frontend</li>
|
<li>Configuration im Frontend</li>
|
||||||
|
<li>
|
||||||
|
On-Load events: manchmal feuern Requests, die User-Daten benötigen bevor der entsprechende User geladen wurde
|
||||||
|
<ul>
|
||||||
|
<li>Settings</li>
|
||||||
|
<li>Dashboard</li>
|
||||||
|
<li>Navigation</li>
|
||||||
|
<li>Clients</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user