diff --git a/rbcs-common/src/main/kotlin/net/woggioni/rbcs/common/PasswordSecurity.kt b/rbcs-common/src/main/kotlin/net/woggioni/rbcs/common/PasswordSecurity.kt index ecc0810..f7be95a 100644 --- a/rbcs-common/src/main/kotlin/net/woggioni/rbcs/common/PasswordSecurity.kt +++ b/rbcs-common/src/main/kotlin/net/woggioni/rbcs/common/PasswordSecurity.kt @@ -7,7 +7,18 @@ import javax.crypto.SecretKeyFactory import javax.crypto.spec.PBEKeySpec object PasswordSecurity { - private const val KEY_LENGTH = 256 + + enum class Algorithm( + val codeName : String, + val keyLength : Int, + val iterations : Int) { + PBEWithHmacSHA512_224AndAES_256("PBEWithHmacSHA512/224AndAES_256", 64, 1), + PBEWithHmacSHA1AndAES_256("PBEWithHmacSHA1AndAES_256",64, 1), + PBEWithHmacSHA384AndAES_128("PBEWithHmacSHA384AndAES_128", 64,1), + PBEWithHmacSHA384AndAES_256("PBEWithHmacSHA384AndAES_256",64,1), + PBKDF2WithHmacSHA512("PBKDF2WithHmacSHA512",512, 1), + PBKDF2WithHmacSHA384("PBKDF2WithHmacSHA384",384, 1); + } private fun concat(arr1: ByteArray, arr2: ByteArray): ByteArray { val result = ByteArray(arr1.size + arr2.size) @@ -23,22 +34,22 @@ object PasswordSecurity { return result } - fun hashPassword(password : String, salt : String? = null) : String { + fun hashPassword(password : String, salt : String? = null, algorithm : Algorithm = Algorithm.PBKDF2WithHmacSHA512) : String { val actualSalt = salt?.let(Base64.getDecoder()::decode) ?: SecureRandom().run { val result = ByteArray(16) nextBytes(result) result } - val spec: KeySpec = PBEKeySpec(password.toCharArray(), actualSalt, 10, KEY_LENGTH) - val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") + val spec: KeySpec = PBEKeySpec(password.toCharArray(), actualSalt, algorithm.iterations, algorithm.keyLength) + val factory = SecretKeyFactory.getInstance(algorithm.codeName) val hash = factory.generateSecret(spec).encoded return String(Base64.getEncoder().encode(concat(hash, actualSalt))) } - fun decodePasswordHash(passwordHash : String) : Pair { - val decoded = Base64.getDecoder().decode(passwordHash) - val hash = ByteArray(KEY_LENGTH / 8) - val salt = ByteArray(decoded.size - KEY_LENGTH / 8) + fun decodePasswordHash(encodedPasswordHash : String, algorithm: Algorithm = Algorithm.PBKDF2WithHmacSHA512) : Pair { + val decoded = Base64.getDecoder().decode(encodedPasswordHash) + val hash = ByteArray(algorithm.keyLength / 8) + val salt = ByteArray(decoded.size - algorithm.keyLength / 8) System.arraycopy(decoded, 0, hash, 0, hash.size) System.arraycopy(decoded, hash.size, salt, 0, salt.size) return hash to salt diff --git a/rbcs-common/src/test/kotlin/net/woggioni/rbcs/common/PasswordHashingTest.kt b/rbcs-common/src/test/kotlin/net/woggioni/rbcs/common/PasswordHashingTest.kt new file mode 100644 index 0000000..ef3733f --- /dev/null +++ b/rbcs-common/src/test/kotlin/net/woggioni/rbcs/common/PasswordHashingTest.kt @@ -0,0 +1,38 @@ +package net.woggioni.rbcs.common + +import net.woggioni.rbcs.common.PasswordSecurity.decodePasswordHash +import net.woggioni.rbcs.common.PasswordSecurity.hashPassword +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource +import java.security.Provider +import java.security.Security +import java.util.Base64 + + +class PasswordHashingTest { + + @EnumSource(PasswordSecurity.Algorithm::class) + @ParameterizedTest + fun test(algo: PasswordSecurity.Algorithm) { + val password = "password" + val encoded = hashPassword(password, algorithm = algo) + val (_, salt) = decodePasswordHash(encoded, algo) + Assertions.assertEquals(encoded, + hashPassword(password, salt = salt.let(Base64.getEncoder()::encodeToString), algorithm = algo) + ) + } + + @Test + fun listAvailableAlgorithms() { + Security.getProviders().asSequence() + .flatMap { provider: Provider -> provider.services.asSequence() } + .filter { service: Provider.Service -> "SecretKeyFactory" == service.type } + .map(Provider.Service::getAlgorithm) + .forEach { + println(it) + } + + } +} \ No newline at end of file