I generate a key and use the doFinal() from the cipher class to encrypt the password/username, now, when the user wants to login he inputs UN and PW then I take them what is the process I need to do so I compare the input to the database I saved the encrypted data in?
Writing this question I feel stupid but the truth is I am really new to this and my information could be remote from right so please move on to explaining and pass the what are you talking about part.
now the code I used :
public class Safety {
public static Users encryptUser(Users user){
Users usera=user;
try {
KeyGenerator kg = KeyGenerator.getInstance("AES/CBC/PKCS5Padding");
Key key=kg.generateKey();
Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
String fNE=new String(cipher.doFinal(user.getFirstname().getBytes()),"UTF-8");
String lNE=new String(cipher.doFinal(user.getLastname().getBytes()) , "UTF-8");
String userNameE= new String(cipher.doFinal(user.getUsername().getBytes()),"UTF-8");
String passWordE= new String(cipher.doFinal(user.getPassword().getBytes()),"UTF-8");
String eME= new String(cipher.doFinal(user.getEmail().getBytes()),"UTF-8");
String sQE= new String(cipher.doFinal(user.getsQ().getBytes()),"UTF-8");
String sAE= new String(cipher.doFinal(user.getsA().getBytes()),"UTF-8");
Users usere=new Users(fNE, lNE, userNameE, passWordE, eME, sQE, sAE, user.getUserID());
return usere;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
return usera;
}
public static String decryptuser(Users user){
//what should I do here exactly?
}
}
after a little of research and work this is what i have come up with :
public class Safety {
public static final String algorithm = "PBKDF2WithHmacSHA1";
public static final int saltbytesize = 24;
public static final int hashbytesize = 24;
public static final int iterations = 1000;
public static final int iIndex = 0;
public static final int sIndex = 1;
public static final int pbkIndex = 2;
public static Users passwordHash(Users user) throws NoSuchAlgorithmException, InvalidKeySpecException{
SecureRandom sR=new SecureRandom();
byte[] pws=new byte[saltbytesize];
sR.nextBytes(pws);
byte[] pwh=pbkdf2(user.getPassword().toCharArray(),pws,iterations,hashbytesize);
user.setPassword(toHex(pwh));
byte[] sas=new byte[saltbytesize];
sR.nextBytes(sas);
byte[] sah=pbkdf2(user.getsA().toCharArray(),sas,iterations,hashbytesize);
user.setsA(toHex(sah));
user.setUserhash(pws);
user.setSahash(sas);
return user;
}
public static boolean hashpassword(String username,String password,Users user) throws NoSuchAlgorithmException, InvalidKeySpecException{
byte[] pws=user.getUserhash();
byte[] pwh=pbkdf2(password.toCharArray(),pws,iterations,hashbytesize);
String searcher=toHex(pwh)+username;
String searched=user.getPassword()+user.getUsername();
if(searcher.equals(searched)){
return true;
}
return false;
}
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
return skf.generateSecret(spec).getEncoded();
}
private static String toHex(byte[] array)
{
BigInteger bi = new BigInteger(1, array);
String hex = bi.toString(16);
int paddingLength = (array.length * 2) - hex.length();
if(paddingLength > 0)
return String.format("%0" + paddingLength + "d", 0) + hex;
else
return hex;
}
}
and this is great for now how ever id like to make it work with SHA512 how can i do that?
You should not encrypt the password, you should hash it with the user name and a salt.
See Why should I hash passwords?
Related
im facing a problem in my code.
I want to encrypt the Password from the user and save it to an SQL Database.
The Code im Using to Encrypt the Password with AES:
String key = "1234567890123456"; // 128 bit key
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(Passwort.getBytes());
String verschlüsselt = new String(encrypted);
Now im saving the String "verschlüsselt" to my Database.
In my LoginGUI im getting the String with the Password from the database:
String psswd = res.getString("Passwort"); //Getting the Password from Database and saving it into String
System.out.println(psswd);
The PrintOut is: "!?¿[ŸÊm,r~¤u" which is the correct encrypted password.
Now i'm trying to decrypt the password with following code:
String key = "1234567890123456"; // 128 bit key
cipher.init(Cipher.DECRYPT_MODE, aesKey);
EPasswort = new String(cipher.doFinal(bpsswd)); //Im facing the problem here
I Know that i can't decrypt the String and that I have to convert the String into an byte[].
I've done that in several ways:
byte[] password = psswd.getBytes();
System.out.println(password);
password = psswd.getBytes("UTF-8");
System.out.println(password);
EPasswort = new String(cipher.doFinal(password));
But the Output is always something the decrypter can't work with:
[B#ab33b4
[B#189090b
How do i convert my String with the Password (!?¿[ŸÊm,r~¤u) into an byte[] so my decrypter can work with it?
Thanks for any help.
You should encrypt the String and than save it as a String, in this example I'm using a Base64 encoder and a different cypher but the logic behind is the same. You should save bytes or String instead of using the reference to your bytes, here an example:
#Test
public void testCrypt () {
final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
final int TAG_LENGTH_BIT = 128;
final int IV_LENGTH_BYTE = 12;
final int SALT_LENGTH_BYTE = 16;
final String password = "password";
final byte[] salt = getRandomNonce(SALT_LENGTH_BYTE);
final byte[] iv = getRandomNonce(IV_LENGTH_BYTE);
final SecretKey aesKeyFromPassword = getAESKeyFromPassword(password.toCharArray(), salt);
String msg = "Test";
String out = "";
try {
out = encrypt(ENCRYPT_ALGO, TAG_LENGTH_BIT, iv, aesKeyFromPassword, msg);
} catch (Exception e) {
e.printStackTrace();
}
byte[] plainText = new byte[0];
try {
plainText = decrypt(ENCRYPT_ALGO, TAG_LENGTH_BIT, iv, aesKeyFromPassword, out);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Decrypted " + out + " in: " + new String(plainText, StandardCharsets.ISO_8859_1));
}
following the encrypt method:
private String encrypt(final String ENCRYPT_ALGO, final int TAG_LENGTH_BIT, final byte[] iv,
final SecretKey aesKeyFromPassword, String msg) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
final byte[] pText = msg.getBytes(StandardCharsets.ISO_8859_1);
Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
final byte[] cipherText = cipher.doFinal(pText);
String out = Base64.getEncoder().encodeToString(cipherText);
System.out.println("Encrypted " + msg + " as: " + out);
return out;
}
private static SecretKey getAESKeyFromPassword(final char[] password, final byte[] salt) {
final int KEY_LENGTH = 128;
final int ITERATION_COUNT = 65536;
try {
final SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
final KeySpec spec = new PBEKeySpec(password, salt, ITERATION_COUNT, KEY_LENGTH);
return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
} catch (final Exception e) {
LOGGER.error(e);
throw new RedException(e);
}
}
private static byte[] getRandomNonce(final int numBytes) {
final byte[] nonce = new byte[numBytes];
new SecureRandom().nextBytes(nonce);
return nonce;
}
Once you save the msgCrypted in db you should retrieve it and process it to decrypt like this:
private byte[] decrypt(final String ENCRYPT_ALGO, final int TAG_LENGTH_BIT, final byte[] iv,
final SecretKey aesKeyFromPassword, String out) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
Cipher dCipher = Cipher.getInstance(ENCRYPT_ALGO);
dCipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
final byte[] plainText = dCipher.doFinal(Base64.getDecoder().decode(out));
return plainText;
}
In the console you should have something like this:
You are printing the reference to the byte array, not the elements.
Run this for demonstration:
public static void main (String [] args) {
String s = "12345678";
byte [] b = s.getBytes ();
System.out.println (b);
for (int i = 0; i < 8; i++) {
System.out.println (b [i]);
}
}
Output:
[B#7a81197d
49
50
51
52
53
54
55
56
Since yesterday when I posted the frist time I learned a lot. So what I am trying to do now is to implement user console input instead of the given values, written in bold, encrypt, save them in the file and then keep verifying next user input with the encrypted values saved in the file until correct or up to 10 times.
String openPwd = "my password is datasecurity";
String openUser = "a user is ME";
The first question is: can I implement second user input and verification in the same main method of the class?
Moreover I encrypt values with two way encryption AES (Now I know that is not the safest way to encrypt) and one way encryption with hash and salt would be the safest option due to the number of reasons. Also I save password and key in the system file, since setting up database would be too time consuming for the task.
The second question is: can I use PBKDF2 and salt instead of encryption with AES if I do not save password and user name in dbms, but in system file instead? How will verification process in encryption case and PBKDF2 with salt differ?
public class PasswordEncryption {
public static final String AES = "AES";
public static String encrypt(String value, File keyFile)
throws GeneralSecurityException, IOException {
if (!keyFile.exists()) {
KeyGenerator keyGen = KeyGenerator
.getInstance(PasswordEncryption.AES);
keyGen.init(128);
SecretKey sk = keyGen.generateKey();
FileWriter fw = new FileWriter(keyFile);
fw.write(byteArrayToHexString(sk.getEncoded()));
fw.flush();
fw.close();
}
SecretKeySpec sks = getSecretKeySpec(keyFile);
Cipher cipher = Cipher.getInstance(PasswordEncryption.AES);
cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
byte[] encrypted = cipher.doFinal(value.getBytes());
return byteArrayToHexString(encrypted);
}
public static String decrypt(String message, File keyFile)
throws GeneralSecurityException, IOException {
SecretKeySpec sks = getSecretKeySpec(keyFile);
Cipher cipher = Cipher.getInstance(PasswordEncryption.AES);
cipher.init(Cipher.DECRYPT_MODE, sks);
byte[] decrypted = cipher.doFinal(hexStringToByteArray(message));
return new String(decrypted);
}
private static SecretKeySpec getSecretKeySpec(File keyFile)
throws NoSuchAlgorithmException, IOException {
byte[] key = readKeyFile(keyFile);
SecretKeySpec sks = new SecretKeySpec(key, PasswordEncryption.AES);
return sks;
}
private static byte[] readKeyFile(File keyFile)
throws FileNotFoundException {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(keyFile).useDelimiter("\\Z");
String keyValue = scanner.next();
scanner.close();
return hexStringToByteArray(keyValue);
}
private static String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
private static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
public static void main(String[] args) throws Exception {
final String KEY_FILE = "/Users/xxx/key";
final String PASSWORD_FILE = "/Users/xxx/properties";
String openPwd = "my password is datasecurity";
String openUser = "a user is ME";
Properties p1 = new Properties();
String encryptedPwd = PasswordEncryption.encrypt(openPwd, new File(
KEY_FILE));
String encryptedUser = PasswordEncryption.encrypt(openUser, new File(
KEY_FILE));
p1.put("password",encryptedPwd);
p1.put("user",encryptedUser);
p1.store(new FileWriter(PASSWORD_FILE),"");
// ==================
Properties p2 = new Properties();
p2.load(new FileReader(PASSWORD_FILE));
encryptedPwd = p2.getProperty("password");
encryptedUser = p2.getProperty("user");
System.out.println(encryptedPwd);
System.out.println(encryptedUser);
System.out.println(PasswordEncryption.decrypt(encryptedPwd, new File(
KEY_FILE)));
System.out.println(PasswordEncryption.decrypt(encryptedUser, new File(
KEY_FILE)));
}
}
There are two simple ways of doing this. The first would be to create a static class level variable in your first class that contains the value of the encryptedPwd. To make a class level variable you would define it like this:
public class FirstClass {
public static String encryptedPwd = "";
public static void main(String[] args){
...
FirstClass.encryptedPwd = encryptedPwd; //Set the class level variable equal to the encrypted password
...
}
}
Then you could access it from your Authenticate class using:
if (FirstClass.encryptedPwd.equals(inputHash)) //Getting the password variable in Authenticate class
or you could create a static method to access it as well which would allow you to keep the variable private.
public static String getPassword(){ //Static Method inside your first class that returns the encrypted password
return encryptedPwd;
}
if(FirstClass.getPassword().equals(inputHash)) //Method call in Authenticate class
There are other options too, but it depends on what your doing and the design you want for your project
I'm developping an Android App that need authentication. However, there an existing web application (using Symfony) with accounts, and I have to reuse the same accounts to authenticate my users.
In the DB, I have the basicals : username, salt, encryptedPassword, encryption_type(sha1)...
The problem is that I don't know how to authenticate my users, by using these informations.
If someone can help me solving this...
Many thanks!
Depending on the encryption algorithm you would do something like this:
public final boolean authenticate(String attemptedPassword, byte[] encryptedPassword, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
String algorithm = "PBKDF2WithHmacSHA1";
int derivedKeyLength = 160;
int iterations = 20000;
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, derivedKeyLength);
SecretKeyFactory f = SecretKeyFactory.getInstance(algorithm);
byte[] encryptedPassword = f.generateSecret(spec).getEncoded();
return Arrays.equals(encryptedPassword, encryptedAttemptedPassword);
}
You need to know which settings were used to generate the encrypted passwords in your database.
I finally succeded in doing that ! I've analysed the way Symfony check the password thanks to the PHP class "PluginsfGuardUser".
public final boolean authenticate(User user)
MessageDigest md = MessageDigest.getInstance(SHA1);
byte[] saltPassword = (user.getSalt() + user.getPassword()).getBytes();
byte[] encryptedPassword = md.digest(saltPassword);
String pass = byteArrayToHexString(encryptedPassword);
if (pass.equals(user.getDbPassword()))
{
return true;
}
else
{
return false;
}
}
private String byteArrayToHexString(byte[] array) {
String result = "";
for (int i = 0; i < array.length; i++) {
result
+= Integer.toString((array[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
I am working in a Spring-MVC application and for password reset function, i am sending an email, which contains the username+Joda Date. Before sending it, I would like to encrypt the content(date+username) in such a way that they can be perfectly reversed. As far as my theoretical understanding goes, doesn't reversing defeat the purpose of encryption in first place? If not, then I would like to encrypt them and then some mechanism to decrypt them. There are some solutions I found, one of them mentions retrieving but I cannot figure out which entity is which. I am pasting the code below. Kindly have a look :
Encryption and decryption :
byte[] userBytes = username.getBytes("UTF-8");
byte[] keyBytes = key.getBytes("UTF-8");
//XOR scramble
byte[] encrypted = new byte[userBytes.length];
for(int i = 0; i < userBytes.length; i++){
encrypted[i] = (byte)(userBytes[i] ^ keyBytes[i % keyBytes.length]);
}
BASE64Encoder encoder = new BASE64Encoder();
String encoded = encoder.encode(encrypted);
// webappB, decode the parameter
BASE64Decoder decoder = new BASE64Decoder();
byte[] decoded = decoder.decodeBuffer( encoded );
//XOR descramble
byte[] decrypted = new byte[decoded.length];
for(int i = 0; i < decoded.length; i++){
decrypted[i] = (byte)(decoded[i] ^ keyBytes[i % keyBytes.length] );
}
This class has two public methods, one for generating token and another for validating it. It is abridged from much larger and more complex code, so, some errors might be introduced. There are also some tests embedded, so you can play with it immediately. Any way, I hope it will be sufficient to get you on the right track.
package tokens;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class TokenUtils {
private static final String HMAC_ALGO = "HmacSHA256";
private static final String TOKEN_SEPARATOR = ":";
private static final long MAX_AGE = 1_000 * 60 * 60 * 24; // 24h
private TokenUtils() {
}
public static String createToken(String username, long timestamp, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
StringBuilder sb = new StringBuilder();
sb.append(generateTokenStringPublicPart(username, timestamp));
sb.append(TOKEN_SEPARATOR);
sb.append(computeSignature(username, timestamp, secretKey));
return sb.toString();
}
public static boolean verifyToken(String token, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
String[] parts = token.split(TOKEN_SEPARATOR);
boolean result = false;
if (parts.length == 3) {
String username = parts[0];
Long timestamp = Long.valueOf(parts[1]);
String signature = parts[2];
if (signature.equals(computeSignature(username, timestamp, secretKey))) {
if (System.currentTimeMillis() - timestamp < MAX_AGE) {
result = true;
}
}
}
return result;
}
private static String generateTokenStringPublicPart(String username, long timestamp) {
StringBuilder sb = new StringBuilder();
sb.append(username);
sb.append(TOKEN_SEPARATOR);
sb.append(timestamp);
return sb.toString();
}
private static String computeSignature(String username, long timestamp, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
StringBuilder sb = new StringBuilder();
sb.append(generateTokenStringPublicPart(username, timestamp));
SecretKeySpec sks = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_ALGO);
Mac hmac = Mac.getInstance(HMAC_ALGO);
hmac.init(sks);
return Base64.getUrlEncoder().encodeToString(hmac.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8)));
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException {
String secretKey = "secret_key";
String token = TokenUtils.createToken("marko", System.currentTimeMillis(), secretKey);
System.out.println(token);
System.out.println("Original token verification: " + TokenUtils.verifyToken(token, secretKey));
token = token.replaceAll("a", "b");
System.out.println("Tampered token verification: " + TokenUtils.verifyToken(token, secretKey));
token = TokenUtils.createToken("marko", System.currentTimeMillis() - 1_000 * 60 * 60 * 48, secretKey);
System.out.println("Expired token verification: " + TokenUtils.verifyToken(token, secretKey));
}
}
I've been looking around and the closest answer is : How to generate a random alpha-numeric string?
I want to follow this workflow according to this CrackStation tutorial:
To Store a Password
Generate a long random salt using a CSPRNG.
Prepend the salt to the password and hash it with a standard cryptographic hash function such as SHA256.
Save both the salt and the hash in the user's database record.
To Validate a Password
Retrieve the user's salt and hash from the database.
Prepend the salt to the given password and hash it using the same hash function.
Compare the hash of the given password with the hash from the database. If they match, the password is correct. Otherwise, the password is incorrect.
I don't know how to generate a SALT. I figured out how to generate a hash using the MessageDigest. I tried using SecureRandom but nextByte method produces garbled code.
Edit: I don't know which answer to choose, they're too complicated for me, I have decided to use jBCrypt; jBCript is easy to use, does all the complex stuff behind the scenes. so I'll let the community vote up for the best answer.
Inspired from this post and that post, I use this code to generate and verify hashed salted passwords. It only uses JDK provided classes, no external dependency.
The process is:
you create a salt with getNextSalt
you ask the user his password and use the hash method to generate a salted and hashed password. The method returns a byte[] which you can save as is in a database with the salt
to authenticate a user, you ask his password, retrieve the salt and hashed password from the database and use the isExpectedPassword method to check that the details match
/**
* A utility class to hash passwords and check passwords vs hashed values. It uses a combination of hashing and unique
* salt. The algorithm used is PBKDF2WithHmacSHA1 which, although not the best for hashing password (vs. bcrypt) is
* still considered robust and recommended by NIST .
* The hashed value has 256 bits.
*/
public class Passwords {
private static final Random RANDOM = new SecureRandom();
private static final int ITERATIONS = 10000;
private static final int KEY_LENGTH = 256;
/**
* static utility class
*/
private Passwords() { }
/**
* Returns a random salt to be used to hash a password.
*
* #return a 16 bytes random salt
*/
public static byte[] getNextSalt() {
byte[] salt = new byte[16];
RANDOM.nextBytes(salt);
return salt;
}
/**
* Returns a salted and hashed password using the provided hash.<br>
* Note - side effect: the password is destroyed (the char[] is filled with zeros)
*
* #param password the password to be hashed
* #param salt a 16 bytes salt, ideally obtained with the getNextSalt method
*
* #return the hashed password with a pinch of salt
*/
public static byte[] hash(char[] password, byte[] salt) {
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
Arrays.fill(password, Character.MIN_VALUE);
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
} finally {
spec.clearPassword();
}
}
/**
* Returns true if the given password and salt match the hashed value, false otherwise.<br>
* Note - side effect: the password is destroyed (the char[] is filled with zeros)
*
* #param password the password to check
* #param salt the salt used to hash the password
* #param expectedHash the expected hashed value of the password
*
* #return true if the given password and salt match the hashed value, false otherwise
*/
public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
byte[] pwdHash = hash(password, salt);
Arrays.fill(password, Character.MIN_VALUE);
if (pwdHash.length != expectedHash.length) return false;
for (int i = 0; i < pwdHash.length; i++) {
if (pwdHash[i] != expectedHash[i]) return false;
}
return true;
}
/**
* Generates a random password of a given length, using letters and digits.
*
* #param length the length of the password
*
* #return a random password
*/
public static String generateRandomPassword(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int c = RANDOM.nextInt(62);
if (c <= 9) {
sb.append(String.valueOf(c));
} else if (c < 36) {
sb.append((char) ('a' + c - 10));
} else {
sb.append((char) ('A' + c - 36));
}
}
return sb.toString();
}
}
You were right regarding how you want to generate salt i.e. its nothing but a random number. For this particular case it would protect your system from possible Dictionary attacks. Now, for the second problem what you could do is instead of using UTF-8 encoding you may want to use Base64. Here, is a sample for generating a hash. I am using Apache Common Codecs for doing the base64 encoding you may select one of your own
public byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
return bytes;
}
public String bytetoString(byte[] input) {
return org.apache.commons.codec.binary.Base64.encodeBase64String(input);
}
public byte[] getHashWithSalt(String input, HashingTechqniue technique, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(technique.value);
digest.reset();
digest.update(salt);
byte[] hashedBytes = digest.digest(stringToByte(input));
return hashedBytes;
}
public byte[] stringToByte(String input) {
if (Base64.isBase64(input)) {
return Base64.decodeBase64(input);
} else {
return Base64.encodeBase64(input.getBytes());
}
}
Here is some additional reference of the standard practice in password hashing directly from OWASP
Another version using SHA-3, I am using bouncycastle:
The interface:
public interface IPasswords {
/**
* Generates a random salt.
*
* #return a byte array with a 64 byte length salt.
*/
byte[] getSalt64();
/**
* Generates a random salt
*
* #return a byte array with a 32 byte length salt.
*/
byte[] getSalt32();
/**
* Generates a new salt, minimum must be 32 bytes long, 64 bytes even better.
*
* #param size the size of the salt
* #return a random salt.
*/
byte[] getSalt(final int size);
/**
* Generates a new hashed password
*
* #param password to be hashed
* #param salt the randomly generated salt
* #return a hashed password
*/
byte[] hash(final String password, final byte[] salt);
/**
* Expected password
*
* #param password to be verified
* #param salt the generated salt (coming from database)
* #param hash the generated hash (coming from database)
* #return true if password matches, false otherwise
*/
boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash);
/**
* Generates a random password
*
* #param length desired password length
* #return a random password
*/
String generateRandomPassword(final int length);
}
The implementation:
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.apache.log4j.Logger;
import org.bouncycastle.jcajce.provider.digest.SHA3;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public final class Passwords implements IPasswords, Serializable {
/*serialVersionUID*/
private static final long serialVersionUID = 8036397974428641579L;
private static final Logger LOGGER = Logger.getLogger(Passwords.class);
private static final Random RANDOM = new SecureRandom();
private static final int DEFAULT_SIZE = 64;
private static final char[] symbols;
static {
final StringBuilder tmp = new StringBuilder();
for (char ch = '0'; ch <= '9'; ++ch) {
tmp.append(ch);
}
for (char ch = 'a'; ch <= 'z'; ++ch) {
tmp.append(ch);
}
symbols = tmp.toString().toCharArray();
}
#Override public byte[] getSalt64() {
return getSalt(DEFAULT_SIZE);
}
#Override public byte[] getSalt32() {
return getSalt(32);
}
#Override public byte[] getSalt(int size) {
final byte[] salt;
if (size < 32) {
final String message = String.format("Size < 32, using default of: %d", DEFAULT_SIZE);
LOGGER.warn(message);
salt = new byte[DEFAULT_SIZE];
} else {
salt = new byte[size];
}
RANDOM.nextBytes(salt);
return salt;
}
#Override public byte[] hash(String password, byte[] salt) {
Validate.notNull(password, "Password must not be null");
Validate.notNull(salt, "Salt must not be null");
try {
final byte[] passwordBytes = password.getBytes("UTF-8");
final byte[] all = ArrayUtils.addAll(passwordBytes, salt);
SHA3.DigestSHA3 md = new SHA3.Digest512();
md.update(all);
return md.digest();
} catch (UnsupportedEncodingException e) {
final String message = String
.format("Caught UnsupportedEncodingException e: <%s>", e.getMessage());
LOGGER.error(message);
}
return new byte[0];
}
#Override public boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash) {
Validate.notNull(password, "Password must not be null");
Validate.notNull(salt, "Salt must not be null");
Validate.notNull(hash, "Hash must not be null");
try {
final byte[] passwordBytes = password.getBytes("UTF-8");
final byte[] all = ArrayUtils.addAll(passwordBytes, salt);
SHA3.DigestSHA3 md = new SHA3.Digest512();
md.update(all);
final byte[] digest = md.digest();
return Arrays.equals(digest, hash);
}catch(UnsupportedEncodingException e){
final String message =
String.format("Caught UnsupportedEncodingException e: <%s>", e.getMessage());
LOGGER.error(message);
}
return false;
}
#Override public String generateRandomPassword(final int length) {
if (length < 1) {
throw new IllegalArgumentException("length must be greater than 0");
}
final char[] buf = new char[length];
for (int idx = 0; idx < buf.length; ++idx) {
buf[idx] = symbols[RANDOM.nextInt(symbols.length)];
}
return shuffle(new String(buf));
}
private String shuffle(final String input){
final List<Character> characters = new ArrayList<Character>();
for(char c:input.toCharArray()){
characters.add(c);
}
final StringBuilder output = new StringBuilder(input.length());
while(characters.size()!=0){
int randPicker = (int)(Math.random()*characters.size());
output.append(characters.remove(randPicker));
}
return output.toString();
}
}
The test cases:
public class PasswordsTest {
private static final Logger LOGGER = Logger.getLogger(PasswordsTest.class);
#Before
public void setup(){
BasicConfigurator.configure();
}
#Test
public void testGeSalt() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt(0);
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(64));
}
#Test
public void testGeSalt32() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt32();
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(32));
}
#Test
public void testGeSalt64() throws Exception {
IPasswords passwords = new Passwords();
final byte[] bytes = passwords.getSalt64();
int arrayLength = bytes.length;
assertThat("Expected length is", arrayLength, is(64));
}
#Test
public void testHash() throws Exception {
IPasswords passwords = new Passwords();
final byte[] hash = passwords.hash("holacomoestas", passwords.getSalt64());
assertThat("Array is not null", hash, Matchers.notNullValue());
}
#Test
public void testSHA3() throws UnsupportedEncodingException {
SHA3.DigestSHA3 md = new SHA3.Digest256();
md.update("holasa".getBytes("UTF-8"));
final byte[] digest = md.digest();
assertThat("expected digest is:",digest,Matchers.notNullValue());
}
#Test
public void testIsExpectedPasswordIncorrect() throws Exception {
String password = "givemebeer";
IPasswords passwords = new Passwords();
final byte[] salt64 = passwords.getSalt64();
final byte[] hash = passwords.hash(password, salt64);
//The salt and the hash go to database.
final boolean isPasswordCorrect = passwords.isExpectedPassword("jfjdsjfsd", salt64, hash);
assertThat("Password is not correct", isPasswordCorrect, is(false));
}
#Test
public void testIsExpectedPasswordCorrect() throws Exception {
String password = "givemebeer";
IPasswords passwords = new Passwords();
final byte[] salt64 = passwords.getSalt64();
final byte[] hash = passwords.hash(password, salt64);
//The salt and the hash go to database.
final boolean isPasswordCorrect = passwords.isExpectedPassword("givemebeer", salt64, hash);
assertThat("Password is correct", isPasswordCorrect, is(true));
}
#Test
public void testGenerateRandomPassword() throws Exception {
IPasswords passwords = new Passwords();
final String randomPassword = passwords.generateRandomPassword(10);
LOGGER.info(randomPassword);
assertThat("Random password is not null", randomPassword, Matchers.notNullValue());
}
}
pom.xml (only dependencies):
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.51</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
Here's my solution, i would love anyone's opinion on this, it's simple for beginners
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Scanner;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class Cryptography {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
System.out.print("Password: ");
String strPassword = new Scanner(System.in).nextLine();
byte[] bSalt = Salt();
String strSalt = encoder.encodeToString(bSalt); // Byte to String
System.out.println("Salt: " + strSalt);
System.out.println("String to be hashed: " + strPassword + strSalt);
String strHash = encoder.encodeToString(Hash(strPassword, bSalt)); // Byte to String
System.out.println("Hashed value (Password + Salt value): " + strHash);
}
private static byte[] Salt() {
SecureRandom random = new SecureRandom();
byte salt[] = new byte[6];
random.nextBytes(salt);
return salt;
}
private static byte[] Hash(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = factory.generateSecret(spec).getEncoded();
return hash;
}
}
You can validate by just decoding the strSalt and using the same hash method:
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
Encoder encoder = Base64.getUrlEncoder().withoutPadding();
Decoder decoder = Base64.getUrlDecoder();
System.out.print("Password: ");
String strPassword = new Scanner(System.in).nextLine();
String strSalt = "Your Salt String Here";
byte[] bSalt = decoder.decode(strSalt); // String to Byte
System.out.println("Salt: " + strSalt);
System.out.println("String to be hashed: " + strPassword + strSalt);
String strHash = encoder.encodeToString(Hash(strPassword, bSalt)); // Byte to String
System.out.println("Hashed value (Password + Salt value): " + strHash);
}