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();
Related
I'm trying to change encryption algorithm of existing project. But i have a little bit confusion. When i use "PBEWithHmacSHA512AndAES_256" as a parameter, it produce different result but when i use "PBEWithMD5AndDES" as a parameter it produce same result. My functions are :
public static synchronized String encrypt1(final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
public static synchronized String encrypt2 (final String textToEncrypt, final String pathPublicKey) throws Exception {
final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithHmacSHA512AndAES_256").generateSecret(pbeKeySpec);
// Prepare the parameter to the ciphers
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());
// Create the ciphers
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
// Encode the string into bytes using utf-8
final byte[] utf8 = textToEncrypt.getBytes("UTF8");
// Encrypt
final byte[] enc = cipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
}
Any suggestions, ideas will help me to figure out what's going on here.
Also this is produce different results:
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(new FileInputStream((pathOfJKSfile)), password.toCharArray());
Key key = keyStore.getKey(keyName, keyPass.toCharArray());
byte[] raw = key.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "PBEWithHmacSHA512AndAES_256");
final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);
final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
final byte[] enc = cipherEncrypt.doFinal(messageBytes);
System.out.println( new sun.misc.BASE64Encoder().encode(enc));
And i know that cipher.init() using "JceSecurity.RANDOM" for pruducing different results.
Both algorithms, PBEWithHmacSHA512AndAES_256 and PBEWithMD5AndDES, first generate an encryption key by processing a password, a salt and an iteration count (using HmacSHA512 and MD5, respectively) and then encrypt the plain text (with AES-256 and DES, respectively) using this key and the CBC-mode. When the Cipher-instance is initialized, a pseudo-random initialization vector (IV) is generated that is required for the CBC- mode.
In the context of PBEWithHmacSHA512AndAES_256, the IV is generated using the SecureRandom implementation of the highest-priority installed provider, at least for the Cipher#init()-method used in the code (note that there are several overloads of the Cipher#init()-method and that a SecureRandom-instance can also be passed explicitly). I.e. with each Cipher-initialization a new (random) IV is generated and therefore the encrypted text is always different, even for an identical plain text. For this reason, the encrypted text in your examples changes in this context.
In the context of PBEWithMD5AndDES, the IV is only determined by the password, the salt, the iteration count (and of course the MD5-hash-algorithm itself). Therefore, the IV and the encrypted text do not change in case of repetition (provided that password, salt, iteration count etc. are the same). For this reason, the encrypted text in your example does not change in this context.
The generation of a new, random IV during the Cipher-initalization makes sense with regard to the following requirements for the IV: For security reasons, an IV in CBC-mode (btw this also applies to other modes) may only be used once under the same key. In addition the IV must be unpredictable.
PBEWithMD5AndDES is deprecated.
EDIT:
The use of an IV is standard nowadays (for security reasons). A lot of information can be found on the Internet on this topic e.g. here. In the following I will only describe a few basic things.
The IV used for encryption must be stored in some way because it is required for decryption. The IV does not have to be kept secret, so that it is usually concatenated with the encrypted data (e.g. before the encrypted data) and stored together with them. During decryption, both parts can be separated because the length of the IV is known (16 Byte for AES). E.g for the concatenation in the encryption-method something like the following is used (let iv and enc be the byte-arrays with the IV and the encrypted data, respectively):
byte[] result = new byte[enc.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(enc, 0, result, iv.length, enc.length);
and in the decryption-method the corresponding counterpart (having in mind that the length of an IV in AES is 16 Byte).
In the encryption-method the IV can be determined with Cipher#getIV() (this must of course happen after calling Cipher#init()).
In the decryption-method you have to pass the IV to the PBEParameterSpec-ctor (e.g. let iv be the byte-array with the IV):
IvParameterSpec ivSpec = new IvParameterSpec(iv);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
The generation of an IV can also take place outside the Cipher-class, see e.g. Generating random IV for AES in Java. Then you have to pass that IV in the encryption-method in the same way as above described for the decryption-method.
Note, in connection with an IV some points have to be considered e.g. using a mode without an IV (e.g. ECB), using an IV consisting exclusively of 0-values, using a predictable IV or using an IV more than once under the same key etc. drastically reduces security in general, see e.g. here!
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?
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.
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.
SecureRandom random = new SecureRandom(); // quite heavy, look into a lighter method.
String stringToEncrypt = "mypassword";
byte[] realiv = new byte[16];
random.nextBytes(realiv);
Cipher ecipher = Cipher.getInstance("AES");
SecureRandom random = new SecureRandom(); // quite heavy, look into a lighter method.
byte[] realiv = new byte[16];
random.nextBytes(realiv);
byte[] secret = "somelongsecretkey".getBytes();
SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
byte[] encryptedData = ecipher.doFinal();
but the init() only takes in 3 parameters. I need a way to do something like:
ecipher.init(Cipher.ENCRYPT_MODE, stringToEncrypt, secretKey, random);
In general you don't need something that generates random numbers for an algorithm that has deterministic behavior. Furthermore, you don't need an IV when you are using ECB block mode, which is what Java defaults to. To be precise, Java defaults to "AES/ECB/PKCS5Padding" for in Cipher.getInstance("AES").
So you should be OK with code like this:
// lets use the actual key value instead of the platform specific character decoding
byte[] secret = Hex.decodeHex("25d6c7fe35b9979a161f2136cd13b0ff".toCharArray());
// that's fine
SecretKeySpec secretKey = new SecretKeySpec(secret, "AES");
// SecureRandom should either be slow or be implemented in hardware
SecureRandom random = new SecureRandom();
// first create the cipher
Cipher eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// filled with 00h characters first, use Cipher instance so you can switch algorithms
byte[] realIV = new byte[eCipher.getBlockSize()];
// actually fill with random
random.nextBytes(realIV);
// MISSING: create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(realIV);
// create the cipher using the IV
eCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
// NOTE: you should really not encrypt passwords for verification
String stringToEncrypt = "mypassword";
// convert to bytes first, but don't use the platform encoding
byte[] dataToEncrypt = stringToEncrypt.getBytes(Charset.forName("UTF-8"));
// actually do the encryption using the data
byte[] encryptedData = eCipher.doFinal(dataToEncrypt);
Now that looks a whole lot better. I've used the Apache commons codec for decoding the hexadecimal string.
Note that you need to save the realIV with the encryptedData, and that you haven't included integrity protection, e.g. a MAC (for passwords, you may not need that though).
I strongly suspect that what you want to do is call ecipher.doFinal(stringToEncrypt), possibly after a series of doUpdate(...) if you have longer strings.
.init() creates the cipher object, update() and doFinal() populate the encrypted output and take the plaintext as input.
Of course, you'll need to convert between String and a byte array.