I have a String representing a symmetric key, obtained by using Hashicorp Vault (this may not be important actually). I need this key to encrypt big files, so I cannot send the file directly to Vault asking it to encrypt the data. I want to do it locally instead, so I asked Vault to create a symmetric key for me (by using the transit/datakey/plaintext/ endpoint). I have now a symmetric key (and its ciphertext) that is 44 byte long, generated with aes256_gcm96 algorithm. So my 32 byte key is wrapped with a 96 bits (12 bytes) gcm block, as far I've understood.
Now I want to use this key to encrypt my data, but the key is too long to do that, so I need somehow either to unwrap it or call some function that takes in input such a key. I was trying to use Cipher to encrypt my data. This is what I (wrongly) did so far
byte[] datakeyByteArray = mySymmetricKey.getBytes();
SecretKey secretKey = new SecretKeySpec(datakeyByteArray, "AES_256");
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);`
When calling the init function, obviously, an exception is thrown: java.security.InvalidKeyException: The key must be 32 bytes
What kind of operation can I do to obtain a valid key?
Thank you.
You already get a link from #Saptarshi Basu that shows in general how to encrypt data with AES GCM. As you see with my code there is nothing really "mystic" doing this but there are some traps to run in.
Let's start with the most important information - what is the encryption key ? From Hashicorp you received an 44 bytes long string that is the pure 32 bytes long AES GCM key but in Base64-encoding. To get the key usable with Java encryption you need to decode the key to a byte array like this:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
byte[] key = Base64.getDecoder().decode(keyBase64);
The second information we do need is the AES mode - you named it correctly as AES GCM mode and as you provide Java an 32 byte = 256 bit long key it's the requested AES GCM 256 algorithm/mode.
There is a third parameter necessary for AES GCM encryption and it's the nonce (or sometimes named as initialization vector). Hashicorp tells you to use a 96 bit = 12 byte long nonce. For safety reasons it is important that you use a different nonce each time you encrypt so it is good practice to use a (secure) randomly generated nonce:
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
Now we are ready for encryption and putting all data together, do the ".doFinal" step and we receive a byte array with the ciphertext. But stop - we need to concatenate the used nonce and the ciphertext to a larger ciphertextWithNonce this way:
nonce | ciphertext
by simply copying the nonce and ciphertext to a new byte array. This "ciphertextWithNonce" is then Base64 encoded to the final ciphertextBase64 and for upload reasons written to a file.
If you paste your own key in the beginning of the program and run it you will receive a file named "hashicorp_test.enc" that is ready for upload to your fault.
This is a sample output (yours will differ as there is a random element):
Hashicorp Vault AES GCM encryption
ciphertext: /YB+kfVlIhMowLrsnndD737o2CcyWMfr4xnAADnCBSNCSvMG25aR8UzU2ta8wLwdnHfcago/25KFJ2ky95wpFtsCNE63xRs=
ciphertext written to file: hashicorp_test.enc
used key: VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=
If you like to see this code running in an online compiler here is the link:
https://repl.it/#javacrypto/SoHashicorpVaultAesGcmEncryption
This is a "proof of concept" to show in general how to perform an encryption but it lacks some critical points that I'm to lazy to make your work :-).
This example encrypts a string to an encrypted file - you will need to get the original data from a file
Having a large file you may encounter an "out of memory" error as all operations with your data are done in
your heap - for a simple calculation you will need a free memory of 4.5 * original data because you take the original data
into memory, second time you have the encrypted data in memory, third time you're copying the encrypted data to
ciphertextWithNonce and in the end (number 4) you encode all data to a base64-String. For large programs you will need to
switch to a "chunk wise" encryption, done with CiphertextOutputStream
To make the Base64-writing of the complete data a little more convenient I recommend the additional usage of Apache's Base64OutputStream (available via Maven https://mvnrepository.com/artifact/commons-codec/commons-codec).
Security warning: this code has no exception handling and is for educational purpose only.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class Hashicorp_Aes_Gcm_encryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
System.out.println("Hashicorp Vault AES GCM encryption");
// https://stackoverflow.com/questions/64714527/how-can-i-encrypt-data-with-an-already-generated-aes-256-gcm-96-key-coming-from
// paste your key here:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
// filename with ciphertext for upload
String filename = "hashicorp_test.enc";
// my sample plaintext
String plaintext = "The quick brown fox jumps over the lazy dog";
// aes gcm encryption
// decode key
byte[] key = Base64.getDecoder().decode(keyBase64);
// generate random nonce
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
// calculate specs
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, nonceRandom);
// initialize cipher
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");//NOPadding
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// encrypt
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// concentenate iv + ciphertext
int ciphertextWithNonceLength = nonceRandom.length + ciphertext.length;
byte[] ciphertextWithNonce = new byte[ciphertextWithNonceLength];
System.arraycopy(nonceRandom, 0, ciphertextWithNonce, 0, nonceRandom.length);
System.arraycopy(ciphertext, 0, ciphertextWithNonce, nonceRandom.length, ciphertext.length);
String ciphertextBase64 = Base64.getEncoder().encodeToString(ciphertextWithNonce);
System.out.println("ciphertext: " + ciphertextBase64);
// save encrypted data to a file
Files.write(Paths.get(filename), ciphertextBase64.getBytes(StandardCharsets.UTF_8));
System.out.println("ciphertext written to file: " + filename);
System.out.println("used key: " + keyBase64);
}
}
I'm trying to implement a more advanced password hashing algorithm (PBKDF2) that uses the Base64 class that is found in the java util library, but since this class is outdated I need to get the Apache Codecs library that supports the updated Base64 class. The amazing thing is that on a normal java class this works flawlessly, but when I use the same piece of code in an android activity it gives me an error saying that the method that I'm trying to call from Base64 does not exist!
I think the problem here is that in the activity, the Base64 is called from the util library that has the outdated version of Base64.
Here is an example of the code.
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;
public class Password {
// The higher the number of iterations the more
// expensive computing the hash is for us and
// also for an attacker.
private final int iterations = 20 * 1000;
private final int saltLen = 32;
private final int desiredKeyLen = 256;
/**
* Computes a salted PBKDF2 hash of given plaintext password
* suitable for storing in a database.
* Empty passwords are not supported.
*/
public String getSaltedHash(String password) throws Exception {
byte[] salt = SecureRandom.getInstance("SHA1PRNG").generateSeed(saltLen);
// store the salt with the password
return Base64.encodeBase64String(salt) + "$" + hash(password, salt);
}
/**
* Checks whether given plaintext password corresponds
* to a stored salted hash of the password.
*/
public boolean check(String password, String stored) throws Exception {
String[] saltAndPass = stored.split("\\$");
if (saltAndPass.length != 2) {
throw new IllegalStateException(
"The stored password have the form 'salt$hash'");
}
String hashOfInput = hash(password, Base64.decodeBase64(saltAndPass[0]));
return hashOfInput.equals(saltAndPass[1]);
}
// using PBKDF2 from Sun, an alternative is https://github.com/wg/scrypt
// cf. http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html
private String hash(String password, byte[] salt) throws Exception {
if (password == null || password.length() == 0)
throw new IllegalArgumentException("Empty passwords are not supported.");
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey key = f.generateSecret(new PBEKeySpec(
password.toCharArray(), salt, iterations, desiredKeyLen)
);
return Base64.encodeBase64String(key.getEncoded());
}
public static void main(String[] args) throws Exception {
Password passwordHash = new Password();
String password = passwordHash.getSaltedHash("password");
String password2 = passwordHash.getSaltedHash("password");
System.out.println("P1-HASH: " + password);
System.out.println("P2-HASH: " + password2);
System.out.println(passwordHash.check("password", password2));
}
}
The method seems to be called encodeToString. I really don't know where you found your particular method, but it isn't documented; I think you were just referring to the wrong class.
Let say i have encripted SHA1 password like this
String pass = "f6ce584e7b4ff5253eed4a2ea2b44247";
and i want make condition like this :
if (pass.equals("userinput")){
System.out.println("success");
}
please someone help me to make proper condition / function to compare those both value between user input and encripted password. Your help will be highly appreciated. thanks
SHA1 is a hash algorithm, which means that it is one-way. You can't get the original message after hashing it. Unlike encryption which is two-way (allows encryption and decryption).
This means that if you want to compare a hash, you don't try to get the original message. Instead, you hash the message-to-be-compared as well, then you perform the match:
So if the hashed pw is stored as:
String pass = "f6ce584e7b4ff5253eed4a2ea2b44247";
To match the subsequent input of the password, you do:
//check if hashed userInput is also "f6ce584e7b4ff5253eed4a2ea2b44247"
if(pass.equals(sha1(userInput))){
//do whatever
}
To implement a sha1() hash function, refer to: Java String to SHA1
To get your hashcode:
public static byte[] sha1(byte[] data)
Calculates the SHA-1 digest and returns the value as a byte[].
Parameters:
data - Data to digest
Returns:
SHA-1 digest
Found these at
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html#sha1Hex(java.lang.String)
This helps your process.
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi.SHA1;
public class SHA1_test {
public static String sha1(String s, String keyString)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException {
SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"),
"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(key);
byte[] bytes = mac.doFinal(s.getBytes("UTF-8"));
return new String(Base64.encodeBase64(bytes));
}
public static void main(String[] args) throws InvalidKeyException,
UnsupportedEncodingException, NoSuchAlgorithmException {
Boolean validate = false;
String code = sha1("admin", "123456");
String your_user_inputString = "testpassword";
if (code.equals(sha1(your_user_inputString, "123456"))) {
System.out.println("Correct");
} else {
System.out.println("Bad password");
}
}
}
This works!!!
I am developing a GUI based encryptor/decryptor based on AES-128 bit symmetric encryption.
My problem is that how to determine that decryption has not occurred and show a dialog box "Decryption Failed". The code I have written would always generate a file without a .enc extension regardless of the fact that it is still encrypted !
Hoping to get a answer as always from Stack Overflow's top notch programmers :)
Do note that the decryption process doesn't fail or throws exception ! It's just the fact that it generates a file that's still not decrypted. That we have to stop and that's what I meant !
Code here: (Sorry for bad indentation !)
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.security.MessageDigest;
public class FileEncryptor{
private String algo;
private String path;
private String password;
public FileEncryptor(String algo,String path, String password) {
this.algo = algo; //setting algo
this.path = path;//setting file path
this.password = password;
}
public void encrypt() throws Exception{
SecureRandom padding = new SecureRandom();
byte[] salt = new byte[16];
padding.nextBytes(salt);
//generating key
byte k[] = password.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
k = sha.digest(k);
k = Arrays.copyOf(k, 16);
SecretKeySpec key = new SecretKeySpec(k,algo);
//creating and initialising cipher and cipher streams
Cipher encrypt = Cipher.getInstance(algo);
encrypt.init(Cipher.ENCRYPT_MODE, key);
//opening streams
FileOutputStream fos =new FileOutputStream(path+".enc");
try(FileInputStream fis =new FileInputStream(path)){
try(CipherOutputStream cout=new CipherOutputStream(fos, encrypt)){
copy(fis,cout);
}
}
}
public void decrypt() throws Exception{
SecureRandom padding = new SecureRandom();
byte[] salt = new byte[16];
padding.nextBytes(salt);
//generating same key
byte k[] = password.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-1");
k = sha.digest(k);
k = Arrays.copyOf(k, 16);
SecretKeySpec key = new SecretKeySpec(k,algo);
//creating and initialising cipher and cipher streams
Cipher decrypt = Cipher.getInstance(algo);
decrypt.init(Cipher.DECRYPT_MODE, key);
//opening streams
FileInputStream fis = new FileInputStream(path);
try(CipherInputStream cin=new CipherInputStream(fis, decrypt)){
try(FileOutputStream fos =new FileOutputStream(path.substring(0,path.lastIndexOf(".")))){
copy(cin,fos);
}
}
}
private void copy(InputStream is,OutputStream os) throws Exception{
byte buf[] = new byte[4096]; //4K buffer set
int read = 0;
while((read = is.read(buf)) != -1) //reading
os.write(buf,0,read); //writing
}
public static void main (String[] args)throws Exception {
System.out.println("Enter Password: ");
new FileEncryptor("AES","sample.txt",new java.util.Scanner(System.in).nextLine()).encrypt();
new FileEncryptor("AES","sample.txt.enc",new java.util.Scanner(System.in).nextLine()).decrypt();
}
}
Without looking at the API calls, the decrypt methods should throw an exception if an error occurs. In your exception handler, you can set a flag that will allow you to display an error message. You can also delay the decrypted file creation till after successful decryption (or at least till after the first block has been successfully decrypted). If decryption then fails further along the line, you can delete the (essentially temporary) decrypted output file and display the error message.
[edit]
I slightly misunderstood the original post, so some suggestions to check for failed decryption (note that these are higher level than AES, so it might be specific to your application only):
Add a checksum to the plaintext data before encryption
Append other metadata (file size, user, date, etc) to the plaintext, and check for these when decrypting
Usually, a padding exception would occur on decryption - check for these (and any other giveaways)
Use PKI (public key infrastructure) functionality such as signatures (this is outside the scope of this answer, and possibly outside the scope of the problem you're trying to solve)
I suggest appending a constant, rather than a checksum, to your data before encryption, and verifying it after encryption.
And the encryption algorithm should use chaining, that means avoid ECB (see here why: http://bobnalice.wordpress.com/2009/01/28/friends-don%E2%80%99t-let-friends-use-ecb-mode-encryption).
Using a constant with chaining, is nearly as good as a checksum and much simpler.
I need to hash passwords for storage in a database. How can I do this in Java?
I was hoping to take the plain text password, add a random salt, then store the salt and the hashed password in the database.
Then when a user wanted to log in, I could take their submitted password, add the random salt from their account information, hash it and see if it equates to the stored hash password with their account information.
You can actually use a facility built in to the Java runtime to do this. The SunJCE in Java 6 supports PBKDF2, which is a good algorithm to use for password hashing.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
System.out.printf("salt: %s%n", enc.encodeToString(salt));
System.out.printf("hash: %s%n", enc.encodeToString(hash));
Here's a utility class that you can use for PBKDF2 password authentication:
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* Hash passwords for storage, and test passwords against password tokens.
*
* Instances of this class can be used concurrently by multiple threads.
*
* #author erickson
* #see StackOverflow
*/
public final class PasswordAuthentication
{
/**
* Each token produced by this class uses this identifier as a prefix.
*/
public static final String ID = "$31$";
/**
* The minimum recommended cost, used by default
*/
public static final int DEFAULT_COST = 16;
private static final String ALGORITHM = "PBKDF2WithHmacSHA1";
private static final int SIZE = 128;
private static final Pattern layout = Pattern.compile("\\$31\\$(\\d\\d?)\\$(.{43})");
private final SecureRandom random;
private final int cost;
public PasswordAuthentication()
{
this(DEFAULT_COST);
}
/**
* Create a password manager with a specified cost
*
* #param cost the exponential computational cost of hashing a password, 0 to 30
*/
public PasswordAuthentication(int cost)
{
iterations(cost); /* Validate cost */
this.cost = cost;
this.random = new SecureRandom();
}
private static int iterations(int cost)
{
if ((cost < 0) || (cost > 30))
throw new IllegalArgumentException("cost: " + cost);
return 1 << cost;
}
/**
* Hash a password for storage.
*
* #return a secure authentication token to be stored for later authentication
*/
public String hash(char[] password)
{
byte[] salt = new byte[SIZE / 8];
random.nextBytes(salt);
byte[] dk = pbkdf2(password, salt, 1 << cost);
byte[] hash = new byte[salt.length + dk.length];
System.arraycopy(salt, 0, hash, 0, salt.length);
System.arraycopy(dk, 0, hash, salt.length, dk.length);
Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
return ID + cost + '$' + enc.encodeToString(hash);
}
/**
* Authenticate with a password and a stored password token.
*
* #return true if the password and token match
*/
public boolean authenticate(char[] password, String token)
{
Matcher m = layout.matcher(token);
if (!m.matches())
throw new IllegalArgumentException("Invalid token format");
int iterations = iterations(Integer.parseInt(m.group(1)));
byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
byte[] check = pbkdf2(password, salt, iterations);
int zero = 0;
for (int idx = 0; idx < check.length; ++idx)
zero |= hash[salt.length + idx] ^ check[idx];
return zero == 0;
}
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations)
{
KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
try {
SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
return f.generateSecret(spec).getEncoded();
}
catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
}
catch (InvalidKeySpecException ex) {
throw new IllegalStateException("Invalid SecretKeyFactory", ex);
}
}
/**
* Hash a password in an immutable {#code String}.
*
* <p>Passwords should be stored in a {#code char[]} so that it can be filled
* with zeros after use instead of lingering on the heap and elsewhere.
*
* #deprecated Use {#link #hash(char[])} instead
*/
#Deprecated
public String hash(String password)
{
return hash(password.toCharArray());
}
/**
* Authenticate with a password in an immutable {#code String} and a stored
* password token.
*
* #deprecated Use {#link #authenticate(char[],String)} instead.
* #see #hash(String)
*/
#Deprecated
public boolean authenticate(String password, String token)
{
return authenticate(password.toCharArray(), token);
}
}
BCrypt is a very good library, and there is a Java port of it.
You could use Spring Security Crypto (has only 2 optional compile dependencies), which supports PBKDF2, BCrypt, SCrypt and Argon2 password encryption.
Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
String aCryptedPassword = argon2PasswordEncoder.encode("password");
boolean passwordIsValid = argon2PasswordEncoder.matches("password", aCryptedPassword);
SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
String sCryptedPassword = sCryptPasswordEncoder.encode("password");
boolean passwordIsValid = sCryptPasswordEncoder.matches("password", sCryptedPassword);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String bCryptedPassword = bCryptPasswordEncoder.encode("password");
boolean passwordIsValid = bCryptPasswordEncoder.matches("password", bCryptedPassword);
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
String pbkdf2CryptedPassword = pbkdf2PasswordEncoder.encode("password");
boolean passwordIsValid = pbkdf2PasswordEncoder.matches("password", pbkdf2CryptedPassword);
You can comput hashes using MessageDigest, but this is wrong in terms of security. Hashes are not to be used for storing passwords, as they are easily breakable.
You should use another algorithm like bcrypt, PBKDF2 and scrypt to store you passwords. See here.
You can use the Shiro library's (formerly JSecurity) implementation of what is described by OWASP.
It also looks like the JASYPT library has a similar utility.
In addition to bcrypt and PBKDF2 mentioned in other answers, I would recommend looking at scrypt
MD5 and SHA-1 are not recommended as they are relatively fast thus using "rent per hour" distributed computing (e.g. EC2) or a modern high end GPU one can "crack" passwords using brute force / dictionary attacks in relatively low costs and reasonable time.
If you must use them, then at least iterate the algorithm a predefined significant amount of times (1000+).
See here for more: https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
And here: http://codahale.com/how-to-safely-store-a-password/ (criticizes SHA family, MD5 etc for password hashing purposes)
And here: http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html (criticizes bcrypt and recommends scrypt and PBKDF2)
Fully agree with Erickson that PBKDF2 is the answer.
If you don't have that option, or only need to use a hash, Apache Commons DigestUtils is much easier than getting JCE code right:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html
If you use a hash, go with sha256 or sha512. This page has good recommendations on password handling and hashing (note it doesn't recommend hashing for password handling):
http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
While the NIST recommendation PBKDF2 has already been mentioned, I'd like to point out that there was a public password hashing competition that ran from 2013 to 2015. In the end, Argon2 was chosen as the recommended password hashing function.
There is a fairly well adopted Java binding for the original (native C) library that you can use.
In the average use-case, I don't think it does matter from a security perspective if you choose PBKDF2 over Argon2 or vice-versa. If you have strong security requirements, I recommend considering Argon2 in your evaluation.
For further information on the security of password hashing functions see security.se.
As of 2020, the most reliable password hashing algorithm in use, most likely to optimise its strength given any hardware, is Argon2id or Argon2i but not its Spring implementation.
The PBKDF2 standard includes the the CPU-greedy/computationally-expensive feature of the block cipher BCRYPT algo, and add its stream cipher capability. PBKDF2 was overwhelmed by the memory exponentially-greedy SCRYPT then by the side-channel-attack-resistant Argon2
Argon2 provides the necessary calibration tool to find optimized strength parameters given a target hashing time and the hardware used.
Argon2i is specialized in memory greedy hashing
Argon2d is specialized in CPU greedy hashing
Argon2id use both methods.
Memory greedy hashing would help against GPU use for cracking.
Spring security/Bouncy Castle implementation is not optimized and relatively week given what attacker could use.
cf: Spring doc Argon2 and Scrypt
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.
The most credible implementation in use for java is mkammerer's one,
a wrapper jar/library of the official native implementation written in C.
It is well written and simple to use.
The embedded version provides native builds for Linux, windows and OSX.
As an example, it is used by jpmorganchase in its tessera security project used to secure Quorum, its Ethereum cryptocurency implementation.
Here is an example:
final char[] password = "a4e9y2tr0ngAnd7on6P১M°RD".toCharArray();
byte[] salt = new byte[128];
new SecureRandom().nextBytes(salt);
final Argon2Advanced argon2 = Argon2Factory.createAdvanced(Argon2Factory.Argon2Types.ARGON2id);
byte[] hash = argon2.rawHash(10, 1048576, 4, password, salt);
(see tessera)
Declare the lib in your POM:
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm</artifactId>
<version>2.7</version>
</dependency>
or with gradle:
compile 'de.mkammerer:argon2-jvm:2.7'
Calibration may be performed using de.mkammerer.argon2.Argon2Helper#findIterations
SCRYPT and Pbkdf2 algorithm might also be calibrated by writing some simple benchmark, but current minimal safe iterations values, will require higher hashing times.
Here you have two links for MD5 hashing and other hash methods:
Javadoc API: https://docs.oracle.com/javase/1.5.0/docs/api/java/security/MessageDigest.html
Tutorial: http://www.twmacinta.com/myjava/fast_md5.php
Among all the standard hash schemes, LDAP ssha is the most secure one to use,
http://www.openldap.org/faq/data/cache/347.html
I would just follow the algorithms specified there and use MessageDigest to do the hash.
You need to store the salt in your database as you suggested.
i leaned that from a video on udemy and edited to be stronger random password
}
private String pass() {
String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq##$%^&*" ;
char icon1;
char[] t=new char[20];
int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters
icon1=passswet.charAt(rand1);//will produce char with a special character
int i=0;
while( i <11) {
int rand=(int)(Math.random()*passswet.length());
//notice (int) as the original value of Math>random() is double
t[i] =passswet.charAt(rand);
i++;
t[10]=icon1;
//to replace the specified item with icon1
}
return new String(t);
}
}
Here is my simple PasswordHasher class that I made:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
public class PasswordHasher {
private static final String ALGO = "PBKDF2WithHmacSHA1";
private static final byte[] SALT = {
8, 8, 8, 8, 2,
8, 7, 7, 7, 2,
1, 1, 1, 1, 2,
11
};
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 128;
private SecretKeyFactory mFactory;
byte[] hashPassword(String password) {
SecretKeyFactory factory = getFactory();
if (factory != null) {
try {
KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
return factory.generateSecret(spec).getEncoded();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
return null;
}
boolean verifyPassword(String password, byte[] expectedHashResult) {
byte[] hashedPassword = hashPassword(password);
if (hashedPassword == null) {
// Log fail result
return false;
}
return Arrays.equals(hashedPassword, expectedHashResult);
}
private SecretKeyFactory getFactory() {
if (mFactory == null) {
try {
mFactory = SecretKeyFactory.getInstance(ALGO);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
return mFactory;
}
}
import java.security.MessageDigest;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Base64;
import java.util.Properties;
public class Main{
public static void main(String[]a]{
//enter code here
}
public static String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] hash = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
hashPassword is the method that returns a hashed value when we pass a String as its parameter.
MessageDigest is the class that provides the interface for hashing password
getInstance is used to get the instance of a hashing algorithm such as MD-5,SHA 216,SHA-512 and so on.
The hashed Sting is in the forn of -byte []
In the return statement we convert the byte using ToString.