I have written following function to compute Md5 checksum in Java.
class Utils {
public static String md5Hash(String input) {
String result = "";
try {
System.out.println("Input=" + input);
final MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(input.getBytes());
result = md.digest().toString();
} catch (Exception ee) {
System.err.println("Error computing MD5 Hash");
}
return result;
}
};
Calling Utils.md5Hash("abcde") multiple times gives different results. My understanding says md5 returns a deterministic and unique checksum for a string. Is that wrong? Else please let me know the bug in my implementation. Thanks
The toString() method of a byte array doesn't return a meaningful string. It returns the type of the array object, followed by the hashCode of the array.
Transform the byte array to a String using Hex or Base64 encoding if you want it printed. Apache commons-codec has methods to do that.
Also, make sure to specify en encoding which supports any kind of character to transform your string to a byte array. The method you're using uses the platform default encoding, which could fail if, for example, it's latin-1 and you're transforming non-latin-1 characters. UTF-8 is a good choice.
I have done using the following way :
public static String encryptedLoginPassword( String password )
{
String encryptedData="";
try{
MessageDigest algorithm = MessageDigest.getInstance("MD5");
byte[] defaultBytes = password.getBytes();
algorithm.reset();
algorithm.update(defaultBytes);
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
encryptedData=hexString.toString();
}catch(NoSuchAlgorithmException nsae){
}
return encryptedData;
}
int the code given by Dinup Kandel, I had to change this:
for (int i=0;i<messageDigest.length;i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
in to
if ((0xff & messageDigest[i]) < 0x10) {
hexString.append("0"
+ Integer.toHexString((0xFF & messageDigest[i])));
} else {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
to get my unit tests working.
note: i used this to verify the correct answer:
echo -n MyTestString | md5sum
Related
I am trying to build a simple password authenticator where passwords that have been hashed using SHA-256 .
I found a couple calculators online (http://onlinemd5.com/) that hashed "password" to "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
I tried a couple other passwords with expected results.
So I tried to implement a fairly straight forward set of code (or so I thought)
String pswd="password";
String storedPswd="5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8";
//first as a byte[]
Arrays.equals(hashWord(pswd),storedPswd.getBytes("UTF-8") );
...
private byte[] hashWord(String word) {
try {
return MessageDigest.getInstance("SHA-256").digest(word.getBytes("UTF-8"));
} catch (Exception e) {
throw new BadCredentialsException("Could not hash supplied password", e);
}
}
I also tried without success.
return storedPswd.toUpperCase().equals(DigestUtils.sha256Hex(password));
The Apache codec library (v1.10) and Java 1.6 gives me :
113459EB7BB31BDDEE85ADE5230D6AD5D8B2FB52879E00A84FF6AE1067A210D3
instead of
5E884898DA28047151D0E56F8DC6292773603D0D6AABBDD62A11EF721D1542D8
What am I missing ??
The Solution (wrong inputs):
updated Test Code:
String passwordSHA="5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8";
String complexSHA="8849fb9b221ddec0117e2796d16929179accf3a6012f738e1ed6c11af9cc2081";
#Test
public void testDigest() throws InterruptedException{
System.out.println("Starting Digest test");
String complexPassword = "a7$h1UC8";
try {
Assert.assertTrue(authenticateUser(complexPassword, complexSHA));
Assert.assertTrue(authenticateUser("password", passwordSHA));
Assert.assertTrue( hashWord(complexPassword).equals(complexSHA) );
} catch (Exception e) {
Assert.fail();
}
}
public boolean authenticateUser(String word, String stored) throws Exception {
String apache2Pswd = hashApache(word);
System.out.println(apache2Pswd);
return stored.equals(apache2Pswd);
}
private String hashApache(String pswd){
return DigestUtils.sha256Hex(pswd);
}
public static String hashWord(String word) throws Exception{
byte[] digest = MessageDigest.getInstance("SHA-256").digest(word.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
System.out.println(sb.toString());
return sb.toString();
}
With Results:
Starting Digest test
8849fb9b221ddec0117e2796d16929179accf3a6012f738e1ed6c11af9cc2081
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
8849fb9b221ddec0117e2796d16929179accf3a6012f738e1ed6c11af9cc2081
The hashWord method that you posted is not correct, it does not compile (is this your actual code?); it's not returning a value.
With this:
byte[] digest = MessageDigest.getInstance("SHA-256").digest("password".getBytes("UTF-8"));
for (byte b : digest) {
System.out.printf("%02x", b);
}
I do get the expected output:
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
The output 113459eb7bb31bddee85ade5230d6ad5d8b2fb52879e00a84ff6ae1067a210d3 is what you get when you calculate the SHA-256 hash over the string 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 instead of the original string password.
You are calculating the hash over the hex string containing the hash, instead of the hash of the original password.
An online resource that does not carefully document how it converts user input to binary before hashing is worthless for the purpose of comparing hashes.
It ultimately doesn't matter if your encoding method produces hashes compatible with anything else. What matters is that you get the same hash for the same input consistently (i.e. the hash procedure must be deterministic).
I am sending data from my java tomcat server to my browser using a WebSocket. I get the error: "Uncaught InvalidCharacterError: 'atob' failed: The string to be decoded is not correctly encoded."
Here is my code:
(java server code):
public void open(Session session)
{
String base64ImageString = generateImageString();
try
{
session.getBasicRemote().sendText(base64ImageString);
}
catch(IOException e)
{
e.printStackTrace();
}
}
private String generateImageString()
{
int imageData[] = new int[2];
imageData[0] = 255;
imageData[1] = 128;
String base64Image = "";
for(int i = 0; i < imageData.length; i++)
{
try
{
base64Image += Base64.encode(Integer.toString(imageData[i]).getBytes("UTF8"));
catch (UnsupportedEncodingException e)
}
catch( UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
return base64Image;
}
(JavaScript code):
function onMessage(evt)
{
base64ImageDataString = evt.data;
imageDataString = window.atob(base64ImageDataString);
}
My base64 string looks like this on the java and javascript side: [B#74193bd0[B#24a6103c
I am using org.glassfish.jersey.internal.util.Base64 if it matters. I am really stumped :(
My base64 string looks like this on the java and javascript side: [B#74193bd0[B#24a6103c
That's not base64. That's the concatenation of the result of calling toString() on two byte arrays. You're using a method which returns a byte[], not a string, which means your string concatenation is inappropriate. You could use Base64.encodeAsString - or use a different base64 library entirely (e.g. the iharder one). But really you shouldn't be doing any string concatenation.
Your generateImageString code is completely broken. It's not at all clear why you'd convert an integer to a string, get the UTF-8 representation of that, and then convert the byte array to base64... and then do that in a loop. That's just not the way to get anything meaningful.
I suspect you should actually be starting with a byte[] rather than an int[] - it's not clear what those values are meant to be - but then you want a single call to Base64.encode, passing the byte[] in. If you're calling Integer.toString or concatenating bits of Base64 data, you're doing it wrong.
[B#24a6103c represents a byte array as a string since Base64.encode returns a byte array.
You need to convert the byte array to a string before concatenating it to the String base64Image
I think you want to do this:
base64Image += new String(Base64.encode(Integer.toString(imageData[i]).getBytes("UTF8")));
I am currently trying to get Java to generate the same hash for a string as PHP's hash algorithm does.
I have come close enough:
hash('sha512', 'password');
outputs:
b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
Java code:
public static void main(String[] args) {
hash("password");
}
private static String hash(String salted) {
byte[] digest;
try {
MessageDigest mda = MessageDigest.getInstance("SHA-512");
digest = mda.digest(salted.getBytes("UTF-8"));
} catch (Exception e) {
digest = new byte[]{};
}
String str = "";
for (byte aDigest : digest) {
str += String.format("%02x", 0xFF & aDigest);
}
return str;
}
This outputs the same.
My problem is when I use the third argument within PHP's hash function. On PHP's site it's described as following:
raw_output
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
I am not quite sure how to implement this extra parameter. I think mainly my question would be, how do I convert a String object into a binary String object? Currently, running it with PHP generates the following: http://sandbox.onlinephpfunctions.com/code/a1bd9b399b3ac0c4db611fe748998f18738d19e3
This should reproduce the outcome from your link:
String strBinary = null;
try {
strBinary = new String(digest, "UTF-8");
} catch (UnsupportedEncodingException e) {
}
and you'll need these imports at the top of your file:
import java.nio.charset.Charset;
import java.io.UnsupportedEncodingException;
I hope I understood your issue correctly.
I searched around for how to hash device identifiers and stumbled on the following code.
I don't really understand what it's doing.
Why do I need to urlEncode the device id ?
Why do I need to hash the bytes, couldn't I just do that on a String ?
Why do I need to convert it to a BigInteger ?
Why do I need to shift bits to get a String with the hashed id ?
Can anyone explain what's going on line by line? I hope this will help other people understand this snippet that's getting passed around in blogs and forums, too.
String hashedId = "";
String deviceId = urlEncode(Secure.getString(context.getContentResolver(), Secure.ANDROID_ID));
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte bytes[] = digest.digest(deviceId.getBytes());
BigInteger b = new BigInteger(1, bytes);
hashedId = String.format("%0" + (bytes.length << 1) + "x", b);
} catch (NoSuchAlgorithmException e) {
//ignored
}
return hashedId;
Why do I need to urlEncode the device id ?
Why do I need to hash the bytes, couldn't I just do that on a String ?
Most hashing algorithms, including SHA-1, work on binary data as input (i.e. bytes). Strings themselves don't have a specific binary representation; it changes depending on the encoding.
The line of code they provide uses the default encoding, which is a bit fragile. I would prefer to see something like
byte bytes[] = digest.digest(deviceId.getBytes(Charset.forName("UTF-8")));
Why do I need to convert it to a BigInteger ?
This is being used for convenience to help with the conversion to a hexadecimal representation.
Why do I need to shift bits to get a String with the hashed id ?
The format String being used is %0Nx, which causes the string to be zero-padded to N characters. Since it takes two characters to represent a byte in hexadecimal, N is bytes*2, which is the result as bytes << 1.
I don't really understand why you wouldn't just include Guava for Android and use the Hashing builder:
String hash = Hashing.sha1().hashString(deviceId, Charsets.UTF_8).toString();
It's one line and doesn't throw checked exceptions.
About the bit-shifting: shifting left by one is equivalent to multiplying by 2. Each byte in the string is represented by 2 hex characters, so the resulting string will be twice as long as the number of bytes in the hash.
This will create a format string that looks something like %032x, which will print an integral value as a zero-padded 32-character string.
You need to hash the bytes, rather than the String, so that you're hashing the character data rather than the String object, which may have unpredictable internal state for a given sequence of characters.
It's converted to BigInteger so it can be consistently formatted with two hex digits per byte. (This is why the length is multiplied by two with the left shift.)
Basically, the answer to all of your questions is: so that you get reliable, repeatable results, even on different platforms.
You Can Use this Code also :
public class sha1Calculate {
public static void main(String[] args)throws Exception
{
File file = new File("D:\\Android Links.txt");
String outputTxt= "";
String hashcode = null;
try {
FileInputStream input = new FileInputStream(file);
ByteArrayOutputStream output = new ByteArrayOutputStream ();
byte [] buffer = new byte [65536];
int l;
while ((l = input.read (buffer)) > 0)
output.write (buffer, 0, l);
input.close ();
output.close ();
byte [] data = output.toByteArray ();
MessageDigest digest = MessageDigest.getInstance( "SHA-1" );
byte[] bytes = data;
digest.update(bytes, 0, bytes.length);
bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for( byte b : bytes )
{
sb.append( String.format("%02X", b) );
}
System.out.println("Digest(in hex format):: " + sb.toString());
}catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Here is the code for my class:
public class Md5tester {
private String licenseMd5 = "?jZ2$??f???%?";
public Md5tester(){
System.out.println(isLicensed());
}
public static void main(String[] args){
new Md5tester();
}
public boolean isLicensed(){
File f = new File("C:\\Some\\Random\\Path\\toHash.txt");
if (!f.exists()) {
return false;
}
try {
BufferedReader read = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
//get line from txt
String line = read.readLine();
//output what line is
System.out.println("Line read: " + line);
//get utf-8 bytes from line
byte[] lineBytes = line.getBytes("UTF-8");
//declare messagedigest for hashing
MessageDigest md = MessageDigest.getInstance("MD5");
//hash the bytes of the line read
String hashed = new String(md.digest(lineBytes), "UTF-8");
System.out.println("Hashed as string: " + hashed);
System.out.println("LicenseMd5: " + licenseMd5);
System.out.println("Hashed as bytes: " + hashed.getBytes("UTF-8"));
System.out.println("LicenseMd5 as bytes: " + licenseMd5.getBytes("UTF-8"));
if (hashed.equalsIgnoreCase(licenseMd5)){
return true;
}
else{
return false;
}
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
return false;
} catch (NoSuchAlgorithmException e) {
return false;
}
}
}
Here's the output I get:
Line read: Testing
Hashed as string: ?jZ2$??f???%?
LicenseMd5: ?jZ2$??f???%?
Hashed as bytes: [B#5fd1acd3
LicenseMd5 as bytes: [B#3ea981ca
false
I'm hoping someone can clear this up for me, because I have no clue what the issue is.
A byte[] returned by MD5 conversion is an arbitrary byte[], therefore you cannot treat it as a valid representation of String in some encoding.
In particular, ?s in ?jZ2$??f???%? correspond to bytes that cannot be represented in your output encoding. It means that content of your licenseMd5 is already damaged, therefore you cannot compare your MD5 hash with it.
If you want to represent your byte[] as String for further comparison, you need to choose a proper representation for arbitrary byte[]s. For example, you can use Base64 or hex strings.
You can convert byte[] into hex string as follows:
public static String toHex(byte[] in) {
StringBuilder out = new StringBuilder(in.length * 2);
for (byte b: in) {
out.append(String.format("%02X", (byte) b));
}
return out.toString();
}
Also note that byte[] uses default implementation of toString(). Its result (such as [B#5fd1acd3) is not related to the content of byte[], therefore it's meaningless in your case.
The ? symbols in the printed representation of hashed aren't literal question marks, they're unprintable characters.
You get this error when your java file format is not UTF-8 encoding while you encode a string using UTF-8, try remove UTF-8 and the md5 will output another result, you can copy to the string and see the result true.
Another way is set the file encoding to UTF-8, the string encode also be different