I'm working on an assignment for school regarding security and how to crack passwords using a dictionary. We are provided six hashed passwords, a dictionary of words, and a salt hash value. Three of the passwords are not salted, I have already cracked those. I am having trouble finding the three salted passwords.
I have my code set up to iterate through the large dictionary and hash each word and then compare to the hashed passwords. I've tried to hash the salt value on to the front and/or back of each word that I am hashing for comparison, but I can't find any matches. We're using three different algorithms (MD-5, SHA-256, and SHA-1). My program checks the length of the password hash and routes it to which algorithm needs to be used for cracking.
Here's an example of what I have running for the salted MD-5 password:
MessageDigest md = MessageDigest.getInstance("MD5");
md.update((word + salt).getBytes());
byte[] bytes = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff)
+ 0x100, 16).substring(1));
}
return sb.toString();
"word" is the password sent in to be checked, "salt" is the salt hex in a string. This returns the value after hashing to a for loop that is running through my dictionary looking for a match. I can't understand why it can find the three unsalted versions but appending the salt to the front (or back) wont work. Obviously I'm misunderstanding something here. Any clarity is greatly appreciated. Thanks!
Try something like this:
MessageDigest md5 = MessageDigest.getInstance("MD5");
Base64.Encoder base64 = Base64.getEncoder();
ShadowRecord r = new ShadoRecord("username", "usedSalt", "hashedPassword");
if (test(r, "secret")) {
System.out.println("Password is 'secret'");
}
public class ShadowRecord {
public final String username;
public final String salt;
public final String hash; // result of hash(password + salt)
public ShadowRecord(String username, String salt, String hash) {
this.username = username;
this.salt = salt;
this.hash = hash;
}
}
public boolean test(ShadowRecord r, String pwd) {
String input = pwd + r.salt;
String result = hash(input);
return result.equals(r.hash);
}
// input = pwd + salt
public String hash(String input) {
byte[] result = md5.digest(pwd.getBytes(StandardCharsets.UTF_8));
byte[] hash = base64.encode(result);
return new String(hash, StandardCharsets.UTF_8);
}
Related
Two scenarios:
Say my user provides me the following:
long firstId;
long secondId;
or
UUID firstId;
long secondId;
and I have a variable to define a secret string:
private String mySecret = "2m75eB4xmAtMrz5cYQdSCch9R5R3xU6G";
Can I use the user inputs to generate a secure random string on the fly without hardcoding a string in the code (which seems pretty insecure)? If my program restarts and I provide the same input, I should get the same secret generated.
When you talk about security you should give some info about the context and what are you tryng to do, for random string i suggest a digest:
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(
originalString.getBytes(StandardCharsets.UTF_8));
private static String bytesToHex(byte[] hash) {
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
You can use as input string something like:
String input = mySecret + (firstId + secondId);
Given the same input this digest will give you always the same result but you would be exposed to an attack that is called dictionary attack, in this way a cracker that know what algorithm you are using "sha-256" can retrieve the original secret from the digest.
So, I am currently learning SpringBoot and right now I am trying to store a password together with other data for the users in a database (MySql). In order for it to be secure I am using salted hashes to store. Generating these hashes and storing them in the database works fine, however when I try to validate the password by taking the salt and the password, I am getting a different hash, therefore the wrong result.
Below is my code.
First of: The class User where I start the validation
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long uID;
#NotNull
private String nname;
#NotNull
private String vname;
private String email;
private String telnr;
#Embedded
private Address address;
#NotNull
private boolean isAdmin;
private String hash;
// Default Constructor
public User() {
}
// Constructor
public User(String name, String vname, String email, String telnr, Address address, boolean isAdmin,
String password) throws NoSuchAlgorithmException {
HashHelper hashHelper = new HashHelper();
this.nname = name;
this.vname = vname;
this.email = email;
this.telnr = telnr;
this.address = address;
this.isAdmin = isAdmin;
this.hash = hashHelper.createHash(password);
}
public boolean validateHash(String password) {
HashHelper hashHelper = new HashHelper();
// Get the used Salt
String[] parts = this.hash.split(":");
byte[] salt = parts[0].getBytes();
// Create Hash with old salt
String newHash = hashHelper.getHash(password, salt);
if (parts[1] == newHash) {
return true;
}
return false;
}
Second, my class HashHelper where I handle everything to do with hashing. I use createHash whenever a new password gets stored (therefore, new salt) and getHash for validation with a specific salt.
public class HashHelper {
public HashHelper() {
}
public byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
sr.nextBytes(salt);
return salt;
}
// Create Salt and Hash and store them, seperated by :
public String createHash(String password) throws NoSuchAlgorithmException {
String hash = null;
byte[] salts = getSalt();
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salts);
byte[] bytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
hash=salts.toString() + ":" + sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
System.out.println("Hash: " + hash);
return hash;
}
public String getHash(String password, byte[] salt) {
String hash = "";
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] bytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
hash = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hash;
}
}
The call for validation is set in a CommandLineRunner just for testing and is the following:
Optional<User> user = userRepository.findById((long)10);
if (user.get().validateHash("Password")) {
System.out.println("Correct Password");
}
else {
System.out.println("Wrong password");
}
I think it was something to do with the getBytes() and toString() methods, because byte[] salt seems to have a shorter length when I try to validate it (around 11-12 bytes instead of 16) but I can't figure out why. Any help would be greatly appreciated!
Your question is moot, because you shouldn't use this algorithm to hash passwords, as it's not secure. Even if you aren't protecting anything important, users might use the same password in multiple places, and when an attacker cracks your insecure password table, they will be able to use many of those passwords elsewhere.
Use Argon2, or at least PBKDF2 to hash passwords, with at least 10,000 rounds of hashing.
The reason you are having trouble here is that you aren't storing the salt you use to hash the password. Without it, you'll never be able to compute the same hash. The problem is salts.toString() in the createHash() method:
hash=salts.toString() + ":" + sb.toString();
Calling toString() on a byte[] doesn't tell you anything about the content of the array. That's why you went to all the trouble to convert the result of the digest, bytes, into hexadecimal. You should do something similar to the salt.
Likewise, calling getBytes() on a string just encodes the string with the default encoding for your platform. That's not what you want.
Make sure that you use their equals() method when you compare String instances. The == operator will only tell you if they are the identical instance.
When storing byte arrays, which you'll still need to do with a good hash algorithm, I recommend using base-64, since there is great support for it in java.util.Base64, and it will yield more compact encodings of your array.
I've set up a fairly basic password hashing and salting system for saving passwords in my database. It works fine, but I'm not so sure about how the salt is being stored.
At the moment I'm creating it in a byte[] array, then converting that to a String, which is then stored in the database.
My only concern is that every salt starts with [B#, for example: [B#b24f11 or [B#1e71a51
Is it ok to store it like this, or should I also be leaving it as a byte[] array and storing it as binary data in the database? Or even doing something else?
public class PasswordHasher {
// calculates a hash, given a password and a salt
public static String getHash(String pass, String salt) {
String hashedPassword = null;
try{
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(salt.getBytes()); //update digest to include salt
byte[] hashedBytes = md.digest(pass.getBytes());
// convert byte array to hex
StringBuilder sb = new StringBuilder();
for (int i=0;i<hashedBytes.length;i++) {
sb.append(Integer.toHexString((int) hashedBytes[i] & 0xFF));
}
hashedPassword = sb.toString();
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}
return hashedPassword;
}
// calculates a hash, then returns both hash and salt to store in DB
public static String[] registerHashAndSalt(String pass){
String salt = getSalt();
String hashedPassword = getHash(pass, salt);
String[] hashAndSalt = {hashedPassword, salt};
return hashAndSalt;
}
// creates a random salt
private static String getSalt(){
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[16];
sr.nextBytes(salt);
return salt.toString();
}
}
You're not transforming the bytes to a string. You're calling toString() on a byte[], which returns the type of the array ([B) followed by the # symbol and by the hashCode of the array.
Use base 64 or Hex encoding to transform bytes to a printable string.
i need to encrypt password to insert in a database.Also when i need that password i need to decrypt this value.What is the simple way to do this?
Note : This operation have not to be very safe.
Please don't implement your current plans, instead you should use a MessageDigest to accomplish this. Apply a one way cryptographic hash function to the user's password (e.g. one of SHA-256, SHA-384, and SHA-512 [and there are others]) and a SALT to prevent rainbow table based attacks. Finally, for password resets, just replace the current password hash.
As an example,
// We need a bytesToHex method first. So, from -
// http://stackoverflow.com/a/9855338/2970947
final protected static char[] hexArray = "0123456789ABCDEF"
.toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
// Change this to something else.
private static String SALT = "123456";
// A password hashing method.
public static String hashPassword(String in) {
try {
MessageDigest md = MessageDigest
.getInstance("SHA-256");
md.update(SALT.getBytes()); // <-- Prepend SALT.
md.update(in.getBytes());
// md.update(SALT.getBytes()); // <-- Or, append SALT.
byte[] out = md.digest();
return bytesToHex(out); // <-- Return the Hex Hash.
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
public static void main(String[] args) {
System.out.println(hashPassword("Hello"));
System.out.println(hashPassword("Hello"));
System.out.println(hashPassword("Hello1"));
System.out.println(hashPassword("Hello2"));
}
Which should output
60C1E22D18D022F01EEF0CAF999E52FD44C0C8EFD2161E9F4D24120AB0AFC84D
60C1E22D18D022F01EEF0CAF999E52FD44C0C8EFD2161E9F4D24120AB0AFC84D
CAAC2288692DD57BADFAE0225A42E59E1979E0116D009EEF01912E8C75529515
E0A3963BFAF209A17422918CB1FC950A62858993CA9A7BA6F760B8D4688306FD
Demonstrating how tremendously different one character makes the resulting hash.
One more way is to use Encrypt class for encrypting your password with randomly generated keyvalue. But you need to store the keyvalue in your DB for encrypted password. Like this,
Integer randVal = random.nextInt();
Encrypt encrypt = new Encrypt();
// convert password to encrypted password
String encyppassword = encrypt.encryptText(
Integer.toString(randVal) + "",
your_password);
While decrypt you need to use keyvalue and encrypted password. Like this,
Decrypt decrypt = new Decrypt();
Integer randVal = keyvalue_from_db;
String decryptedPassword = decrypt.decryptText(
String.valueOf(randVal.toString()),
encrypted_password);
Hope this helps.
I am currently encoding a password. I have to decode the password. Here is the code to encode. I am trying to get the original password compare it. I have researched about MessageDigest that says it is a one-way method. Not sure how to get the original message. We have a decode method but it isn't giving me the original password - Base64.decode.
public static synchronized String getMD5_Base64(String input) {
if (!isInited) {
isInited = true;
try {
digest = MessageDigest.getInstance("MD5");
} catch (Exception ex) {
}
}
if (digest == null)
return input;
// now everything is ok, go ahead
try {
digest.update(input.getBytes("UTF-8"));
} catch (java.io.UnsupportedEncodingException ex) {
}
byte[] rawData = digest.digest();
byte[] encoded = Base64.encode(rawData);
String retValue = new String(encoded);
return retValue;
}
}
You cannot get the original password. Keep in mind that the digest and Base64 encoding do two completely different things. The MD5 digest creates a cryptographic hash of the data supplied to it. This is irreversible. Base64 is an encoding mechanism to convert data (which might contain unprintable binary data) into a string that is guaranteed to contain only printable characters. This step is reversible.
The standard way of checking a password is not to decode the original password and compare the plain text. What you need to do is take the encoding (MD5 hash then Base64 encode) you did on the original password and apply it to the newly supplied password. Then compare the stored encoded version with the newly encoded version. If they're the same then the passwords matched.
This design is a more secure mechanism than storing passwords that could be decoded. This way, if someone steals your password database they don't automatically have access to all the passwords of your users. In order to break into the system they'd still have to find a password that encoded to the same value. The point of cryptographic hashes like MD5 is to make that very difficult. On the other hand, MD5 is not considered a very secure hash anymore. You'd be better off using SHA1 or SHA256 (but remember, you can't change the existing stored passwords from their MD5 hash to another hash without the original password, which you don't have, i.e. you can't just convert your database of stored passwords).
MessageDigest with MD5 is one way hash. So, why don't you use javax.crypto which can encrypt and decrypt easily. Here is the example:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
public class EncryptDecrypt {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public EncryptDecrypt() throws Exception {
myEncryptionKey = "ThisIsSpartaThisIsSparta";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString.getBytes());
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
public static void main(String args []) throws Exception
{
EncryptDecrypt td= new EncryptDecrypt();
String target="password#123";
String encrypted=td.encrypt(target);
String decrypted=td.decrypt(encrypted);
System.out.println("String To Encrypt: "+ target);
System.out.println("Encrypted String: " + encrypted);
System.out.println("Decrypted String: " + decrypted);
}
}
The MD5 hash algorithm is, like all hash algorithms, one-way. The only way to recover the original password is to try every possibility until you get the one whose MD5 hash matches what you received.
If you're trying to compare the contents of the new password with the older passwords you can't use an MD5 hash. As Jherico noted, MD5 (and all hashes) are one-way meaning that you can't get the original text.
In order to do the compare you will have to keep the original value of the password around somewhere. The best way is probably to encrypt (and base64 the result) before storing it to the database. Then in order to do the compare, you decrypt each of the values and do the work that you want
One important note is that storing the user's passwords in any form that can be reversed is can be dangerous if not done properly.