Reputation: 25
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
Upvotes: 2
Views: 974
Reputation: 85461
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.
Upvotes: 2
Reputation: 10151
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.
Upvotes: 1