I want to convert a String to a SHA-256 Hash. I am using this code:
String text = "YOLO";
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes("UTF-8"));
System.out.println(hash.toString());
The problem is, when I start the program, it prints
[B#28d93b30
Why is this, and how can solve this?
Thanks in advance,
Fihdi
As others have mentioned, you're using the default toString() method which simply outputs the class name and hashcode
If you want a hex print out of the contents of the byte array try...
Hex.encodeHexString(byte[] data) from Apache Commons.
Also How to convert a byte array to a hex string in Java? has some examples for doing this without a library.
To print the bytes as hex (instead of that result, explained in How do I print my Java object without getting "SomeType#2f92e0f4"?), simply run:
System.out.println((new HexBinaryAdapter()).marshal(hash));
In JAVA, arrays do not override Object.toString(). Therefore, hash.toString() does not return a representation of the contents of the array, but rather a representation of the array itself. Apparently, this representation of an array is not very useful. The default toString() implementation returns
getClass().getName() + '#' + Integer.toHexString(hashCode())
I have also faced this type of issue and then solve in this way.
String text = "YOLO";
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String encoded = DatatypeConverter.printHexBinary(hash);
System.out.println(encoded.toLowerCase());
Related
I have a function for hashing passwords, that returns a byte[] with entries using the full range of the byte datatype from -128 to 127. I have tried to convert the byte[] to a String using new String(byte_array, StandardCharsets.UTF_8);. This does return a String - however it can not properly encode negative numbers - hence it encodes them to a "�" character. When comparing two of those characters using: new String(new byte[]{-1}, StandardCharsets.UTF_8).equals(new String(new byte[]{-2}, StandardCharsets.UTF_8)) it turns out the String representation for all negative numbers is equal as the expression above returns true. While this doesn't fully ruin my hashing functionality as the hash of the same expression will still always yield the same result, this is obviously not what I want as it increases the chance of two different inputs yielding the same output drastically.
Is there some easy fix for this or any alternative idea how to convert the byte[] to a String? For context I want to use the String to later write it to a file to store it in a file and later read it again to compare it to other hashes.
Edit: After a bit of trying around with the tips from the comments my solution is to convert the byte[] to a char[] and add 128 to every value. The char array can then easily be converted to a String or be written to a file directly (byteHash is the byte[]):
char[] charHash = new char[byteHash.length];
for(int i = 0; i < byteHash.length; i++){
charHash[i] = (char) (byteHash[i]+128);
}
return new String(charHash);
I do not really like the solution but it works.
The appropriate solution to this is to use an encoding like hexadecimal (https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/HexFormat.html) or Base64 (https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html) to convert an arbitrary byte sequence to a string reversibly.
I have an enum
public enum Test {
VALUE, NAME;
}
I convert it into a byte array
byte[] array = Test.VALUE.toString().getBytes(Charsets.UTF_8)
how can i convert that back into an enum?
Test.valueOf(array.toString()) does not work.
The reason why array.toString didn't work is that toString returns a description of the array, not the string constructed using the bytes in the array with UTF-8 encoding. toString just returns something like [B#60e53b93 which means practically nothing to humans.
To convert a byte array to a string, use the string's constructor, the one that takes a byte array and a Charset. Here's the whole code:
// converting to byte array
Test t = Test.VALUE;
byte[] bytes = t.toString().getBytes(StandardCharsets.UTF_8);
// converting back to Test
String str = new String(bytes, StandardCharsets.UTF_8);
Test newT = Test.valueOf(str);
If you think about it logically, toString can't possibly give you what you expect. This is because to convert a byte array to a string, you need to specify an encoding! You obvious did not pass a Charset object when you call toString, so how on earth is the computer going to know what charset you want?
You have to convert the array back to a proper String first using it constructor. array.toString() does not do what you think and will only return gibberish.
byte[] array = Test.VALUE.toString().getBytes(Charsets.UTF_8);
String valueString = new String(array, Charsets.UTF_8);
Test value = Test.valueOf(valueString);
This is simply to error check my code, but I would like to convert a single byte out of a byte array to a string. Does anyone know how to do this? This is what I have so far:
recBuf = read( 5 );
Log.i( TAG, (String)recBuf[0] );
But of course this doesn't work.
I have googled around a bit but have only found ways to convert an entire byte[] array to a string...
new String( recBuf );
I know I could just do that, and then sift through the string, but it would make my task easier if I knew how to operate this way.
You can make a new byte array with a single byte:
new String(new byte[] { recBuf[0] })
Use toString method of Byte
String s=Byte.toString(recBuf[0] );
Try above , it works.
Example:
byte b=14;
String s=Byte.toString(b );
System.out.println("String value="+ s);
Output:
String value=14
There's a String constructor of the form String(byte[] bytes, int offset, int length). You can always use that for your conversion.
So, for example:
byte[] bite = new byte[]{65,67,68};
for(int index = 0; index < bite.length; index++)
System.out.println(new String(bite, index,1));
What about converting it to char? or simply
new String(buffer[0])
public static String toString (byte value)
Since: API Level 1
Returns a string containing a concise, human-readable description of the specified byte value.
Parameters
value the byte to convert to a string.
Returns
a printable representation of value.]1
this is how you can convert single byte to string try code as per your requirement
Edit:
Hows about
""+ recBuf[0];//Hacky... not sure if would work
((Byte)recBuf[0]).toString();
Pretty sure that would work.
Another alternate could be converting byte to char and finally string
Log.i(TAG, Character.toString((char) recBuf[0]));
Or
Log.i(TAG, String.valueOf((char) recBuf[0]));
You're assuming that you're using 8bit character encoding (like ASCII) and this would be wrong for many others.
But with your assumption you might just as well using simple cast to character like
char yourChar = (char) yourByte;
or if really need String:
String string = String.valueOf((char)yourByte);
I am creating an encryption algorithm and is to XOR two strings. While I know how to XOR the two strings the problem is the length. I have two byte arrays one for the plain text which is of a variable size and then the key which is of 56 bytes lets say. What I want to know is what is the correct method of XORing the two strings. Concatenate them into one String in Binary and XOR the two values? Have each byte array position XOR a concatenated Binary value of the key and such. Any help is greatly appreciated.
Regards,
Milinda
To encode just move through the array of bytes from the plain text, repeating the key as necessary with the mod % operator. Be sure to use the same character set at both ends.
Conceptually we're repeating the key like this, ignoring encoding.
hello world, there are sheep
secretsecretsecretsecretsecr
Encrypt
String plainText = "hello world, there are sheep";
Charset charSet = Charset.forName("UTF-8");
byte[] plainBytes = plainText.getBytes(charSet);
String key = "secret";
byte[] keyBytes = key.getBytes(charSet);
byte[] cipherBytes = new byte[plainBytes.length];
for (int i = 0; i < plainBytes.length; i++) {
cipherBytes[i] = (byte) (plainBytes[i] ^ keyBytes[i
% keyBytes.length]);
}
String cipherText = new String(cipherBytes, charSet);
System.out.println(cipherText);
To decrypt just reverse the process.
// decode
for (int i = 0; i < cipherBytes.length; i++) {
plainBytes[i] = (byte) (cipherBytes[i] ^ keyBytes[i
% keyBytes.length]);
}
plainText = new String(plainBytes, charSet); // <= make sure same charset both ends
System.out.println(plainText);
(As noted in comments, you shouldn't use this for anything real. Proper cryptography is incredibly hard to do properly from scratch - don't do it yourself, use existing implementations.)
There's no such concept as "XOR" when it comes to strings, really. XOR specifies the result given two bits, and text isn't made up of bits - it's made up of characters.
Now you could just take the Unicode representation of each character (an integer) and XOR those integers together - but the result may well be a sequence of integers which is not a valid Unicode representation of any valid string.
It's not clear that you're even thinking in the right way to start with - you talk about having strings, but also having 56 bytes. You may have an encoded representation of a string (e.g. the result of converting a string to UTF-8) but that's not the same thing.
If you've got two byte arrays, you can easily XOR those together - and perhaps cycle back to the start of one of them if it's shorter than the other, so that the result is always the same length as the longer array. However, even if both inputs are (say) UTF-8 encoded text, the result often won't be valid UTF-8 encoded text. If you must have the result in text form, I'd suggest using Base64 at that point - there's a public domain base64 encoder which has a simple API.
PHP code:
echo hash('sha256', 'jake');
PHP output:
cdf30c6b345276278bedc7bcedd9d5582f5b8e0c1dd858f46ef4ea231f92731d
Java code:
String s = "jake";
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(s.getBytes(Charset.forName("UTF-8")));
byte[] hashed = md.digest();
String s2 = "";
for (byte b : hashed) {
s2 += b;
}
System.out.println(s2);
Java output:
-51-1312107528211839-117-19-57-68-19-39-43884791-1141229-4088-12110-12-223531-11011529
I had expected the two to return the same result. Obviously, this is not the case. How can I get the two to match up or is it impossible?
EDIT: I had made a mistake, think I have the answer to the question now anyway.
Well, the very first thing you need to do is use a consistent string encoding. I've no idea what PHP will do, but "jake".getBytes() will use whatever your platform default encoding is for Java. That's a really bad idea. Using UTF-8 would probably be a good start, assuming that PHP copes with Unicode strings to start with. (If it doesn't, you'll need to work out what it is doing and try to make the two consistent.) In Java, use the overload of String.getBytes() which takes a Charset or the one which takes the name of a Charset. (Personally I like to use Guava's Charsets.UTF_8.)
Then persuade PHP to use UTF-8 as well.
Then output the Java result in hex. I very much doubt that the code you've given is the actual code you're running, as otherwise I'd expect output such as "[B#e48e1b". Whatever you're doing to convert the byte array into a string, change it to use hex.
They are printing the same .. convert your byte[] to a hex string, then you'll see CDF30C6B345276278BEDC7BCEDD9D5582F5B8E0C1DD858F46EF4EA231F92731D as Java output, too:
public void testSomething() throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update("jake".getBytes());
System.out.println(getHex(md.digest()));
}
static final String HEXES = "0123456789ABCDEF";
public static String getHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
final StringBuilder hex = new StringBuilder( 2 * raw.length );
for ( final byte b : raw ) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
You need to convert the digest to a HEX string before printing it out. Example code can be found here.