adding PBKDF2_SHA256 from 389-ds

This commit is contained in:
Jörg Prante 2022-04-19 17:28:30 +02:00
parent afde7b4e37
commit 46611904ba
2 changed files with 64 additions and 2 deletions

View file

@ -1,7 +1,11 @@
package org.xbib.groovy.crypt package org.xbib.groovy.crypt
import javax.crypto.Mac import javax.crypto.Mac
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.security.MessageDigest import java.security.MessageDigest
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
@ -79,6 +83,33 @@ class CryptUtil {
base64Digest(plainText, salt, 'SHA-512', 'ssha512') base64Digest(plainText, salt, 'SHA-512', 'ssha512')
} }
static String pbkdf2(String plainText) {
// the 389-ds parameters for PBKDF2-SHA256
pbkdf2(plainText, randomHexString(64), 30000, 256)
}
static String pbkdf2(String plainText, String salt) {
// the 389-ds parameters for PBKDF2-SHA256: 30000 iterations, 64 bytes (128 hex) salt, 256 bytes hash length
if (salt.length() != 128) {
throw new IllegalArgumentException("salt must be 64 bytes")
}
pbkdf2(plainText, salt, 30000, 256)
}
static String pbkdf2(String plainText, String salt, int iterations, int hashLength) {
byte[] n = htonl(iterations).array() // 4 bytes for network byte order = native byte order
byte[] b = salt.decodeHex()
byte[] hash = pbkdf2(plainText.toCharArray(), b, iterations, hashLength * 8)
int len = n.length + b .length + hash.length
byte[] result = new byte[len]
ByteBuffer buffer = ByteBuffer.wrap(result)
buffer.put(n)
buffer.put(b)
buffer.put(hash)
// 4 + 64 + 256 = 324 bytes
"{PBKDF2_SHA256}${result.encodeBase64()}"
}
static String hmacSHA1(String plainText, String secret) { static String hmacSHA1(String plainText, String secret) {
hmac(plainText.getBytes(StandardCharsets.UTF_8), secret.getBytes(StandardCharsets.UTF_8), "HmacSHA1") hmac(plainText.getBytes(StandardCharsets.UTF_8), secret.getBytes(StandardCharsets.UTF_8), "HmacSHA1")
} }
@ -105,9 +136,32 @@ class CryptUtil {
mac.doFinal(plainText).encodeHex() mac.doFinal(plainText).encodeHex()
} }
static String random(int length) { /**
* Computes the PBKDF2 hash of a password.
*
* @param password the password to hash.
* @param salt the salt
* @param iterations the iteration count (slowness factor)
* @param bytes the length of the hash to compute in bytes
* @return the PBDKF2 hash of the password
*/
static byte[] pbkdf2(char[] plainText, byte[] salt, int iterations, int len) {
PBEKeySpec spec = new PBEKeySpec(plainText, salt, iterations, len)
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
skf.generateSecret(spec).getEncoded()
}
static String randomHexString(int length) {
randomBytes(length).encodeHex()
}
static byte[] randomBytes(int length) {
byte[] b = new byte[length] byte[] b = new byte[length]
random.nextBytes(b) random.nextBytes(b)
b.encodeHex() b
}
static ByteBuffer htonl(int value) {
ByteBuffer.allocate(4).order(ByteOrder.nativeOrder()).putInt(value)
} }
} }

View file

@ -169,4 +169,12 @@ class CryptTest {
String code = CryptUtil.ssha512(plaintext, salt) String code = CryptUtil.ssha512(plaintext, salt)
assertEquals('{ssha512}jeWuCXRjsvKh/vK548GP9ZCs4q9Sh1u700C8eONyV+EL/P810C8vlx9Eu4vRjHq/TDoGW8FE1l/P2KG3w9lHITxo8fR/Qdgv', code,'test SSHA-512 method') assertEquals('{ssha512}jeWuCXRjsvKh/vK548GP9ZCs4q9Sh1u700C8eONyV+EL/P810C8vlx9Eu4vRjHq/TDoGW8FE1l/P2KG3w9lHITxo8fR/Qdgv', code,'test SSHA-512 method')
} }
@Test
void testPBKDF2() {
String plaintext = 'geheim'
String salt = "3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f3c68f1f47f41d82f"
String code = CryptUtil.pbkdf2(plaintext, salt)
assertEquals("{PBKDF2_SHA256}MHUAADxo8fR/QdgvPGjx9H9B2C88aPH0f0HYLzxo8fR/QdgvPGjx9H9B2C88aPH0f0HYLzxo8fR/QdgvPGjx9H9B2C9DUj6t+vF3mSI5b6nExWWcUnA6DXTbEa25BIMZ5ERe9JIqjkBr2p0ot9D5x4LZx9evQNexOWb+ea/stJkmi3wWKwS/uzSpEc4NZSv/+W1ZWtnK6NMkxxRJPjXEOCrjbKCOktDwCjSelBAe/rt0DABUYoMw69c8qZ1toAIz1x6oN5y58ImMpVsPK/CkbmbeK0QtDbWYZK8V1SZ6cZlF6kngpGWnAcEilIHqCVjM1HZMI+mZz86h86ZHcbxp9twuENu3DHi3nIZRzILrRIsjWAkruSDw7W/jXseGmVeBj/22xbKSybZmXawFGM59k3U5fE+1WvudOfVzwFyVAxDxispF", code)
}
} }