/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.users.impl.validation;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
import org.apache.commons.codec.binary.Base64;
import org.onebusaway.users.services.validation.KeyValidationProvider;
import org.onebusaway.users.utility.Digester;
import org.onebusaway.users.utility.DigesterSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This {@link KeyValidationProvider} is used to validate passwords. A password
* is salted with a randomly-generated salt and then an SHA-256 digest of the
* salt + password is generated as the key. This key is appropriate for
* long-term storage, as the salt hopefully prevents against rainbow attacks.
*
* The key can then be used to validate a password in the future. When
* validating, the key is expected as the primary argument to
* {@link #isValidKey(String, String...)}, while the password is expected as the
* first optional argument.
*
* @author bdferris
*
*/
public class SaltedPasswordValidationProviderV1Impl implements
KeyValidationProvider {
public static final String PROVIDER_ID = "spv1";
private static Logger _log = LoggerFactory.getLogger(SaltedPasswordValidationProviderV1Impl.class);
private static final String ALGORITHM = "SHA-256";
private static final String KEY_DELIMITER = "|";
private Base64 _coder = new Base64();
public String getId() {
return PROVIDER_ID;
}
public String generateKey(String input, String... arguments) {
String salt = generateRandomSalt(10);
byte[] signature = Digester.digestValue(ALGORITHM, salt.getBytes(),
input.getBytes());
String encodedSig = new String(_coder.encode(signature));
return salt + KEY_DELIMITER + encodedSig;
}
public boolean isValidKey(String key, String... arguments) {
if (arguments.length != 1) {
_log.warn("expected one password argument to isValidKey call");
return false;
}
String[] tokens = key.split("\\|");
if (tokens.length != 2)
return false;
String salt = tokens[0];
String encodedSig = tokens[1];
byte[] expectedSignature = _coder.decode(encodedSig.getBytes());
byte[] actualSignature = Digester.digestValue(ALGORITHM, salt.getBytes(),
arguments[0].getBytes());
return Arrays.equals(actualSignature, expectedSignature);
}
@Override
public void getKeyInfo(Map<String, String> info, String subKey,
String... arguments) {
String value = DigesterSignature.getDecodedValue(subKey);
info.put("salt", value);
}
private String generateRandomSalt(int digits) {
Random r = new Random();
StringBuilder b = new StringBuilder();
for (int i = 0; i < digits; i++) {
char c = (char) ('a' + r.nextInt(26));
b.append(c);
}
return b.toString();
}
}