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.
Related
I want to add some security to my project so I added a password field. In, order to store the password I was going to use a txt and save it in there, to add a bit more security I used the below code to hash the password(theirs more than one password saved this way if that's important). This is just and example of how I have done the hashing, the actual program uses text files etc.
public static void main(String[] args) throws NoSuchAlgorithmException {
System.out.println("Enter Password: ");
Scanner scanner = new Scanner(System.in);
String enteredPassword = scanner.nextLine();
String storedPassword = "�D�Ϛ-�UK�c�=�,�}��}��D��Zj>�m";
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(enteredPassword.getBytes());
String hashedString = new String(messageDigest.digest());
System.out.println(hashedString);
if(storedPassword.equals(hashedString)){
System.out.println("Passwords Match!");
}else{
System.out.println("Passwords Do Not Match!");
}
}
My question is am I doing this securely, besides decompiling my project and bypassing this feature is my project secure or can this method be exploited? Also, is there a way to secure a project against being decompiled and the code re-written to bypass security features?? Thank You
The approach itself is good; SHA-256 by itself is a strong, one-way hashing function. It cannot be "decrypted". But it's fast, thus allowing rapid brute-forcing of the password using a dictionary.
For better security you can slow things down with e.g. bcrypt or PBKDF2. Some 100ms will not be noticeable by the user, but makes brute-forcing impractical.
Here's an example with PBKDF2 using 100000 iterations of SHA-256. It also uses a random salt.
SecureRandom random = SecureRandom.getInstanceStrong();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("my-secret-password".toCharArray(), salt, 100000, 256);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
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));
Note: PBKDF2WithHmacSHA256 is available since Java 8.
Here's a more complete example:
private static final SecureRandom random = new SecureRandom();
/**
* One-way encrypts (hashes) the given password.
*
* #param saltpw the salt (will be generated when null)
* #param pw the password to encrypt
* #return encrypted salted password
*/
public static String encrypt(String saltpw, String pw) throws GeneralSecurityException {
byte[] salt;
if (saltpw == null) {
salt = new byte[16];
random.nextBytes(salt);
} else {
salt = Base64.getDecoder().decode(saltpw.replaceFirst("\\$.*", ""));
}
KeySpec spec = new PBEKeySpec(pw.toCharArray(), salt, 100000, 256);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
return enc.encodeToString(salt) + "$" + enc.encodeToString(hash);
}
public static void main(String[] args) throws Exception {
String enc = encrypt(null, "my-secret-password");
System.out.printf("enc : %s\n", enc);
String test1 = encrypt(enc, "my-secret-password");
System.out.printf("test 1: %s, valid: %b\n", test1, enc.equals(test1));
String test2 = encrypt(enc, "some-other-password");
System.out.printf("test 2: %s, valid: %b\n", test2, enc.equals(test2));
}
Prints:
enc : B5V6SjkjJpeOxvMAkPf7EA==$NNDA7o+Dpd+M+H99WVxY0B8adqVWJHZ+HIjgPxMljwo=
test 1: B5V6SjkjJpeOxvMAkPf7EA==$NNDA7o+Dpd+M+H99WVxY0B8adqVWJHZ+HIjgPxMljwo=, valid: true
test 2: B5V6SjkjJpeOxvMAkPf7EA==$4H1SpH8N+/jqU40G6RWb+ReHUB3C58iAaU4l39j+TV8=, valid: false
Notice how test 1 results in exactly the same encrypted string as the original password, and that test 2 (with a wrong password) doesn't. So that's how you can verify that the provided password is valid or not, by just comparing the hashes.
There is no way to prohibit decompiling of java.
But you can make it hard to understand the decompiled code if you use an obfuscator.
E.g. https://www.guardsquare.com/en/products/proguard
This changes all you method-, class-, variable- names to meaningless short names.
A side-effect is that your class file will shrink too.
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.
I have two PublicKey object.I want to compare both for equality or to check which is latest object using java security API or bouncy castle API.How can i achieve this?
You can use equals
if (!key.equals(copyKey)){
System.out.println("not equals!");
}
or check the hashcode of the keys
if (key.hashCode() != copyKey.hashCode())
{
System.out.println("public key hashCode check failed");
}
or compare the hex string of the two public keys
String encodedKey1 = new String(Hex.encode(key1.getEncoded()));
String encodedKey2 = new String(Hex.encode(key2.getEncoded()));
if (!encodedKey1.equals(encodedKey2)){
System.out.println("not equals!");
}
You have a lot of key comparision and check samples at Bouncy Castle Tests, take a look at the org.bouncycastle.jce.provider.test package for some code. BC is not strictly necesary you can do the comparision with the default java security classes.
Normally public keys are compared using some kind of ID. It depends on the protocol how the key ID is calculated. The best method is probably to keep to the PKCS#11 specifications which defines methods of calculating key ID's.
The creation date is not an integral part of the key itself. Either you have to define it elsewhere, or you should use a public key container such as an X509 certificate. Note that you could use the (hex representation of the) key ID to find the creation date in a map.
It's probably best to use a SHA-1 hash over the modulus as ID. The modulus of both the public key and private key are identical and should be different for each key pair. The following code calculates the ID for an RSA public key.
Obviously you can always directly compare the moduli of two keys as well. Key ID's are a bit easier to store though.
public class CreateRSAPublicKeyID {
/**
* Creates a key ID for a given key.
*
* #param key the key
* #return the key ID for the given key
*/
public static byte[] createKeyID(Key key) {
if (key instanceof RSAKey) {
RSAKey rsaKey = (RSAKey) key;
BigInteger modulus = rsaKey.getModulus();
if (modulus.bitLength() % Byte.SIZE != 0) {
throw new IllegalArgumentException("This method currently only works with RSA key sizes that are a multiple of 8 in bits");
}
final byte[] modulusData = i2os(modulus, modulus.bitLength() / Byte.SIZE);
MessageDigest sha1;
try {
sha1 = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-1 message digest should be available in any Java SE runtime", e);
}
return sha1.digest(modulusData);
}
throw new UnsupportedOperationException("Key type not supported");
}
/**
* Integer to octet string (I2OS) creates a fixed size, left padded, big-endian octet string representation for
* a given integer.
*
* #param i the integer
* #param octets the number of octets (bytes)
* #return the octet string representation of i
*/
public static byte[] i2os(BigInteger i, int octets) {
if (i.bitLength() > octets * Byte.SIZE) {
throw new IllegalArgumentException("i does not fit in " + octets + " octets");
}
final byte[] is = i.toByteArray();
if (is.length == octets) {
return is;
}
final byte[] ius = new byte[octets];
if (is.length == octets + 1) {
System.arraycopy(is, 1, ius, 0, octets);
} else {
System.arraycopy(is, 0, ius, octets - is.length, is.length);
}
return ius;
}
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.length; i++) {
sb.append(String.format("%02X", data[i]));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair pair = kpg.generateKeyPair();
byte[] keyID = createKeyID(pair.getPublic());
System.out.println(toHex(keyID));
}
}
Note that the getModulus() command may not be compatible with some keystores (e.g. those ones that represent HSM tokens or smart cards).
Lookin at Oracle's documentation, I think you can compare PublicKey using its 3 getters : getAlgorithm, getEncoded, getFormat doing this :
oldKey.getAlgorithm().equals(newKey.getAlgorithm()) and so on.
Edit
Actually reinitializing the cipher is not that slow. Creating the key itself is slow because of the iteration count.
Also, the iteration count is ignored and never used in the encryption itself, only on the key generation. The JCE api is kind of misleading depending on the chosen algorithm
Original post
As cryptography in Java is quite... cryptographic, im struggling to do some optimizations. In the functional aspect, this class works quite well and i hope it serves as an example of AES encryption usage
I have a performance issue when encrypting and decrypting data using AES implementation of BouncyCastle (im not comparing, thats the only one implementation I tested). Actually this problem is generic to any cipher I decide to use.
The main issue is: can i avoid the two ciphers whole re-initialization per encrypt/decrypt call? They are too expensive
For the sake of simplicity, keep in mind that the following code had its exception handling removed and a lot of simplification was made to keep the focus on the problem. The synchronized blocks are there to guarantee thread safety
By the way, feedbacks on any part of the code are welcome
Thx
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class AES {
private static final int ITERATIONS = 120000;
private static final int SALT_SIZE_IN_BYTES = 8;
private static final String algorithm = "PBEWithSHA256And128BitAES-CBC-BC";
private static final byte[] KEY_SALT = "a fixed key salt".getBytes(Charset.forName("UTF-8"));
private Cipher encryptCipher;
private Cipher decryptCipher;
private SecretKey key;
private RandomGenerator randomGenerator = new RandomGenerator();
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
Security.addProvider(new BouncyCastleProvider());
}
public AES(String passphrase) throws Exception {
encryptCipher = Cipher.getInstance(algorithm);
decryptCipher = Cipher.getInstance(algorithm);
PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(), KEY_SALT, ITERATIONS);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
key = keyFactory.generateSecret(keySpec);
}
public byte[] encrypt(byte[] data) throws Exception {
byte[] salt = randomGenerator.generateRandom(SALT_SIZE_IN_BYTES);
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONS);
data = DataUtil.append(data, salt);
byte[] encrypted;
synchronized (encryptCipher) {
// as a security constrain, it is necessary to use different salts per encryption
// core issue: want to avoid this reinitialization to change the salt that will be used. Its quite time consuming
encryptCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, parameterSpec);
encrypted = encryptCipher.doFinal(data);
}
return DataUtil.append(encrypted, salt);
}
public byte[] decrypt(byte[] data) throws Exception {
byte[] salt = extractSaltPart(data);
data = extractDataPart(data);
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONS);
byte[] decrypted;
synchronized (decryptCipher) {
// as a security constrain, it is necessary to use different salts per encryption
// core issue: want to avoid this reinitialization to change the salt that will be used. Its quite time consuming
decryptCipher.init(javax.crypto.Cipher.DECRYPT_MODE, key, parameterSpec);
decrypted = decryptCipher.doFinal(data);
}
byte[] decryptedSalt = extractSaltPart(decrypted);
if (Arrays.equals(salt, decryptedSalt))
return extractDataPart(decrypted);
else
throw new IllegalArgumentException("Encrypted data is corrupted: Bad Salt");
}
protected byte[] extractDataPart(byte[] bytes) {
return DataUtil.extract(bytes, 0, bytes.length - SALT_SIZE_IN_BYTES);
}
protected byte[] extractSaltPart(byte[] bytes) {
return DataUtil.extract(bytes, bytes.length - SALT_SIZE_IN_BYTES, SALT_SIZE_IN_BYTES);
}
// main method to basic check the code execution
public static void main(String[] args) throws Exception {
String plainText = "some plain text, have fun!";
String passphrase = "this is a secret";
byte[] data = plainText.getBytes(Charset.forName("UTF-8"));
AES cipher = new AES(passphrase);
byte[] encrypted = cipher.encrypt(data);
byte[] decrypted = cipher.decrypt(encrypted);
System.out.println("expected: true, actual: " + Arrays.equals(data, decrypted));
}
}
// Utility class
class RandomGenerator {
private SecureRandom random = new SecureRandom();
public RandomGenerator() {
random = new SecureRandom();
random.nextBoolean();
}
public synchronized byte[] generateRandom(int length) {
byte[] data = new byte[length];
random.nextBytes(data);
return data;
}
}
// Utility class
class DataUtil {
public static byte[] append(byte[] data, byte[] append) {
byte[] merged = new byte[data.length + append.length];
System.arraycopy(data, 0, merged, 0, data.length);
System.arraycopy(append, 0, merged, data.length, append.length);
return merged;
}
public static byte[] extract(byte[] data, int start, int length) {
if (start + length > data.length)
throw new IllegalArgumentException("Cannot extract " + length + " bytes starting from index " + start + " from data with length " + data.length);
byte[] extracted = new byte[length];
System.arraycopy(data, start, extracted, 0, length);
return extracted;
}
}
You're out of luck. If you're picking a new salt each time, that means you're using a new key for encryption/decryption each time, which means you need to call init each time.
If you want something faster, just salt your message:
byte[] salt = randomGenerator.generateRandom(SALT_SIZE_IN_BYTES);
encryptCipher.update(salt);
encrypted = encryptCipher.doFinal(data);
That way, you use the same key every time so you don't need to reinitialize it. (Don't use PBE, just use 128 bit AES/CBC). It's hard to know if that is adequate for your needs without knowing how you plan to apply this encryption in the real world.
p.s. ITERATIONS == 120000? No wonder it is so slow.
Actually reinitializing the cipher is not that slow. Creating the key itself is slow because of the iteration count.
Also, the iteration count is ignored and never used in the encryption itself, only on the key generation. The JCE API is kind of misleading depending on the chosen algorithm
About the salt: Adding a salt to the plain message is completely unnecessary. What I really should use to achieve randomness in each encryption is using a random Initialization Vector, that can be appended or prepended to the ciphertext after the encryption just like the salt.
If you're just transmitting data and need it to be encrypted in flight, use TLS/SSL. Its way faster and won't break.
Make Sure you use some authentication on your cipher-text. Either use a MAC or better use AES in GCM or CCM modes. Otherwise you encryption is insecure.
As to your question : yes its fixable.
Just generate the key once and reuse it/ . AES is safe to use to send multiple messages with the same key. So just derive the key/Cipher once and keep using it.
Just make sure you use a fresh IV for each message.
Can someone provide an example for java/android on how to hash a password using PW_HASH_ITERATION_COUNT iterations of sha512 + salt?
in pseudo code:
hash = sha512(concat(pw,salt));
for (i = 1; i<PW_HASH_ITERATION_COUNT; i++){
hash = sha512(concat(hash,concat(pw,salt)));
}
Where z = concat(x,y) is the concatenation of x and y.
Maybe using MessageDigest ?
What would you suggest as PW_HASH_ITERATION_COUNT? How many iterations would be the maximum so that this might even run on some older devices (2.1+)
UPDATE UPDATE UPDATE
Due to good reasons, we will use bcrypt to encrypt our passwords. We use the jBCrypt implementation.
Anyway.. to answer the question... this is the code for the question above to use SHA-512 with the MessageDigest:
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import de.seduceme.utils.Base64;
public class PwStorage {
public static int PW_HASH_ITERATION_COUNT = 5000;
private static MessageDigest md;
public static void main(String[] args) {
String pw = "teüöäßÖst1";
String salt = "e33ptcbnto8wo8c4o48kwws0g8ksck0";
try {
md = MessageDigest.getInstance("SHA-512");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("No Such Algorithm");
}
String result = PwStorage.hashPw(pw, salt);
System.out.println(result);
// result: 2SzT+ikuO9FBq7KJWulZy2uZYujLjFkSpcOwlfBhi6VvajJMr6gxuRo5WvilrMlcM/44u2q8Y1smUlidZQrLCQ==
}
private static String hashPw(String pw, String salt) {
byte[] bSalt;
byte[] bPw;
try {
bSalt = salt.getBytes("UTF-8");
bPw = pw.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported Encoding", e);
}
byte[] digest = run(bPw, bSalt);
for (int i = 0; i < PW_HASH_ITERATION_COUNT - 1; i++) {
digest = run(digest, bSalt);
}
return Base64.encodeBytes(digest);
}
private static byte[] run(byte[] input, byte[] salt) {
md.update(input);
return md.digest(salt);
}
}
With this Base64 lib.
Read my post here, especially the post I linked to about password hashing.
You should ideally use bcrypt or scrypt rather than doing your own password hashing.
But if you must, you should run for a few thousand iterations at the minimum, preferably more.
Yes, you can use MessageDigest for SHA-512. Each time you call digest, the state of the object automatically resets, which is really handy---you can start updating for the next iteration straight away.
But I still think you should use bcrypt or scrypt instead. For your own good, and the good of your users. :-)
a HMAC is found to be sufficient for what you wanna do and it does only 2 iterations
it boils down to
hash = sha512(concat(xor(salt,nonce2),sha512(concat(xor(salt,nonce1),pw)));