Browse Source

implemented EncryptedMailConfig, needs testing

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
sqlite
Stephan Richter 2 months ago
parent
commit
8debdc781b
  1. 2
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java
  2. 1
      de.srsoftware.oidc.app/build.gradle
  3. 12
      de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java
  4. 14
      de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Configuration.java
  5. 22
      de.srsoftware.oidc.datastore.encrypted/build.gradle
  6. 71
      de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedConfig.java
  7. 105
      de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedMailConfig.java
  8. 1
      settings.gradle

2
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java

@ -18,6 +18,7 @@ public class Constants {
public static final String CONFIG_PATH = "LIGHTOIDC_CONFIG_PATH"; public static final String CONFIG_PATH = "LIGHTOIDC_CONFIG_PATH";
public static final String CONFIRMED = "confirmed"; public static final String CONFIRMED = "confirmed";
public static final String DAYS = "days"; public static final String DAYS = "days";
public static final String ENCRYPTION_KEY = "encryption_key";
public static final String ERROR_DESCRIPTION = "error_description"; public static final String ERROR_DESCRIPTION = "error_description";
public static final String EXPIRATION = "expiration"; public static final String EXPIRATION = "expiration";
public static final String EXPIRES_IN = "expires_in"; public static final String EXPIRES_IN = "expires_in";
@ -39,6 +40,7 @@ public class Constants {
public static final String REDIRECT_URIS = "redirect_uris"; public static final String REDIRECT_URIS = "redirect_uris";
public static final String REQUEST_NOT_SUPPORTED = "request_not_supported"; public static final String REQUEST_NOT_SUPPORTED = "request_not_supported";
public static final String RESPONSE_TYPE = "response_type"; public static final String RESPONSE_TYPE = "response_type";
public static final String SALT = "salt";
public static final String SCOPE = "scope"; public static final String SCOPE = "scope";
public static final String SECRET = "secret"; public static final String SECRET = "secret";
public static final String SESSION_DURATION = "session_duration"; public static final String SESSION_DURATION = "session_duration";

1
de.srsoftware.oidc.app/build.gradle

@ -19,6 +19,7 @@ dependencies {
implementation project(':de.srsoftware.oidc.backend') implementation project(':de.srsoftware.oidc.backend')
implementation project(':de.srsoftware.oidc.web') implementation project(':de.srsoftware.oidc.web')
implementation project(':de.srsoftware.utils') implementation project(':de.srsoftware.utils')
implementation project(':de.srsoftware.oidc.datastore.encrypted')
implementation project(':de.srsoftware.oidc.datastore.file') implementation project(':de.srsoftware.oidc.datastore.file')
implementation project(':de.srsoftware.oidc.datastore.sqlite') implementation project(':de.srsoftware.oidc.datastore.sqlite')
implementation 'org.json:json:20240303' implementation 'org.json:json:20240303'

12
de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java

@ -18,6 +18,7 @@ import de.srsoftware.logging.ColorLogger;
import de.srsoftware.oidc.api.*; 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.EncryptedMailConfig;
import de.srsoftware.oidc.datastore.file.FileStoreProvider; import de.srsoftware.oidc.datastore.file.FileStoreProvider;
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore; import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
import de.srsoftware.oidc.datastore.sqlite.*; import de.srsoftware.oidc.datastore.sqlite.*;
@ -110,10 +111,19 @@ public class Application {
private static MailConfig setupMailConfig(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException { private static MailConfig setupMailConfig(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
var mailConfigLocation = new File(config.getOrDefault("mail_config_storage",defaultFile)); var mailConfigLocation = new File(config.getOrDefault("mail_config_storage",defaultFile));
return switch (extension(mailConfigLocation)){ var mailConfig = switch (extension(mailConfigLocation)){
case "db", "sqlite", "sqlite3" -> new SqliteMailConfig(connectionProvider.get(mailConfigLocation)); case "db", "sqlite", "sqlite3" -> new SqliteMailConfig(connectionProvider.get(mailConfigLocation));
default -> fileStoreProvider.get(mailConfigLocation); default -> fileStoreProvider.get(mailConfigLocation);
}; };
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
var salt = config.getOrDefault(SALT,uuid());
if (encryptionKey.isPresent()){
mailConfig = new EncryptedMailConfig(mailConfig,encryptionKey.get(),salt);
}
return mailConfig;
} }
private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider, UuidHasher passHasher) throws SQLException { private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider, UuidHasher passHasher) throws SQLException {

14
de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Configuration.java

@ -1,11 +1,14 @@
/* © SRSoftware 2024 */ /* © SRSoftware 2024 */
package de.srsoftware.oidc.app; package de.srsoftware.oidc.app;
import static java.util.Optional.empty;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
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.Optional;
import org.json.JSONObject; import org.json.JSONObject;
public class Configuration { public class Configuration {
@ -30,6 +33,17 @@ public class Configuration {
return json.getString(key); return json.getString(key);
} }
public <T> Optional<T> get(String key) {
if (!json.has(key)) return empty();
var o = json.get(key);
try {
@SuppressWarnings("unchecked") var result = (T)o;
return Optional.of(result);
} catch (Exception e) {
return empty();
}
}
public Configuration save() { public Configuration save() {
try { try {
Files.writeString(storageFile, json.toString(2)); Files.writeString(storageFile, json.toString(2));

22
de.srsoftware.oidc.datastore.encrypted/build.gradle

@ -0,0 +1,22 @@
plugins {
id 'java'
}
group = 'de.srsoftware'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation project(':de.srsoftware.oidc.api')
implementation 'com.sun.mail:jakarta.mail:2.0.1'
}
test {
useJUnitPlatform()
}

71
de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedConfig.java

@ -0,0 +1,71 @@
/* © SRSoftware 2024 */
package de.srsoftware.oidc.datastore.encrypted;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptedConfig {
private final Cipher cipher;
private static final int KEY_LENGTH = 256;
private static final int ITERATION_COUNT = 65536;
private final SecretKeySpec secretKeySpec;
public EncryptedConfig(String key, String salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(key.toCharArray(), salt.getBytes(), ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
secretKeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
throw new RuntimeException(ex);
}
}
public String encrypt(String plain) {
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);
IvParameterSpec initVector = new IvParameterSpec(iv);
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, initVector);
byte[] cipherText = cipher.doFinal(plain.getBytes(UTF_8));
byte[] encryptedData = new byte[iv.length + cipherText.length];
System.arraycopy(iv, 0, encryptedData, 0, iv.length);
System.arraycopy(cipherText, 0, encryptedData, iv.length, cipherText.length);
return Base64.getEncoder().encodeToString(encryptedData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String decrypt(String secret) {
byte[] encryptedData = Base64.getDecoder().decode(secret);
byte[] iv = new byte[16];
System.arraycopy(encryptedData, 0, iv, 0, iv.length);
IvParameterSpec ivspec = new IvParameterSpec(iv);
try {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivspec);
byte[] cipherText = new byte[encryptedData.length - 16];
System.arraycopy(encryptedData, 16, cipherText, 0, cipherText.length);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText, UTF_8);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

105
de.srsoftware.oidc.datastore.encrypted/src/main/java/de/srsoftware/oidc/datastore/encrypted/EncryptedMailConfig.java

@ -0,0 +1,105 @@
/* © SRSoftware 2024 */
package de.srsoftware.oidc.datastore.encrypted;
/* © SRSoftware 2024 */
import static de.srsoftware.oidc.api.Constants.*;
import de.srsoftware.oidc.api.MailConfig;
import jakarta.mail.Authenticator;
import jakarta.mail.PasswordAuthentication;
public class EncryptedMailConfig extends EncryptedConfig implements MailConfig {
private final MailConfig storage;
private Authenticator auth;
public EncryptedMailConfig(MailConfig storage, String encryotionKey, String salt) {
super(encryotionKey, salt);
this.storage = storage;
}
@Override
public MailConfig save() {
return storage.save();
}
@Override
public String senderAddress() {
return decrypt(storage.senderAddress());
}
@Override
public MailConfig senderAddress(String newValue) {
storage.senderAddress(encrypt(newValue));
return this;
}
@Override
public String senderPassword() {
return decrypt(storage.senderPassword());
}
@Override
public MailConfig senderPassword(String newValue) {
storage.senderPassword(encrypt(newValue));
return this;
}
@Override
public boolean smtpAuth() {
return storage.smtpAuth();
}
@Override
public MailConfig smtpAuth(boolean newValue) {
storage.smtpAuth(newValue);
return this;
}
@Override
public Authenticator authenticator() {
if (auth == null) {
auth = new Authenticator() {
// override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderAddress(), senderPassword());
}
};
}
return auth;
}
@Override
public String smtpHost() {
return decrypt(storage.smtpHost());
}
@Override
public MailConfig smtpHost(String newValue) {
storage.smtpHost(encrypt(newValue));
return this;
}
@Override
public int smtpPort() {
return storage.smtpPort();
}
@Override
public MailConfig smtpPort(int newValue) {
storage.smtpPort(newValue);
return this;
}
@Override
public boolean startTls() {
return storage.startTls();
}
@Override
public MailConfig startTls(boolean newValue) {
storage.startTls(newValue);
return this;
}
}

1
settings.gradle

@ -8,4 +8,5 @@ include 'de.srsoftware.oidc.datastore.file'
include 'de.srsoftware.oidc.web' include 'de.srsoftware.oidc.web'
include 'de.srsoftware.utils' include 'de.srsoftware.utils'
include 'de.srsoftware.oidc.datastore.sqlite' include 'de.srsoftware.oidc.datastore.sqlite'
include 'de.srsoftware.oidc.datastore.encrypted'

Loading…
Cancel
Save