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.
Related
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.
I am currently creating application using Java, I googled password encryption with java but the results are so enormous I felt overwhelmed. How would I encrypt and decrypt a password using Java? And what is the best practice for encrypting and decrypting passwords? I am guessing MD5 is not a way to go since it is a one way hash. I am using struts2 as my framework, was wondering if they provide password encryption
Updated:
Try JBCrypt:
String password = "MyPassword123";
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));
System.out.println(hashed); // $2a$12$QBx3/kI1SAfwBDFOJK1xNOXK8R2yC7vt2yeIYusaqOisYbxTNFiMy
Download jBCrypt-0.3 from here, check README file for more details.
Also I don't recommend to use MD5 because, it's already broken. Instead of that you can use SHA512 it's secure hashing method, you can use MessageDigest. Below code I am using in one of my project, which works perfectly
public String encode(String password, String saltKey)
throws NoSuchAlgorithmException, IOException {
String encodedPassword = null;
byte[] salt = base64ToByte(saltKey);
MessageDigest digest = MessageDigest.getInstance("SHA-512");
digest.reset();
digest.update(salt);
byte[] btPass = digest.digest(password.getBytes("UTF-8"));
for (int i = 0; i < ITERATION_COUNT; i++) {
digest.reset();
btPass = digest.digest(btPass);
}
encodedPassword = byteToBase64(btPass);
return encodedPassword;
}
private byte[] base64ToByte(String str) throws IOException {
BASE64Decoder decoder = new BASE64Decoder();
byte[] returnbyteArray = decoder.decodeBuffer(str);
if (log.isDebugEnabled()) {
log.debug("base64ToByte(String) - end");
}
return returnbyteArray;
}
well, as I know we have following some algorithm to secure password.
MD5 -
PBKDF2 -
SHA -
BCrypt and SCrypt -
among this BCrypt and SCrypt are the more secure way for password security.
There is quite nice project dedicating to solving that problem in Java.
Essentially, it provides two ways of encrypting user passwords:
- MD5
- SHA1
Take a look to the link:
jasypt
for me i see that MD5 its the best way and you don't need to decrypt the password in case the user forgot his password you can give him a way to generate a new one and for the log in you can compare just the hash existing in the data base and the one entred by the user
Always use ONE WAY HASH ALGORITHM.
I would say GO with MD5 hashing. While storing password in DB, use MD5 hashing. So that if you have your password as pass, after hashing it will get stored as asjasdfklasdjf789asdfalsdfashdflasdf (32 character).
As you said, you want to de-crypt the password also. I would say don't do that. While checking the password against DB, what you can do is hash the password and compare that string with what you have in database.
if (DoHashMD5(myPass).equals(rs.getString(2))) {
System.out.print("You are registered user!!!");
} else {
System.out.print("Invalid user!!!");
}
here rs.getString(2) would be your query parameter.
I would like to create a file protected by a password in JAVA.
What I mean is, once I launch the program, one file created by my program would be directly protected by previously determined password.
Is there an easy way to do it ?
Once again, my aim is not to create a file and then add it a password, but right during the creation protecting the file by a password.
Actually, I want the current runner program not having access in reading/editing the created file EXCEPT if he/she has the password previously set.
So anyway, if some of you know an easy way to protect files when writing them thanks to java, I would be most grateful.
Have a nice day!
You want to encrypt your file('s content) with a password. Here is a pretty well known library to do it: http://www.jasypt.org/
From their site:
..encrypting and decrypting a text...
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);
String myEncryptedText = textEncryptor.encrypt(myText);
...
String plainText = textEncryptor.decrypt(myEncryptedText);
You can read/write the encrypted content to your file.
When you want to encrypt files, strings, etc there are 2 main approaches.
You should start by building a class or method to convert ur string/file to an array of bytes. Build another method to convert the array of bytes back to the string/file.
You may encrypt a file using 2 approaches:
1 - Symmetric key - A secret word (usually a huge string of chars or a password set by the user) will encrypt your file and password, and the same password will be used to decrypt.
2 - Asymmetric key - You generate a pair of keys. One is called the public key and the other is called a private key. Public keys are used to encrypt files, private keys to decrypt.
This would be the more 'professional' approach.
If you want a really safe approach, you should download GnuPG. GnuPG is an executable that manages assymmetric encryption, you may build a class to work with GnuPG and let GnuPG manage ur encryption/decryption process.
Theres an unsafe approach that is 'native' to java (symmetric key) that may work out for you:
Encryption:
byte[] key = //... password converted to an array of bytes
byte[] dataToSend = ...
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k =
new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(dataToSend);
Decryption:
byte[] key = //
byte[] encryptedData = //
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k =
new SecretKeySpec(key, "AES");
c.init(Cipher.DECRYPT_MODE, k);
byte[] data = c.doFinal(encryptedData);
Hope this helps.
If the file is a plain text file, then not giving the user access to the file without a password in your program does not really password-protect the data, because the user can just open the file with some other program. So IF the file is a text file, then I think you must use encryption.
You can use the comment by #mazaneicha to help you get started in this direction. If you want to dive more into it, you can look at the Java Cryptography architectre and the javax.crypto java docs.
If your file is not human-readable, and only your program understands it, then I would make the first line or first n Bytes of the file a password. If you prefer, you could save another password file in the same directory and use that to authenticate the user before deciding if the user has the right to view the file. A common way to encrypt a password is with an MD5 hash function. The user enters a password, you compute the hash of it, then compare the computed hash with the hash value read from the file:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Use to encrypt passwords using MD5 algorithm
* #param password should be a plain text password.
* #return a hex String that results from encrypting the given password.
*/
static String encryptPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuilder hexString = new StringBuilder();
for (int i=0;i<byteData.length;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1)
hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
catch(java.security.NoSuchAlgorithmException missing) {
return password;
}
}
I'm having a problem with MessageDigest returning different hash values on different computers.
One computer is running 32-bit Java on Windows Vista and the other is running 64-bit Java on Mac OS. I'm not sure if it is because MessageDigest is machine dependent, or I need to explicitly specify a character encoding somewhere, or perhaps something else. Here's the
code:
public static boolean authenticate(String salt, String encryptedPassword,
char[] plainTextPassword ) throws NoSuchAlgorithmException {
// do I need to explcitly specify character encoding here? -->
String saltPlusPlainTextPassword = salt + new String(plainTextPassword);
MessageDigest sha = MessageDigest.getInstance("SHA-512");
// is this machine dependent? -->
sha.update(saltPlusPlainTextPassword.getBytes());
byte[] hashedByteArray = sha.digest();
// or... perhaps theres a translation problem here? -->
String hashed = new String(hashedByteArray);
return hashed.equals(encryptedPassword);
}
Should this code execute differently on these two different machines?
If it is machine dependent the way I've written it, is there another way hash these passwords that is more portable? Thanks!
Edit:::::
This is the code I'm using to generate the salts:
public static String getSalt() {
int size = 16;
byte[] bytes = new byte[size];
new Random().nextBytes(bytes);
return org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(bytes);
}
Solution:::
Thanks to the accepted solution, I was able to fix my code:
public static boolean authenticate_(String salt, String encryptedPassword,
char[] plainTextPassword ) throws NoSuchAlgorithmException, UnsupportedEncodingException {
// This was ok
String saltPlusPlainTextPassword = salt + new String(plainTextPassword);
MessageDigest sha = MessageDigest.getInstance("SHA-512");
// must specify "UTF-8" encoding
sha.update(saltPlusPlainTextPassword.getBytes("UTF-8"));
byte[] hashedByteArray = sha.digest();
// Use Base64 encoding here -->
String hashed = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(hashedByteArray);
return hashed.equals(encryptedPassword);
}
Encodings are causing you problems. First here:
saltPlusPlainTextPassword.getBytes()
That will use the default encoding for the machine. Bad idea. Specify "UTF-8" as a simple solution. (It's guaranteed to be present.)
Next this causes issues:
String hashed = new String(hashedByteArray);
hashedByteArray is arbitrary binary data. To safely convert it to text, either use a base-64 encoding or just hex. Again, you're currently using the default encoding, which will vary from machine to machine. There are loads of 3rd party libraries for base64 encoding in Java.
Likely Jon Skeet's solution above is the cause, and his recommendations should definitely be taken into account, but another possible cause is a misunderstanding of salt.
Salt is a semi-secret random value that is applied to a String prior to hashing. This makes it harder to perform a brute force attack when trying to guess what an originating String was because the salt is presumably unknown to the attacker.
Salt values generally differ installation to installation. Its possible that the actual cause is just that you have the salt values set differently on the different machines.
First and foremost, I am not a Java programmer. I'm looking for an example solution to this problem because the Java developer I have does not have much experience working with encryption. Everything we've found online pertains to encrypting web pages and dealing with the MS keystore. We just want to work with a single string from PowerBuilder (PB) and be able to decrypt it in Java. The restriction here is the MS library. Due to certain limitations, we are stuck with using this method of encrypting so it's up to the Java side to handle what's being thrown at it.
What I have is a PB version 10.2 program that needs to call this Java utility and pass it a username & password. We are trying to encrypt the password as a command line friendly string as that is how PB will make the call to the Java app.
In PB I'm using the following object:
http://www.topwizprogramming.com/freecode_crypto.html
What the code is doing, is wrapping the Microsoft cryptographic API found in advapi32.dll. The functions it uses are:
CryptAcquireContext
http://msdn.microsoft.com/en-us/library/aa379886(VS.85).aspx
CryptCreateHash
http://msdn.microsoft.com/en-us/library/aa379908(VS.85).aspx
CryptHashData
http://msdn.microsoft.com/en-us/library/aa380202(VS.85).aspx
CryptDeriveKey
http://msdn.microsoft.com/en-us/library/aa379916(VS.85).aspx
CryptEncrypt
http://msdn.microsoft.com/en-us/library/aa379924(VS.85).aspx
It's using the Microsoft Strong Cryptographic Provider and PROV_RSA_FULL. The code takes the data to be encrypted, converts it to a BLOB which is then passed to the encryption functions. There, it acuires a context, creates a hash object from the context, hashes the password, gets a session key from the hash, then calls encrypt/decrypt. Last thing is it takes the BLOB returned and converts it to a string under the ANSI character set.
There are a number of constants which at a glance I understand where some come from, others not so much:
Constant String KEY_CONTAINER = "MyKeyContainer"
Constant ULong PROV_RSA_FULL = 1
Constant ULong CALG_MD5 = 32771
Constant ULong CALG_RC4 = 26625
Constant ULong ENCRYPT_ALGORITHM = CALG_RC4
Constant ULong CRYPT_NEWKEYSET = 8
Constant ULong ERROR_MORE_DATA = 234
Whether this is done in 1.5 using something like BouncyCastle or 1.6 with the Sun crypto interface for MS I don't care, we're just dying to see this work and are honestly over our heads.
Hey i need to crypt a string and store it a file and then I need to read the file again and decrypt the same string back.
But i dont want to crypt the whole file. Once I have got the required crypted value stored in the file ,i need to convert that alone to the original string.
Can you please help me out with the sample code.
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Decrypt
{
public static void main(String... argv)
throws Exception
{
byte[] password = "password".getBytes("UTF-8");
byte[] ciphertext = { -68, -112, 66, 78, 85, 50, 22, -63,
16, 24, -45, 4, -116, -14, 88, 34,
-85, 116, 105, 59, 45, -126 };
byte[] plaintext = decrypt(password, ciphertext);
System.out.println(new String(plaintext, "UTF-8"));
}
public static byte[] decrypt(byte[] password, byte[] ciphertext)
throws GeneralSecurityException
{
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] hash = digest.digest(password);
Cipher rc4 = Cipher.getInstance("RC4");
rc4.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash, "RC4"));
return rc4.doFinal(ciphertext);
}
}
N.B.:
This "encryption" is horrible. RC4 is a key stream cipher. Never use the same keystream cipher key for more than one message! Using the same password in this way for multiple messages makes it trivial to recover the plain text and the key given multiple cipher texts. Given the weakness in MD5, they can probably recover the password too. These flaws are enough to compromise a good stream cipher, but RC4, like MD5, has its own vulnerabilities and is not recommended for new applications.
I'm sure that you knew all that, and are constrained by some legacy application, but if other people see this answer, they need to understand that the PowerBuilder "crypto" library you are compelled to use is incompetently implemented.
Since cipher input and output is always "binary", a text encoding is commonly used, such as Base-64 or Base-85 when the cipher text has to pass through a text-oriented channel (like the command line). If possible, can you Base-64 encode the cipher text before invoking the Java utility? That would insulate you from any character encoding issues.