Kyle
Kyle

Reputation: 25

Hashing vulnerability

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

Answers (2)

rustyx
rustyx

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

MrSmith42
MrSmith42

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

Related Questions