JDBC: How to store and retrieve hashed passwords - java

I'm trying to make a simple but secure login system. I've read that hashing and salting passwords gives sufficient security if you're using a good algorithm for hashing and creating a unique salt for each hash. I found this code-snippet on the OWASP website for the hashing method:
public static byte[] hashPassword(final char[] password, final byte[] salt, final int iterations, final int keyLength) {
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
SecretKey key = skf.generateSecret(spec);
byte[] res = key.getEncoded();
return res;
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
and I'm using SecureRandom to generate the salt
public static byte[] generateSalt(int length) {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[length];
random.nextBytes(salt);
return salt;
}
This is where my question comes
I would like to store the hashed password and salt to a database using JDBC.
I'm not sure which datatype to use in the databse (varchar? blob? something else?)
I've tried storing the byte array as a varchar and reading it as a String, but when i output the result i get all question marks, so i guess that's not the way to do it.
A blob looks right, considering its storing bytes. But all the examples i find seem to use it for storing images so i was thinking there might be another approach for byte arrays? What's the way to do it?

Related

AES256 can not decrypt previous encrypted data after restarting server

Currently, I'm having a problem that I don't know how to solve. It's the decryption and encryption of the string using AES256. Everything was working fine until I restarted the server and I couldn't decode the previous data.
I tried saving the salt and IVParameter to decrypt for next time, but it doesn't work.
private static final String SECRET_KEY = "my_key";
private static final byte[] SALT;
private static final SecureRandom random;
private static final IvParameterSpec ivspec;
static {
random = new SecureRandom();
SALT = new byte[16];
random.nextBytes(SALT);
byte[] bytesIV = new byte[16];
random.nextBytes(bytesIV);
ivspec = new IvParameterSpec(bytesIV);
}
public static String encrypt(String stringToEncrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e);
}
return null;
}
public static String decrypt(String stringToDecrypt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e);
}
return null;
}
SecureRandom initialises itself differently everytime you instantiate it. I.e., it will also create a different sequence of random values each time. Even if you initialise SALT with a fixed initial value, in the next step you overwrite it again by calling random.nextBytes(SALT). Either don't do that or instantiate SecureRandom with a seed, so it creates the same sequence of random numbers every time. But this is kind of counter-productive. Similarly, you also randomise IvParameterSpec.
You only need the nextBytes() result, if you want to generate new salt or IV values for a multiple users or a sequence of distinct encryption/decryption actions. AES being a symmetric cypher, you need to make sure that when decrypting a message, you use the same salt and IV (if any) which were used for encryption. Try this in order to get identical encryption results:
static {
random = new SecureRandom(); // not used in this example
SALT = "I am so salty!".getBytes(StandardCharsets.UTF_8);
byte[] bytesIV = "my super fancy IV".getBytes(StandardCharsets.UTF_8);
ivspec = new IvParameterSpec(Arrays.copyOfRange(bytesIV, 0, 16));
}
Of course, in the example above I am assuming that actually salt and IV were initially created randomly, then securely saved or transmitted to the recipient, and then loaded/received and used to decrypt the message. In a real-world scenario, you would transmit or store salt and IV asymmetrically encrypted (using public-key cryptography), while the message itself (which usually is much bigger than secret key, salt and IV) is encrypted using the much faster and more efficient symmetric AES256 algorithm.
P.S.: The Arrays.copyOfRange(bytesIV, 0, 16) is necessary, because in contrast to the salt the IV must be exactly 16 bytes long. The salt is more flexible.
Update: Actually, it is not necessary to encrypt salt and IV. They just make sure that the same input and secret key do not yield the same encrypted message in order to make attacks based on known cleartext more difficult. This is also why e.g. when storing salted hashes in a database, you store the salt values as cleartext along with the salted password hash (not the password itself!), because you need them every time you want to validate a user password.

Java SecureRandom as salt

