I am using this function to calculate the SHA 256
public static String getSHA1(String plainText) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
md.update(plainText.getBytes(Charset.forName("UTF-8")));
StringBuffer hexString = new StringBuffer();
byte[] bytes = md.digest();
for (int i = 0; i < bytes.length; i++) {
hexString.append(Integer.toHexString(0xFF & bytes[i]));
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
And to be sure of my results, I check this online website
http://onlinemd5.com/
the results between my code and the online is almost the same, but you know that it must be equal. for instance
my plain testis:
1234567
the website result
8BB0CF6EB9B17D0F7D22B456F121257DC1254E1F01665370476383EA776DF414
my code result
8bb0cf6eb9b17df7d22b456f121257dc1254e1f1665370476383ea776df414
and this is more examples:
7777777
8C1CDB9CB4DBAC6DBB6EBD118EC8F9523D22E4E4CB8CC9DF5F7E1E499BBA3C10
8c1cdb9cb4dbac6dbb6ebd118ec8f9523d22e4e4cb8cc9df5f7e1e499bba3c10
147258
7A2EC40FF8A1247C532309355F798A779E00ACFF579C63EEC3636FFB2902C1AC
7a2ec4ff8a1247c53239355f798a779e0acff579c63eec3636ffb292c1ac
888888
92925488B28AB12584AC8FCAA8A27A0F497B2C62940C8F4FBC8EF19EBC87C43E
92925488b28ab12584ac8fcaa8a27af497b2c6294c8f4fbc8ef19ebc87c43e
I do know that this is maybe about the encoding. but look i used utf-8 which is what the website used
This is the problem:
hexString.append(Integer.toHexString(0xFF & bytes[i]));
This will lose any leading 0s - in other words, any byte less than 16 will come out as a single hex digit instead of two.
There are plenty of fixes for this. For example:
Manually append 0 if the value is between 0 and 15 (ick)
Use String.format("%02x", bytes[i] & 0xff)
Use a full "byte array to hex conversion" method in a utility library (there are loads around)
Related
private String getString(byte[] bytes)
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++)
{
byte b = bytes[i];
sb.append(0xFF & b);
}
return sb.toString();
}
public String encrypt(String source)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(source.getBytes());
return getString(bytes);
}
catch (Exception e)
{
e.printStackTrace(); }
return null;
}
If my text = "test"
The First Part toString()) produces a value of "Encryption$2#6966b26b"
And the second part then gets that and produces a value of "91431072057033211115202222781313839180246"
But why is the md5 a number and not 31f521a06d5060d1f38159c74a1f7cf2 or something similar?
The function "encrypt()" returns a MD5 hash. You should rename it to "hash", because hashing != encrypting.
If you want to encrypt a string, you can look here: https://gist.github.com/bricef/2436364
It's clearly stated in code yuou are using MD5 hashing algorithm
Now your question is why:
But why is the md5 a number and not 31f521a06d5060d1f38159c74a1f7cf2 or something similar?
your answer is simple, look at code which generates you string from your byte array.
byte b = bytes[i];
sb.append(0xFF & b);
you take byte, ie 0x20 then you perform logical and operation with integer 0x255 and then you add decimal representation of result yo your StringBuilder.
What you want to do is more like
sb.append(Integer.toHexString(0xff&b));
I would say MD5 hash, because the code says MessageDigest.getInstance("MD5") :D
This question already has answers here:
What does & 0xff do And MD5 Structure?
(2 answers)
Closed 7 years ago.
I've found this java function that encrypt a string in MD5, but I fail to understand how it works:
public static String makeMD5(String text){
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
md.update(text.getBytes());
byte byteData[] = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++)
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
text = sb.toString();
return text;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
I don't understand the line just after the for loop..
Thanks a lot!
The line after the for loop is a frankly overcomplicated way to convert a byte array into hexadecimal. An equivalent, simpler approach might be
sb.append(String.format("%02x", b & 0xff));
though if you can use third-party libraries there are even simpler solutions. How to convert a byte array to a hex string in Java? has a number of suggestions.
(If third party libraries are available, Guava will let you do this whole method in the one line Hashing.md5().hashString(text, Charset.defaultCharset()).toString().)
I'm porting a Windows app to Android and I'm running into an issue with endianness. The app takes a series of text fields from the user and generates a password based on MD5. The problem is when I create the byte array to pass into the MD5 digest method, the bytes on the Android app are in big endian format. Thus, the MD5 output does not match between the two platforms.
I've tried using a ByteBuffer to convert to little endian and then copy that value back into the byte array using ByteBuffer.get(). Sadly, that doesn't work as it doesn't maintain the order setting.. This seems to be a known "gotcha" when dealing with ByteBuffers. If I compare the ByteBuffer.getLong() value and the equivalent in the windows version the values match but I don't know how to get the array back out of the ByteBuffer in the correct order.
Edit: I've attached both the java and C# functions below.
Below is the java version that doesn't try to fix the order/endianness:
public static final long md5(final String input) {
try {
// Create MD5
MessageDigest md5 = MessageDigest.getInstance("MD5");
// Read in string as an array of bytes.
byte[] originalBytes = input.getBytes("US-ASCII");
byte[] encodedBytes = md5.digest(originalBytes);
long output = 0;
long multiplier = 1;
// Create 64 bit integer from the MD5 hash of the input
for (int i = 0; i < encodedBytes.length; i++) {
output = output + encodedBytes[i] * multiplier;
multiplier = multiplier * 255;
}
return output;
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return 0;
}
And here is the C# version
private Int64 MD5(string input)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] originalBytes = ASCIIEncoding.ASCII.GetBytes(input);
byte[] encodedBytes = md5.ComputeHash(originalBytes);
Int64 output = 0;
Int64 Multiplyer = 1;
for (int i = 0; i < encodedBytes.Length; i++)
{
output = output + encodedBytes[i] * Multiplyer;
Multiplyer = Multiplyer * 255;
}
return output;
}
The problem is that this line of Java:
output = output + encodedBytes[i] * multiplier;
is subtly different from this line of C# code:
output = output + encodedBytes[i] * Multiplyer;
Specifically, the implicit conversion of encodedBytes[i] from byte to long (Java) or Int64 (C#) is a bit different.
You see, in Java, a byte is a signed value between -128 and 127, whereas in C#, it's an unsigned value between 0 and 255. So, for example, if encodedBytes[i] is B2 (1011 0010), then Java interprets that as -78, while C# interprets that as 178.
To emulate the C# interpretation in Java, you can write something like this:
output = output + ((encodedBytes[i] + 256) % 256) * multiplier;
(Fortunately, Java has the same handling for integer overflow as C#'s "unchecked" mode, which is apparently what you're using; that would have been much trickier to emulate, if you had to.)
The MD5 standard calls for 128-bit values, not 64-bit. So first of all, the signature private Int64 MD5(string input) makes no sense. You should not be converting these to integers and trying to compare them. Just pass around the byte[] references and compare those.
I'm trying to make a simple String to SHA1 converter in Java and this is what I've got...
public static String toSHA1(byte[] convertme) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
return new String(md.digest(convertme));
}
When I pass it toSHA1("password".getBytes()), I get [�a�ɹ??�%l�3~��. I know it's probably a simple encoding fix like UTF-8, but could someone tell me what I should do to get what I want which is 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8? Or am I doing this completely wrong?
UPDATE
You can use Apache Commons Codec (version 1.7+) to do this job for you.
DigestUtils.sha1Hex(stringToConvertToSHexRepresentation)
Thanks to #Jon Onstott for this suggestion.
Old Answer
Convert your Byte Array to Hex String. Real's How To tells you how.
return byteArrayToHexString(md.digest(convertme))
and (copied from Real's How To)
public static String byteArrayToHexString(byte[] b) {
String result = "";
for (int i=0; i < b.length; i++) {
result +=
Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return result;
}
BTW, you may get more compact representation using Base64. Apache Commons Codec API 1.4, has this nice utility to take away all the pain. refer here
This is my solution of converting string to sha1. It works well in my Android app:
private static String encryptPassword(String password)
{
String sha1 = "";
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
sha1 = byteToHex(crypt.digest());
}
catch(NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch(UnsupportedEncodingException e)
{
e.printStackTrace();
}
return sha1;
}
private static String byteToHex(final byte[] hash)
{
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
Using Guava Hashing class:
Hashing.sha1().hashString( "password", Charsets.UTF_8 ).toString()
SHA-1 (and all other hashing algorithms) return binary data. That means that (in Java) they produce a byte[]. That byte array does not represent any specific characters, which means you can't simply turn it into a String like you did.
If you need a String, then you have to format that byte[] in a way that can be represented as a String (otherwise, just keep the byte[] around).
Two common ways of representing arbitrary byte[] as printable characters are BASE64 or simple hex-Strings (i.e. representing each byte by two hexadecimal digits). It looks like you're trying to produce a hex-String.
There's also another pitfall: if you want to get the SHA-1 of a Java String, then you need to convert that String to a byte[] first (as the input of SHA-1 is a byte[] as well). If you simply use myString.getBytes() as you showed, then it will use the platform default encoding and as such will be dependent on the environment you run it in (for example it could return different data based on the language/locale setting of your OS).
A better solution is to specify the encoding to use for the String-to-byte[] conversion like this: myString.getBytes("UTF-8"). Choosing UTF-8 (or another encoding that can represent every Unicode character) is the safest choice here.
This is a simple solution that can be used when converting a string to a hex format:
private static String encryptPassword(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(password.getBytes("UTF-8"));
return new BigInteger(1, crypt.digest()).toString(16);
}
Just use the apache commons codec library. They have a utility class called DigestUtils
No need to get into details.
As mentioned before use apache commons codec. It's recommended by Spring guys as well (see DigestUtils in Spring doc). E.g.:
DigestUtils.sha1Hex(b);
Definitely wouldn't use the top rated answer here.
It is not printing correctly because you need to use Base64 encoding. With Java 8 you can encode using Base64 encoder class.
public static String toSHA1(byte[] convertme) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
return Base64.getEncoder().encodeToString(md.digest(convertme));
}
Result
This will give you your expected output of 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
Message Digest (hash) is byte[] in byte[] out
A message digest is defined as a function that takes a raw byte array and returns a raw byte array (aka byte[]). For example SHA-1 (Secure Hash Algorithm 1) has a digest size of 160 bit or 20 byte. Raw byte arrays cannot usually be interpreted as character encodings like UTF-8, because not every byte in every order is an legal that encoding. So converting them to a String with:
new String(md.digest(subject), StandardCharsets.UTF_8)
might create some illegal sequences or has code-pointers to undefined Unicode mappings:
[�a�ɹ??�%l�3~��.
Binary-to-text Encoding
For that binary-to-text encoding is used. With hashes, the one that is used most is the HEX encoding or Base16. Basically a byte can have the value from 0 to 255 (or -128 to 127 signed) which is equivalent to the HEX representation of 0x00-0xFF. Therefore hex will double the required length of the output, that means a 20 byte output will create a 40 character long hex string, e.g.:
2fd4e1c67a2d28fced849ee1bb76e7391b93eb12
Note that it is not required to use hex encoding. You could also use something like base64. Hex is often preferred because it is easier readable by humans and has a defined output length without the need for padding.
You can convert a byte array to hex with JDK functionality alone:
new BigInteger(1, token).toString(16)
Note however that BigInteger will interpret given byte array as number and not as a byte string. That means leading zeros will not be outputted and the resulting string may be shorter than 40 chars.
Using Libraries to Encode to HEX
You could now copy and paste an untested byte-to-hex method from Stack Overflow or use massive dependencies like Guava.
To have a go-to solution for most byte related issues I implemented a utility to handle these cases: bytes-java (Github)
To convert your message digest byte array you could just do
String hex = Bytes.wrap(md.digest(subject)).encodeHex();
or you could just use the built-in hash feature
String hex = Bytes.from(subject).hashSha1().encodeHex();
Base 64 Representation of SHA1 Hash:
String hashedVal = Base64.getEncoder().encodeToString(DigestUtils.sha1(stringValue.getBytes(Charset.forName("UTF-8"))));
Convert byte array to hex string.
public static String toSHA1(byte[] convertme) {
final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] buf = md.digest(convertme);
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i) {
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
The reason this doesn't work is that when you call String(md.digest(convertme)), you are telling Java to interpret a sequence of encrypted bytes as a String. What you want is to convert the bytes into hexadecimal characters.
Maybe this helps (works on java 17):
import org.apache.tomcat.util.codec.binary.Base64;
return new String(Base64.encodeBase64(md.digest(convertme)));
I know that there is a lot of similar topics, but still... can someone provide me a working example of method which generates MD5 String.
I'm currently using MessageDigest, and I'm doing the following to get a string
sun.misc.BASE64Encoder().encode(messageDigest.digest())
I guess there is some better way to do that.
Thanks in advance!
I'd use commons-codec
Base64 - Base64.encodeBase64(digestBytes)
Hex-string - Hex.encodeHex(digestBytes)
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] arr = md.digest(bytesOfMessage);
return Base64.getEncoder().encodeToString(arr);
note: md5 is not considered as good hash algorithm anymore, consider choosing SHAs
// Convert to hex string
StringBuffer sb = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
if ((0xff & messageDigest[i]) < 0x10) {
sb.append('0');
}
sb.append(Integer.toHexString(0xff & messageDigest[i]));
}
String md5 = sb.toString();
This assumes you actually want your MD5 printed as an hex string, not BASE64-encoded. That's the way it is normally represented.
I've seen next solution:
byte[] digest = md.digest(someDataByteArray);
StringBuilder hex = new StringBuilder();
for (byte b : digest) {
hex.append(String.format("%02x", b));
}
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
...
String input = "westerngun";
MessageDigest digest = MessageDigest.getInstance("MD5"); // not thread-safe, create instance for each thread
byte[] result = digest.digest(input.getBytes()); // get MD5 hash array, could contain negative
String hex = DatatypeConverter.printHexBinary(result).toLowerCase(); // convert byte array to hex string
If you want a number:
Integer number = Integer.parseInt(hex, 16); // parse hex number to integer. If overflowed, use Long.parseLong()