I wanted to allow having a security question for users. I am using Spring3 and was not sure if there exists a standard way of doing this. Can anyone point me to such standard it at all it exists?
I'm not familiar with a standard that covers this, but I would recommend that you treat the security questions just like you would the password...
Make sure that you turn autocomplete="off" so that the browser won't remember their answer
Generate a salted hash for the answer (or you could reuse the one for their password)
public byte[] generateSalt() throws NoSuchAlgorithmException {
// VERY important to use SecureRandom instead of just Random
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
// Generate a 8 byte (64 bit) salt as recommended by RSA PKCS5
byte[] salt = new byte[8];
secureRandom.nextBytes(salt);
return salt;
}
Encrypt the answer
public static byte[] getEncryptedAnswer(String answer, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(answer.toCharArray(), salt, 20000, 160);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return secretKeyFactory.generateSecret(spec).getEncoded();
}
Store both the salted hash and the encrypted answer. At this point you're pretty much using the answer to this question just like a password.
Now with all that said, I have to agree with jHilscher... Too many of these security questions are predictable or easy to guess. Favorite color, year they graduated, etc... I never answer these questions with the correct information. I recommend that you find a different way to reset the password when needed.
Related
I am currently working on a small bit of code, which is supposed to encrypt a byte[] before it gets stored inside a file. In the following snippet, I am trying to generate a SecretKey from a Password using PBKDF2.
public SecretKey deriveKey(String password)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec specs = new PBEKeySpec(password.toCharArray());
SecretKey key = kf.generateSecret(specs);
return key;
}
This throws an exception
java.security.spec.InvalidKeySpecException: Salt not found
at the last line before the return statement. This seems quite confusing to me as the PBEKeySpec does explicitly not require any more parameters as seen here.
This is not a tragedy, I was to add salt later anyway and it's even already defined further above but I was curious whether anybody knows why this happens. I might be doing something terribly wrong and just didn't notice.
It is not about whether PBEKeySpec class needs a salt or not. The algorithm PBKDF2
needs a salt. The API is a generic api which should cater to multiple algorithms, some of which might be defined in future.
Is my implementation of Salting and SHA-512 hashing passwords correct/secure?
I know that I should iterate the algorithm a couple of times on the salted password. Other than that what more should i implement to make it secure or is this enough?
public static String[] SHA512(String password)
{
//Generates the salt
SecureRandom saltRandomizer = new SecureRandom();
byte[] salt = new byte[64]; //The same size as the output of SHA-512 (512 bits = 64 bytes)
saltRandomizer.nextBytes(salt);
String encodedSalt = Base64.encodeToString(salt, Base64.DEFAULT);
//Prepends the salt to the password
String saltedPassword = encodedSalt + password;
//Hashed the salted password using SHA-512
MessageDigest digester;
byte[] digest = null;
try {
digester = MessageDigest.getInstance("SHA-512");
digester.reset();
digester.update(saltedPassword.getBytes());
digest = digester.digest();
} catch (NoSuchAlgorithmException e) {
System.out.println("No such algorithm");
e.printStackTrace();
}
String[] passwordPlusSalt = new String[2];
passwordPlusSalt[0] = Base64.encodeToString(digest, Base64.DEFAULT);
passwordPlusSalt[1] = encodedSalt;
return passwordPlusSalt;
}
Thanks in advance
No. It is not secure. Take a look at this line:
SecureRandom saltRandomizer = new SecureRandom();
I notice you don't specify a PRNG or provider. I'm no expert but I understand the SUN CSP default is to use sun.security.provider.NativePRNG on Solaris and Linux, which simply provides the output of /dev/urandom which may (or may not) be suitable for your needs (it is not, for example, recommended for the generation of cryptographic keys).
It is therefore recommended to always specify a PRNG and provider, as follows:
SecureRandom.getInstance("SHA1PRNG", "SUN");
Furthermore, it is advisable to:
Periodically throw away the existing java.security.SecureRandom instance and create a new one. This will generate a new instance with a new seed.
Periodically add new random material to the PRNG seed by making a call to java.security.SecureRandom.setSeed(java.security.SecureRandom.generateSeed(int)).
The point I'm making is that security is a complex topic that is so easy to get wrong. Don't try and homebrew a solution. Use an existing library. To quote Thomas Pornin's excellent answer:
Complexity is bad. Homemade is bad. New is bad.
Sources:
https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
http://en.wikipedia.org/wiki//dev/random
http://www.cigital.com/justice-league-blog/2009/08/14/proper-use-of-javas-securerandom/
I think that taking action to hash passwords with a salt is a good step for securing your application and accessing higher security levels. However, there are problems with your implementation:
not using a seed for random bytes generation
not keeping track of the generated random bytes to use them for password verification
using string concatenation does not add algorithmic complexity but looks like security by obscurity, which has been proven to be inefficient in security critical environments.
Instead, use a standard HMAC algorithm specifically designed for your need. A list of algorithms supported by java 7 is available here. PBKDF2WithHmacSHA1 is a good choice.
It can simply be used like this:
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] secretPassword = skf.generateSecret(spec).getEncoded();
A complete working code can be found here. It should limit the impact of a brute force/dictionnary attack, if your password database falls in wrong hands.
Note that the salt is a critical ring. Depending on your security requirements, you can store it on a separate filesystem, type or load it at server startup. There can even be specialized (and generally expensive) Hardware Security Modules which can keep it in safety.
I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).
I've been having trouble encrypting with an RSA public key. Here is a sample JUnit code that reproduces the problem:
public class CryptoTests {
private static KeyPair keys;
#BeforeClass
public static void init() throws NoSuchAlgorithmException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = CryptoUtils.getSecureRandom();
keyGen.initialize(2176, random);
keys = keyGen.generateKeyPair();
}
#Test
public void testRepeatabilityPlainRSAPublic() throws EdrmCryptoException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
byte[] plaintext = new byte [10];
Random r = new Random();
r.nextBytes(plaintext);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted1 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted2 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted3 = rsa.doFinal(plaintext);
assertArrayEquals(encrypted1, encrypted2);
assertArrayEquals(encrypted1, encrypted3);
}
}
The result? The assertion fails.
Why is this behaviour seen here? As far as I remember from my crypto classes, any key can be used for encryption. Yet this is not what happens here.
I've tested the same thing with the private key, and I get a repeatable output.
If, for some reason, RSA encryption with a public key is forbidden, then why am I not getting an exception?
What must I do to get repeatable results?
P.S. My JDK is 1.6.0_22 running on an Ubuntu 10.10 box.
My guess is that it's applying randomized padding, precisely to make it more secure. From the RSA wikipedia page:
Because RSA encryption is a deterministic encryption algorithm – i.e., has no random component – an attacker can successfully launch a chosen plaintext attack against the cryptosystem, by encrypting likely plaintexts under the public key and test if they are equal to the ciphertext. A cryptosystem is called semantically secure if an attacker cannot distinguish two encryptions from each other even if the attacker knows (or has chosen) the corresponding plaintexts. As described above, RSA without padding is not semantically secure.
...
To avoid these problems, practical RSA implementations typically embed some form of structured, randomized padding into the value m before encrypting it. This padding ensures that m does not fall into the range of insecure plaintexts, and that a given message, once padded, will encrypt to one of a large number of different possible ciphertexts.
You can confirm that what is happening is that random padding is being added by initialising your Cipher with the string "RSA/ECB/NoPadding". Now, you should see that the ciphertext is identical in each case (though for reasons stated by another answerer, you shouldn't really do this in practice).
To add extra detail to Jon's answer:
When you do Cipher.getInstance("...") you have a number of options, as you've probably gathered. The Standard Algorithm Names specify what these are.
The one you asked for, RSA is by default RSA under PKCS1, which, to quote the wikipedia article:
There are two schemes for encryption
and decryption:
RSAES-OAEP: improved encryption/decryption scheme; based on
the Optimal Asymmetric Encryption
Padding scheme proposed by Mihir
Bellare and Phillip Rogaway.
RSAES-PKCS1-v1_5: older encryption/decryption scheme as first
standardized in version 1.5 of PKCS#1.
See RSALab's PKCS1 documentation for the detail of said padding schemes.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
I have a program that reads server information from a configuration file and would like to encrypt the password in that configuration that can be read by my program and decrypted.
Requirements:
Encrypt plaintext password to be stored in the file
Decrypt the encrypted password read in from the file from my program
How would I go about doing this? I was thinking of writing my own algorithm, but I feel it would be terribly insecure.
A simple way of doing this is to use Password Based Encryption in Java. This allows you to encrypt and decrypt a text by using a password.
This basically means initializing a javax.crypto.Cipher with algorithm "AES/CBC/PKCS5Padding" and getting a key from javax.crypto.SecretKeyFactory with the "PBKDF2WithHmacSHA512" algorithm.
Here is a code example (updated to replace the less secure MD5-based variant):
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class ProtectedConfigFile {
public static void main(String[] args) throws Exception {
String password = System.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("Run with -Dpassword=<password>");
}
// The salt (probably) can be stored along with the encrypted data
byte[] salt = new String("12345678").getBytes();
// Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
int iterationCount = 40000;
// Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);
String originalPassword = "secret";
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
One problem remains: Where should you store the password that you use to encrypt the passwords? You can store it in the source file and obfuscate it, but it's not too hard to find it again. Alternatively, you can give it as a system property when you start the Java process (-DpropertyProtectionPassword=...).
The same issue remains if you use the KeyStore, which also is protected by a password. Basically, you will need to have one master password somewhere, and it's pretty hard to protect.
Check out jasypt, which is a library offering basic encryption capabilities with minimum effort.
Yes, definitely don't write your own algorithm. Java has lots of cryptography APIs.
If the OS you are installing upon has a keystore, then you could use that to store your crypto keys that you will need to encrypt and decrypt the sensitive data in your configuration or other files.
I think that the best approach is to ensure that your configuration file (containing your password) is only accessible to a specific user account. For example, you might have an application-specific user, appuser, to which only trusted people have the password (and to which they su to).
That way, there isn't any annoying cryptography overhead and you still have a password which is secure.
I am assuming that you are not exporting your application configuration outside of a trusted environment (which I'm not sure would make any sense, given the question).
The big point, and the elephant in the room and all that, is that if your application can get hold of the password, then a hacker with access to the box can get hold of it too!
The only way somewhat around this, is that the application asks for the "master password" on the console using Standard Input, and then uses this to decrypt the passwords stored on file. Of course, this completely makes is impossible to have the application start up unattended along with the OS when it boots.
However, even with this level of annoyance, if a hacker manages to get root access (or even just access as the user running your application), he could dump the memory and find the password there.
The thing to ensure, is to not let the entire company have access to the production server (and thereby to the passwords), and make sure that it is impossible to crack this box!
Well to solve the problems of master password - the best approach is not to store the password anywhere, the application should encrypt passwords for itself - so that only it can decrypt them. So if I was using a .config file I would do the following, mySettings.config:
encryptTheseKeys=secretKey,anotherSecret
secretKey=unprotectedPasswordThatIputHere
anotherSecret=anotherPass
someKey=unprotectedSettingIdontCareAbout
so I would read in the keys that are mentioned in the
encryptTheseKeys, apply the Brodwalls example from above on them and
write them back to the file with a marker of some sort (lets say crypt:) to let the application know not to do it again, the output would look like this:
encryptTheseKeys=secretKey,anotherSecret
secretKey=crypt:ii4jfj304fjhfj934fouh938
anotherSecret=crypt:jd48jofh48h
someKey=unprotectedSettingIdontCareAbout
Just make sure to keep the originals in your own secure place...
See what is available in Jetty for storing password (or hashes) in configuration files, and consider if the OBF encoding might be useful for you. Then see in the source how it is done.
http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
Try using ESAPIs encryption methods. It's easy to configure and you can also easily change your keys.
http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
You
encrypt
decrypt
sign
unsign
hashing
time based signatures and much more with just one library.
Depending on how secure you need the configuration files or how reliable your application is, http://activemq.apache.org/encrypted-passwords.html may be a good solution for you.
If you are not too afraid of the password being decrypted and it can be really simple to configure using a bean to store the password key. However, if you need more security you can set an environment variable with the secret and remove it after launch. With this you have to worry about the application / server going down and not application not automatically relaunching.
If you are using Java 8, the use of the internal Base64 encoder and decoder can be avoided by replacing
return new BASE64Encoder().encode(bytes);
with
return Base64.getEncoder().encodeToString(bytes);
and
return new BASE64Decoder().decodeBuffer(property);
with
return Base64.getDecoder().decode(property);
Note that this solution doesn't protect your data as the methods for decrypting are stored in the same place. It just makes it more difficult to break. Mainly it avoids to print it and show it to everybody by mistake.