Is this the correct way of generating salts for passwords?
SecureRandom random = new SecureRandom();
byte[] salt = random.generateSeed(64);
String decoded = new String(salt, "Cp1252");
System.out.println(decoded);
I am trying to generate new passwords (SHA-512), so I will also need a salt.
The hashed password will be = user password + salt ... is this correct?
Wouldnt these strange characters "break" the DB (MySQL)? (Update: no, because salt should be encrypted/encoded/hashed and the result shoudnt use strange characters)
Few outputs:
ã2}wÑ»-ÄKÇæꮃzR4qÉÖÙÚ!ž0ÉW9;*Vß4x»)
àöˆ˜£¿{,J¼…HþTù#+Bv(Fp´G~Aò`^e_ElpíÜžS A!­ñÛz‹y#`ý‡)‡ª€
5a£Æ.¥sgöfÈB:4-�y$Óx%Óâyý¾N¨…áq
Should these salts be also encripted as SHA-512? (Update: encoded using base64 library from apache commons encode, see below)
Update:
SecureRandom random = new SecureRandom();
byte[] saltArr = new byte[64];
random.nextBytes(saltArr);
String salt = new String(saltArr, "Cp1252");
System.out.println("SALT:"+salt);
byte[] encodedBytes = Base64.encodeBase64(saltArr);
System.out.println("Encoded SALT:" + new String(encodedBytes));
byte[] decodedBytes = Base64.decodeBase64(encodedBytes);
System.out.println("Decoded SALT:" + new String(decodedBytes, "Cp1252"));
//SHA
String target = "Test";
MessageDigest sh = MessageDigest.getInstance("SHA-512");
sh.update(target.getBytes());
StringBuffer sb = new StringBuffer();
for (byte b : sh.digest()) sb.append(Integer.toHexString(0xff & b));
System.out.println("Hashed PWD:"+sb);
//And then joining them together...
First, it would be enough to use nextBytes (that actually calls the PRNG) instead of generateSeed. The latter uses the seed generation algorithm, which is a more complex operation since it needs an entropy source (800-90A p19-23)
The primary function of salts is to defend against dictionary attacks
versus a list of password hashes and against pre-computed rainbow
table attacks. (Wikipedia)
In other words, you need a different salt for each password, but that salt is not a secret (you will be storing it along with the digest output), and you
should be fine with the output from the PRNG.
Also, since the salt (which may contain bytes in the full range 0x00-0xFF) will be persisted as a string, it should be encoded* in Base64 (or another charset-safe encoding, such as base 85, but base64 is much more usual).
*SHA is not an encryption function.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[64];
random.nextBytes(salt);
String password="god";
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(password.getBytes(Charset.forName("UTF-8")));
md.update(salt);
byte[] digest = md.digest();

Password storage with SHA-256 hash and AES using Bouncy Castle in Java

I am new to password storage and to Bouncy Castle.
Encryption : Is there a reason to prefer Pkcs5S2ParametersGenerator
over AES to encrypt(salt+hash(password+salt)) ?
An example : How to encrypt and salt the password using BouncyCastle API in Java?
My Java code : is there a better way to get back the salt from the cipher than byte array extraction ?
Here is my java code :
// salt
java.security.SecureRandom rgen = new SecureRandom();
byte[] salt = rgen.generateSeed(20);
// add Bouncy Castle
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// aes secret key
javax.crypto.KeyGenerator kgen = KeyGenerator.getInstance("AES", "BC");
Key cleSecrete = kgen.generateKey();
// aes
javax.crypto.Cipher cipher = Cipher.getInstance("AES", "BC");
// sha-256
java.security.MessageDigest sha256 = MessageDigest.getInstance("SHA-256","BC");
// hash the clear password with the salt to avoid collisions
byte[] motDePasseHash = hasherSaler(motDePasseClair.getBytes("UTF-8"),salt);
// Encrypt the hash with the salt to get the salt back
byte[] chiffreBDD = chiffrerSalerHash(salt,motDePasseHash,cleSecrete );
// Store the cipher in DB
...
// Get back the hash and the salt from DB
byte[] deChiffreBDD = deChiffrer(chiffreBDD,cleSecrete );
byte[] saltBDD = extraireOctets(deChiffreBDD,0,19);
byte[] hashBDD = extraireOctets(deChiffreBDD,20,deChiffreBDD.length-1);
// hash the user intput
byte[] motDePasseHashCandidat = hasherSaler(motDePasseClairCandidat.getBytes("UTF-8"),saltBDD);
// Compare hased user input with DB hash
boolean isMotDePasseOK = Arrays.equals(hashBDD,motDePasseHashCandidat);
private final byte[] hasherSaler(byte[] clair,byte[] salt) {
byte[] concat = concatenerOctets(clair,salt);
return sha256.digest(concat);
}
private final byte[] chiffrerSalerHash(byte[] salt,byte[] hash, Key cle) {
cipher.init(true,cle);
return cipher.doFinal(concatenerOctets(salt,hash));
}
private final byte[] deChiffrer(byte[] chiffre, Key cle) {
cipher.init(false,cle);
return cipher.doFinal(chiffre);
}
Thank you.
If you just want to verify a password, you should only use PBKDF2 (or bcrypt/scrypt). A cipher should not be necessary. The salt can be stored without encryption. You may want to use an additional secret to append to the salt that you keep stored in e.g. the source code. Don't forget to store a protocol number with your passwords or you cannot upgrade later on.
As for your code, you should not use generateSeed for salts. You should use update more. "AES" uses ECB mode by default, so specify another mode. Don't rely on defaults. Use a (possibly derived) IV. Don't explicitly use a provider when not necessary.
OK, I could go on for quite a while, but this will have to do for now.

How to encrypt and salt the password using BouncyCastle API in Java?

I am fairly new to cryptography and I am using BouncyCasetle API to encrypt password and store it in the database. For encryption I am using SHA-1 algorithm and I want to salt the password to prevent it agains dictionary attacks.
Any help would be appreciated.
I'd recommend use of a Password-Based Key Derivation Function instead of a basic hash function for this. Something like this:
// tuning parameters
// these sizes are relatively arbitrary
int seedBytes = 20;
int hashBytes = 20;
// increase iterations as high as your performance can tolerate
// since this increases computational cost of password guessing
// which should help security
int iterations = 1000;
// to save a new password:
SecureRandom rng = new SecureRandom();
byte[] salt = rng.generateSeed(seedBytes);
Pkcs5S2ParametersGenerator kdf = new Pkcs5S2ParametersGenerator();
kdf.init(passwordToSave.getBytes("UTF-8"), salt, iterations);
byte[] hash =
((KeyParameter) kdf.generateDerivedMacParameters(8*hashBytes)).getKey();
// now save salt and hash
// to check a password, given the known previous salt and hash:
kdf = new Pkcs5S2ParametersGenerator();
kdf.init(passwordToCheck.getBytes("UTF-8"), salt, iterations);
byte[] hashToCheck =
((KeyParameter) kdf.generateDerivedMacParameters(8*hashBytes)).getKey();
// if the bytes of hashToCheck don't match the bytes of hash
// that means the password is invalid
Well what you could do is get a:
StringBuilder salt=new StringBuilder();
salt.append("MySuperSecretSalt");
MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "This is text to hash";
salt.append(text);
md.update(salt.toString().getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] digest = md.digest();
Your, digest now contains the hash of your string+salt so it help with protecting against rainbow tables.

How to get IV for decryption in Java?

I need to encrypt / decrypt a username field and I was planning to use the code below:
public class Decrypter {
Cipher dcipher;
byte[] salt = new String("12345678").getBytes();
int iterationCount = 1024;
int keyStrength = 256;
SecretKey key;
byte[] iv;
Decrypter(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount, keyStrength);
SecretKey tmp = factory.generateSecret(spec);
key = new SecretKeySpec(tmp.getEncoded(), "AES");
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
public String encrypt(String data) throws Exception {
dcipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters params = dcipher.getParameters();
iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] utf8EncryptedData = dcipher.doFinal(data.getBytes());
String base64EncryptedData = new sun.misc.BASE64Encoder().encodeBuffer(utf8EncryptedData);
System.out.println("IV " + new sun.misc.BASE64Encoder().encodeBuffer(iv));
System.out.println("Encrypted Data " + base64EncryptedData);
return base64EncryptedData;
}
public String decrypt(String base64EncryptedData) throws Exception {
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = new sun.misc.BASE64Decoder().decodeBuffer(base64EncryptedData);
byte[] utf8 = dcipher.doFinal(decryptedData);
return new String(utf8, "UTF8");
}
public static void main(String args[]) throws Exception {
Decrypter decrypter = new Decrypter("ABCDEFGHIJKL");
String encrypted = decrypter.encrypt("StringToBeEncrypted");
String decrypted = decrypter.decrypt(encrypted);
System.out.println(decrypted);
}
}
I've taken this code from another site. The above code works fine when run as standalone. But the issue that I'm facing is how to decrypt the value when username is already encrypted?
I'll be calling encrypt & decrypt functions from different classes, so if the string is already encrypted & stored in the DB, then when user logs into website, when I'll call decrypt method, how do I pass the IV as CBC mode decrypt requires an IV parameter, while I've not stored iv during the encryption???
Any help is much appreciated!!
NOTE: This has nothing to do with password protection. As mentioned, need to encrypt userid & not password! For password protection, I'm using hash only.
The IV is something you need to supply when encrypting or decrypting data.
Like salt for a hash, the IV ensures that the identical plaintexts will never result in indentical ciphertexts.
You need to generate a (securely) random IV when you encrypt each plaintext and store it alongside the ciphertext.
To decrypt you have to have the IV and the secret key.
Though it is less secure, what I've seen is that people always keep the key (or password) safe somewhere and sometimes just code the IV into the program.
byte[] iv = new byte[]
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
Or with some other set of byte values.
[Note that your code is generating an IV for each call to encrypt()]
Edit
Commenter #SLaks points out that using a constant IV reduces the level of protection and removes the extra level of security added by using CBC (which uses the IV). It reduces to the level of ECB (which has no IV).
(Note: in the code above:
Cipher.getInstance("AES/CBC/PKCS5Padding");
where CBC is selected.)
This is significant in that a particular string of bytes will encrypt to the same result every time when the key, IV and salt used are the same. IV is there to make this stop happening.
We want the encrypted results to be as random looking as possible. That keeps the bad guys from figuring out the original content.
Think of the IV as adding randomness to the plain-text message. For example, you might be encrypting passwords people give you. Those people tend to choose poor passwords and multiple people tend to choose the same one. Adding randomness would be a good thing in this case.
Think of a salt as adding randomness to the passphrase (which is just a fancy word for password to highlight using a long and varied one). In this case, again, people choose poor ones and adding randomness to them makes the encrypted results more random.
That's why you would choose a random bunch of bits to serve as the IV for each message encrypted. To keep it from looking like other encrypted messages. But they have to be stored with each message so it can be decrypted.
Any choosing a random bunch of bits to serve as the salt for each person will serve to make their messages encrypt and look different from anyone elses messages. You could use a different salt each time the person logs in or each time they change their password or even just once per person. However you do it, you have to save the salt values so you can decrypt the messages later.
If you need this level of security, be sure to generate a truly random IV for each message encrypted and store it somewhere to be used when decrypting.

Categories

Resources