From f7117c9df81e47cffd869b5398916fa815281edf Mon Sep 17 00:00:00 2001 From: mrflick72 Date: Fri, 1 Nov 2024 10:55:13 +0100 Subject: [PATCH] WIP plain java key management for non aws environment move from postgres to aws/ non aws profile --- local-environment/readme.md | 9 ++-- .../server/account/AccountConfig.kt | 8 +-- .../config/AuthorizationServerConfig.kt | 4 +- .../server/config/DatabaseConfig.kt | 2 +- .../vauthenticator/server/keys/KeyConfig.kt | 34 +++++++++--- .../adapter/local/BouncyCastleKeyDecrypter.kt | 12 +++++ .../adapter/local/BouncyCastleKeyGenerator.kt | 32 ++++++++++++ ...cyCastleKeyGeneratorMasterKeyRepository.kt | 14 +++++ .../local/KeyCryptographicOperations.kt | 52 +++++++++++++++++++ .../vauthenticator/server/mfa/MfaConfig.kt | 4 +- .../clientapp/ClientApplicationConfig.kt | 8 +-- .../server/role/PermissionConfig.kt | 8 +-- .../server/ticket/TicketConfig.kt | 4 +- .../keys/adapter/kms/KmsKeyGeneratorTest.kt | 8 +++ .../local/BouncyCastleKeyGeneratorTest.kt | 9 ++++ 15 files changed, 177 insertions(+), 31 deletions(-) create mode 100644 src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyDecrypter.kt create mode 100644 src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGenerator.kt create mode 100644 src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorMasterKeyRepository.kt create mode 100644 src/main/kotlin/com/vauthenticator/server/keys/adapter/local/KeyCryptographicOperations.kt create mode 100644 src/test/kotlin/com/vauthenticator/server/keys/adapter/kms/KmsKeyGeneratorTest.kt create mode 100644 src/test/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorTest.kt diff --git a/local-environment/readme.md b/local-environment/readme.md index 30a3bde6..25a87334 100644 --- a/local-environment/readme.md +++ b/local-environment/readme.md @@ -94,11 +94,8 @@ cd ../../../../../communication/default/mail cp * ../../../dist/mail/templates ``` -## Postgres usage +## Installation in a NON AWS Environment -Postgres is an available option as storage, it is experimental right now, and it is supported only for account and roles. +Postgres and plain java key management is an available option -In order to activate it is needed to add the corresponding spring profile '''experimental_database_persistence''' and -for the init process add to the docker run the environment variable '''experimental_database_persistence=true'''with the command like below: - -> docker run --pull=always -e experimental_database_persistence=true -it mrflick72/vauthenticator-local-tenant-installer:latest \ No newline at end of file +In order to activate aws native service usage like KMS, DynamoDB and so on please use the spring profile '''aws''' default otherwise \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/account/AccountConfig.kt b/src/main/kotlin/com/vauthenticator/server/account/AccountConfig.kt index 07e98351..6bb6b0d0 100644 --- a/src/main/kotlin/com/vauthenticator/server/account/AccountConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/account/AccountConfig.kt @@ -56,7 +56,7 @@ class AccountConfig { @Bean("accountRepository") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcAccountRepository( jdbcTemplate: JdbcTemplate ) = JdbcAccountRepository(jdbcTemplate) @@ -68,7 +68,7 @@ class AccountConfig { havingValue = "false", matchIfMissing = true ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbAccountRepository( mapper: ObjectMapper, dynamoDbClient: DynamoDbClient, @@ -85,7 +85,7 @@ class AccountConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun cachedDynamoDbAccountRepository( mapper: ObjectMapper, dynamoDbClient: DynamoDbClient, @@ -106,7 +106,7 @@ class AccountConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun accountCacheOperation( redisTemplate: RedisTemplate<*, *>, @Value("\${vauthenticator.dynamo-db.account.cache.ttl}") ttl: Duration, diff --git a/src/main/kotlin/com/vauthenticator/server/config/AuthorizationServerConfig.kt b/src/main/kotlin/com/vauthenticator/server/config/AuthorizationServerConfig.kt index 99e2002c..61f3dd62 100644 --- a/src/main/kotlin/com/vauthenticator/server/config/AuthorizationServerConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/config/AuthorizationServerConfig.kt @@ -98,14 +98,14 @@ class AuthorizationServerConfig { } @Bean("oAuth2AuthorizationService") - @Profile("!experimental_database_persistence") + @Profile("!aws") fun redisOAuth2AuthorizationService(redisTemplate: RedisTemplate): OAuth2AuthorizationService { return RedisOAuth2AuthorizationService(redisTemplate) } @Bean("oAuth2AuthorizationService") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcOAuth2AuthorizationService( jdbcTemplate : JdbcTemplate, registeredClientRepository : RegisteredClientRepository diff --git a/src/main/kotlin/com/vauthenticator/server/config/DatabaseConfig.kt b/src/main/kotlin/com/vauthenticator/server/config/DatabaseConfig.kt index d67ae8c4..0041d2cb 100644 --- a/src/main/kotlin/com/vauthenticator/server/config/DatabaseConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/config/DatabaseConfig.kt @@ -10,6 +10,6 @@ import org.springframework.context.annotation.Profile @Configuration @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class, DataSourceTransactionManagerAutoConfiguration::class, HibernateJpaAutoConfiguration::class]) -@Profile("!experimental_database_persistence") +@Profile("!aws") class ExcludeDatabaseConfig diff --git a/src/main/kotlin/com/vauthenticator/server/keys/KeyConfig.kt b/src/main/kotlin/com/vauthenticator/server/keys/KeyConfig.kt index c13fb96a..ffdc8aca 100644 --- a/src/main/kotlin/com/vauthenticator/server/keys/KeyConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/keys/KeyConfig.kt @@ -4,6 +4,10 @@ import com.vauthenticator.server.keys.adapter.dynamo.DynamoDbKeyStorage import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage import com.vauthenticator.server.keys.adapter.kms.KmsKeyDecrypter import com.vauthenticator.server.keys.adapter.kms.KmsKeyGenerator +import com.vauthenticator.server.keys.adapter.local.BouncyCastleKeyDecrypter +import com.vauthenticator.server.keys.adapter.local.BouncyCastleKeyGenerator +import com.vauthenticator.server.keys.adapter.local.BouncyCastleKeyGeneratorMasterKeyRepository +import com.vauthenticator.server.keys.adapter.local.KeyCryptographicOperations import com.vauthenticator.server.keys.domain.* import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean @@ -18,14 +22,32 @@ import java.util.* @Configuration(proxyBeanMethods = false) class KeyConfig { - @Bean - fun keyGenerator(kmsClient: KmsClient): KeyGenerator = KmsKeyGenerator(kmsClient) + @Profile("aws") + @Bean("keyGenerator") + fun kmsKeyGenerator(kmsClient: KmsClient): KeyGenerator = KmsKeyGenerator(kmsClient) - @Bean - fun keyDecrypter(kmsClient: KmsClient): KeyDecrypter = KmsKeyDecrypter(kmsClient) + @Profile("!aws") + @Bean("keyGenerator") + fun bouncyCastleKeyGenerator(kmsClient: KmsClient): KeyGenerator = BouncyCastleKeyGenerator( + KeyCryptographicOperations( + BouncyCastleKeyGeneratorMasterKeyRepository() + ) + ) + + @Profile("aws") + @Bean("keyDecrypter") + fun kmsKeyDecrypter(kmsClient: KmsClient): KeyDecrypter = KmsKeyDecrypter(kmsClient) + + @Profile("!aws") + @Bean("keyDecrypter") + fun bouncyCastleKeyDecrypter(): KeyDecrypter = BouncyCastleKeyDecrypter( + KeyCryptographicOperations( + BouncyCastleKeyGeneratorMasterKeyRepository() + ) + ) @Bean("keyStorage") - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbKeyStorage( clock: Clock, dynamoDbClient: DynamoDbClient, @@ -34,7 +56,7 @@ class KeyConfig { ) = DynamoDbKeyStorage(clock, dynamoDbClient, signatureTableName, mfaTableName) @Bean("keyStorage") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcKeyStorage(jdbcTemplate: JdbcTemplate, clock: Clock) = JdbcKeyStorage(jdbcTemplate, clock) @Bean("keyRepository") diff --git a/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyDecrypter.kt b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyDecrypter.kt new file mode 100644 index 00000000..439d4106 --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyDecrypter.kt @@ -0,0 +1,12 @@ +package com.vauthenticator.server.keys.adapter.local + +import com.vauthenticator.server.extentions.encoder +import com.vauthenticator.server.keys.domain.KeyDecrypter +import com.vauthenticator.server.keys.domain.MasterKid + +class BouncyCastleKeyDecrypter(private val keyCryptographicOperations: KeyCryptographicOperations) : KeyDecrypter { + override fun decryptKey(encrypted: String): String { + return encoder.encode(keyCryptographicOperations.decryptKeyWith(MasterKid(""), encrypted.toByteArray())) + .decodeToString() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGenerator.kt b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGenerator.kt new file mode 100644 index 00000000..60854ae4 --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGenerator.kt @@ -0,0 +1,32 @@ +package com.vauthenticator.server.keys.adapter.local + +import com.vauthenticator.server.keys.domain.DataKey +import com.vauthenticator.server.keys.domain.KeyGenerator +import com.vauthenticator.server.keys.domain.MasterKid +import java.util.* + + +class BouncyCastleKeyGenerator( + private val keyCryptographicOperations: KeyCryptographicOperations +) : KeyGenerator { + + + override fun dataKeyPairFor(masterKid: MasterKid): DataKey { + val generateRSAKeyPair = keyCryptographicOperations.generateRSAKeyPair() + return DataKey( + keyCryptographicOperations.encryptKeyWith(masterKid, generateRSAKeyPair.private.encoded), + Optional.of(generateRSAKeyPair.public.encoded) + ) + } + + override fun dataKeyFor(masterKid: MasterKid): DataKey { + val generateRSAKeyPair = keyCryptographicOperations.generateRSAKeyPair() + return DataKey( + keyCryptographicOperations.encryptKeyWith(masterKid, generateRSAKeyPair.private.encoded), + Optional.empty() + ) + } + + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorMasterKeyRepository.kt b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorMasterKeyRepository.kt new file mode 100644 index 00000000..ccbac96f --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorMasterKeyRepository.kt @@ -0,0 +1,14 @@ +package com.vauthenticator.server.keys.adapter.local + +import com.vauthenticator.server.extentions.toSha256 +import com.vauthenticator.server.keys.domain.MasterKid +val toSha256 = "secret".toSha256() + +class BouncyCastleKeyGeneratorMasterKeyRepository { + + //TODO to improve + fun maskerKeyFor(masterKeyId: MasterKid): String { + return toSha256 + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/KeyCryptographicOperations.kt b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/KeyCryptographicOperations.kt new file mode 100644 index 00000000..e4d7965c --- /dev/null +++ b/src/main/kotlin/com/vauthenticator/server/keys/adapter/local/KeyCryptographicOperations.kt @@ -0,0 +1,52 @@ +package com.vauthenticator.server.keys.adapter.local + +import com.vauthenticator.server.extentions.decoder +import com.vauthenticator.server.keys.domain.MasterKid +import org.bouncycastle.jce.provider.BouncyCastleProvider +import java.security.KeyPair +import java.security.KeyPairGenerator +import java.security.Security +import java.security.spec.RSAKeyGenParameterSpec +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec + + +class KeyCryptographicOperations( + private val repository: BouncyCastleKeyGeneratorMasterKeyRepository, +) { + companion object { + init { + Security.addProvider(BouncyCastleProvider()); + } + } + + fun generateRSAKeyPair(): KeyPair { + val keyPair: KeyPair + try { + val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC") + keyPairGenerator.initialize(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)) + keyPair = keyPairGenerator.generateKeyPair() + } catch (ex: Exception) { + throw IllegalStateException(ex) + } + return keyPair + } + + fun encryptKeyWith(masterKid: MasterKid, encodedPlainText: ByteArray): ByteArray { + val masterKey = repository.maskerKeyFor(masterKid); + val key = SecretKeySpec(masterKey.toByteArray(), "AES") + val cipher = Cipher.getInstance("AES") + cipher.init(Cipher.ENCRYPT_MODE, key) + return cipher.doFinal(decoder.decode(encodedPlainText)) + } + + fun decryptKeyWith(masterKid: MasterKid, encodedEncryptedText: ByteArray): ByteArray { + val masterKey = repository.maskerKeyFor(masterKid); + val key = SecretKeySpec(masterKey.toByteArray(), "AES") + val cipher = Cipher.getInstance("AES") + cipher.init(Cipher.DECRYPT_MODE, key) + return cipher.doFinal(decoder.decode(encodedEncryptedText)) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/vauthenticator/server/mfa/MfaConfig.kt b/src/main/kotlin/com/vauthenticator/server/mfa/MfaConfig.kt index 24c50c34..70b0aa77 100644 --- a/src/main/kotlin/com/vauthenticator/server/mfa/MfaConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/mfa/MfaConfig.kt @@ -35,7 +35,7 @@ import java.util.* class MfaConfig { @Bean("mfaAccountMethodsRepository") - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbMfaAccountMethodsRepository( keyRepository: KeyRepository, dynamoDbClient: DynamoDbClient, @@ -52,7 +52,7 @@ class MfaConfig { ) { MfaDeviceId(UUID.randomUUID().toString()) } @Bean("mfaAccountMethodsRepository") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcMfaAccountMethodsRepository( keyRepository: KeyRepository, jdbcTemplate: JdbcTemplate, diff --git a/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/ClientApplicationConfig.kt b/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/ClientApplicationConfig.kt index c829b4c6..a55e5215 100644 --- a/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/ClientApplicationConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/oauth2/clientapp/ClientApplicationConfig.kt @@ -29,7 +29,7 @@ class ClientApplicationConfig { @Bean("clientApplicationRepository") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcClientApplicationRepository(jdbcTemplate: JdbcTemplate, objectMapper: ObjectMapper) : ClientApplicationRepository = JdbcClientApplicationRepository(jdbcTemplate, objectMapper) @@ -39,7 +39,7 @@ class ClientApplicationConfig { havingValue = "false", matchIfMissing = true ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbClientApplicationRepository( dynamoDbClient: DynamoDbClient, @Value("\${vauthenticator.dynamo-db.client-application.table-name}") clientAppTableName: String @@ -51,7 +51,7 @@ class ClientApplicationConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun cachedClientApplicationRepository( dynamoDbClient: DynamoDbClient, clientApplicationCacheOperation: CacheOperation, @@ -70,7 +70,7 @@ class ClientApplicationConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun clientApplicationCacheOperation( redisTemplate: RedisTemplate<*, *>, @Value("\${vauthenticator.dynamo-db.client-application.cache.ttl}") ttl: Duration, diff --git a/src/main/kotlin/com/vauthenticator/server/role/PermissionConfig.kt b/src/main/kotlin/com/vauthenticator/server/role/PermissionConfig.kt index d0ff5f07..d3461e45 100644 --- a/src/main/kotlin/com/vauthenticator/server/role/PermissionConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/role/PermissionConfig.kt @@ -23,7 +23,7 @@ import java.time.Duration class PermissionConfig { @Bean("roleRepository") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbcRoleRepository( jdbcTemplate: JdbcTemplate, @Value("\${vauthenticator.dynamo-db.role.protected-from-delete}") protectedRoleFromDeletion: List @@ -35,7 +35,7 @@ class PermissionConfig { havingValue = "false", matchIfMissing = true ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbRoleRepository( dynamoDbClient: DynamoDbClient, @Value("\${vauthenticator.dynamo-db.role.table-name}") roleTableName: String, @@ -49,7 +49,7 @@ class PermissionConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun cachedDynamoDbRoleRepository( mapper: ObjectMapper, roleCacheOperation: CacheOperation, @@ -68,7 +68,7 @@ class PermissionConfig { havingValue = "true", matchIfMissing = false ) - @Profile("!experimental_database_persistence") + @Profile("!aws") fun roleCacheOperation( redisTemplate: RedisTemplate<*, *>, @Value("\${vauthenticator.dynamo-db.role.cache.ttl}") ttl: Duration, diff --git a/src/main/kotlin/com/vauthenticator/server/ticket/TicketConfig.kt b/src/main/kotlin/com/vauthenticator/server/ticket/TicketConfig.kt index 803129cd..916ab68d 100644 --- a/src/main/kotlin/com/vauthenticator/server/ticket/TicketConfig.kt +++ b/src/main/kotlin/com/vauthenticator/server/ticket/TicketConfig.kt @@ -20,7 +20,7 @@ import java.util.* class TicketConfig { @Bean("ticketRepository") - @Profile("!experimental_database_persistence") + @Profile("!aws") fun dynamoDbTicketRepository( @Value("\${vauthenticator.dynamo-db.ticket.table-name}") tableName: String, dynamoDbClient: DynamoDbClient @@ -28,7 +28,7 @@ class TicketConfig { @Bean("ticketRepository") - @Profile("experimental_database_persistence") + @Profile("aws") fun jdbCTicketRepository( jdbcTemplate: JdbcTemplate, objectMapper: ObjectMapper diff --git a/src/test/kotlin/com/vauthenticator/server/keys/adapter/kms/KmsKeyGeneratorTest.kt b/src/test/kotlin/com/vauthenticator/server/keys/adapter/kms/KmsKeyGeneratorTest.kt new file mode 100644 index 00000000..c6309e45 --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/keys/adapter/kms/KmsKeyGeneratorTest.kt @@ -0,0 +1,8 @@ +package com.vauthenticator.server.keys.adapter.kms + +import org.junit.jupiter.api.Assertions.* + +class KmsKeyGeneratorTest { + + +} \ No newline at end of file diff --git a/src/test/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorTest.kt b/src/test/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorTest.kt new file mode 100644 index 00000000..eae7f8ab --- /dev/null +++ b/src/test/kotlin/com/vauthenticator/server/keys/adapter/local/BouncyCastleKeyGeneratorTest.kt @@ -0,0 +1,9 @@ +package com.vauthenticator.server.keys.adapter.local + +import org.junit.jupiter.api.Assertions.* + +class BouncyCastleKeyGeneratorTest { + + + +} \ No newline at end of file