mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-24 04:52:16 +00:00
Polish SCryptPasswordEncoder
* JKD8 Base64 -> Spring Security's Base64 to continue to support older JDKs * Spaces to tabs * Javadoc cleanup * Remove of @Override to compile in Eclipse Issue gh-3702
This commit is contained in:
parent
7d02e259df
commit
fc75a679d9
@ -13,5 +13,4 @@ configure(project.tasks.withType(Test)) {
|
||||
|
||||
dependencies {
|
||||
optional 'org.bouncycastle:bcprov-jdk15on:1.54'
|
||||
testCompile 'org.assertj:assertj-core:3.3.0'
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -15,25 +15,43 @@
|
||||
*/
|
||||
package org.springframework.security.crypto.scrypt;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Decoder;
|
||||
import java.util.Base64.Encoder;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.bouncycastle.crypto.generators.SCrypt;
|
||||
import org.springframework.security.crypto.codec.Base64;
|
||||
import org.springframework.security.crypto.codec.Utf8;
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.bouncycastle.crypto.generators.SCrypt;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of PasswordEncoder that uses the SCrypt hashing function. Clients
|
||||
* can optionally supply a cpu cost parameter, a memory cost parameter and a parallelization parameter.
|
||||
* <p>
|
||||
* Implementation of PasswordEncoder that uses the SCrypt hashing function.
|
||||
* Clients can optionally supply a cpu cost parameter, a memory cost parameter
|
||||
* and a parallelization parameter.
|
||||
*</p>
|
||||
*
|
||||
* <p>
|
||||
* A few <a href=
|
||||
* "http://bouncy-castle.1462172.n4.nabble.com/Java-Bouncy-Castle-scrypt-implementation-td4656832.html">
|
||||
* warnings</a>:
|
||||
* </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>The currently implementation uses Bouncy castle which does not exploit
|
||||
* parallelism/optimizations that password crackers will, so there is an
|
||||
* unnecessary asymmetry between attacker and defender.</li>
|
||||
* <li>Scrypt is based on Salsa20 which performs poorly in Java (on par with
|
||||
* AES) but performs awesome (~4-5x faster) on SIMD capable platforms</li>
|
||||
* <li>While there are some that would disagree, consider reading "<a href="
|
||||
* http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html
|
||||
* ">Why I Don't Recommend Scrypt</a> (for password storage)"</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Shazin Sadakath
|
||||
* @author Rob Winch
|
||||
*
|
||||
*/
|
||||
public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
@ -55,11 +73,13 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cpu cost of the algorithm. must be power of 2 greater than 1
|
||||
* @param memory cost of the algorithm
|
||||
* @param parallelization of the algorithm
|
||||
* @param key length for the algorithm
|
||||
* @param salt length
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param cpuCost cpu cost of the algorithm (as defined in scrypt this is N). must be power of 2 greater than 1. Default is currently 16,348 or 2^14)
|
||||
* @param memoryCost memory cost of the algorithm (as defined in scrypt this is r) Default is currently 8.
|
||||
* @param parallelization the parallelization of the algorithm (as defined in scrypt this is p) Default is currently 1. Note that the implementation does not currently take advantage of parallelization.
|
||||
* @param key length for the algorithm (as defined in scrypt this is dkLen). The default is currently 32.
|
||||
* @param salt length (as defined in scrypt this is the length of S). The default is currently 64.
|
||||
*/
|
||||
public SCryptPasswordEncoder(int cpuCost, int memoryCost, int parallelization, int keyLength, int saltLength) {
|
||||
if (cpuCost <= 1) {
|
||||
@ -90,12 +110,10 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
this.saltGenerator = KeyGenerators.secureRandom(saltLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return digest(rawPassword, saltGenerator.generateKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String encodedPassword) {
|
||||
if(encodedPassword == null || encodedPassword.length() < keyLength) {
|
||||
logger.warn("Empty encoded password");
|
||||
@ -111,10 +129,9 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
return false;
|
||||
}
|
||||
|
||||
Decoder decoder = Base64.getDecoder();
|
||||
long params = Long.parseLong(parts[1], 16);
|
||||
byte[] salt = decoder.decode(parts[2]);
|
||||
byte[] derived = decoder.decode(parts[3]);
|
||||
byte[] salt = decodePart(parts[2]);
|
||||
byte[] derived = decodePart(parts[3]);
|
||||
|
||||
int cpuCost = (int) Math.pow(2, params >> 16 & 0xffff);
|
||||
int memoryCost = (int) params >> 8 & 0xff;
|
||||
@ -137,14 +154,20 @@ public class SCryptPasswordEncoder implements PasswordEncoder {
|
||||
byte[] derived = SCrypt.generate(Utf8.encode(rawPassword), salt, cpuCost, memoryCost, parallelization, 32);
|
||||
|
||||
String params = Long.toString(((int) (Math.log(cpuCost) / Math.log(2)) << 16L) | memoryCost << 8 | parallelization, 16);
|
||||
Encoder encoder = Base64.getEncoder();
|
||||
|
||||
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
|
||||
sb.append("$").append(params).append('$');
|
||||
sb.append(encoder.encodeToString(salt)).append('$');
|
||||
sb.append(encoder.encodeToString(derived));
|
||||
sb.append(encodePart(salt)).append('$');
|
||||
sb.append(encodePart(derived));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private byte[] decodePart(String part) {
|
||||
return Base64.decode(Utf8.encode(part));
|
||||
}
|
||||
|
||||
private String encodePart(byte[] part) {
|
||||
return Utf8.decode(Base64.encode(part));
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -151,7 +151,8 @@ dependencies {
|
||||
testCompile "junit:junit:$junitVersion",
|
||||
'org.mockito:mockito-core:1.10.19',
|
||||
"org.springframework:spring-test:$springVersion",
|
||||
'org.easytesting:fest-assert:1.4'
|
||||
'org.easytesting:fest-assert:1.4',
|
||||
"org.assertj:assertj-core:2.3.0"
|
||||
|
||||
// Use slf4j/logback for logging
|
||||
testRuntime "org.slf4j:jcl-over-slf4j:$slf4jVersion",
|
||||
|
Loading…
x
Reference in New Issue
Block a user