I try to convert BigInteger to a string using Base64 and check the reversibility :
This case works fine :
Check A1 : "999999999" => "O5rJ/w=="
Check B1 : "O5rJ/w==" => "999999999"
but this case doesn't :
Check B2 : "//////" => "4294967295"
Check A2 : "4294967295" => "/////w" instead of "//////"
I'm using Java 8 and org.apache.commons.codec.binary.Base64
#Test
public void testDecimalToBase64()
{
final String encodedMaxDec = new String(Base64.encodeInteger(new BigInteger("999999999")), StandardCharsets.UTF_8);
Assert.assertEquals("Check A1", "O5rJ/w==", encodedMaxDec);
final String encodedMaxB64 = new String(Base64.encodeInteger(new BigInteger("4294967295")), StandardCharsets.UTF_8);
Assert.assertEquals("Check A2", "//////", encodedMaxB64);
}
#Test
public void testBase64ToDecimal()
{
final BigInteger decodedMaxDec = Base64.decodeInteger(new String("O5rJ/w==").getBytes(StandardCharsets.UTF_8));
Assert.assertEquals("Check B1", "999999999", decodedMaxDec.toString());
final BigInteger decodedMaxB64 = Base64.decodeInteger(new String("//////").getBytes(StandardCharsets.UTF_8));
Assert.assertEquals("Check B2", "4294967295", decodedMaxB64.toString());
}
If anyone has an idea, thank you for helping me ;-)
The base64 encoding of a BitInteger's bytes when representing 4294967295 isn't //////.
I'm assuming that because every integer in Java is signed the BigInteger sticks an additional byte on the front to indicate whether the number is positive or negative.
Therefore this code...
byte[] bigIntegerBytes = BigInteger.valueOf(4294967295l).toByteArray();
.. returns a 5 byte array and you have the following bytes:
000000000 11111111 11111111 11111111 1111111
If you base64 encode that you get AP////8=
If what you want to do is base64 encode a BigInteger representing 4294967295 and then decode it back again, this should do it.
Base64.Encoder encoder = Base64.getEncoder();
Base64.Decoder decoder = Base64.getDecoder();
byte[] bigIntegerBytes = BigInteger.valueOf(4294967295l).toByteArray();
String base64EncodedBigIntegerBytes = encoder.encodeToString(bigIntegerBytes);
System.out.println(base64EncodedBigIntegerBytes);
byte[] decodedBigIntegerBytes = decoder.decode(base64EncodedBigIntegerBytes);
BigInteger decodedBigInteger = new BigInteger(decodedBigIntegerBytes);
System.out.println(decodedBigInteger);
Related
I have come across a legacy piece of code encoding byte array to hex string which is in production and have never caused an issue.
This piece of code is used as:
We encrypt a user password. The encryptor returns a byte[].
We convert the byte[] to Hex String using this encoder code and then use that String representation in our properties file and so on.
However, yesterday we have hit a password, whose encrypted byte[] version is getting encoded incorrectly.
import java.math.BigInteger;
import java.util.HashMap;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
public class ByteArrayToHexEncoder {
public static void main(String[] args) throws DecoderException {
String hexString = "a0d21588c0a2c2fc68dc859197fc78cd"; // correct hex representation
// equivalent byte array: this is the byte array returned by the encryptor
byte[] byteArray = Hex.decodeHex(hexString.toCharArray());
// legacy encoder
System.out.println("Legacy code encodes as: " + encodeHexBytesWithPadding(byteArray));
// commons-codec encoder
System.out.println("Commons codec encode as: " + new String(Hex.encodeHex(byteArray)));
}
private static final String PADDING_ZEROS =
"0000000000000000000000000000000000000000000000000000000000000";
private static final HashMap<Integer, Character> MAP_OF_HEX = new HashMap<>();
static {
MAP_OF_HEX.put(0, '0');
MAP_OF_HEX.put(1, '1');
MAP_OF_HEX.put(2, '2');
MAP_OF_HEX.put(3, '3');
MAP_OF_HEX.put(4, '4');
MAP_OF_HEX.put(5, '5');
MAP_OF_HEX.put(6, '6');
MAP_OF_HEX.put(7, '7');
MAP_OF_HEX.put(8, '8');
MAP_OF_HEX.put(9, '9');
MAP_OF_HEX.put(10, 'a');
MAP_OF_HEX.put(11, 'b');
MAP_OF_HEX.put(12, 'c');
MAP_OF_HEX.put(13, 'd');
MAP_OF_HEX.put(14, 'e');
MAP_OF_HEX.put(15, 'f');
}
public static String encodeHexBytesWithPadding(byte[] inputByteArray) {
String encodedValue = encodeHexBytes(inputByteArray);
int expectedSize = inputByteArray.length * 2;
if (encodedValue.length() < expectedSize) {
int zerosToPad = expectedSize - encodedValue.length();
encodedValue = PADDING_ZEROS.substring(0, zerosToPad) + encodedValue;
}
return encodedValue;
}
public static String encodeHexBytes(byte[] inputByteArray) {
String encodedValue;
if (inputByteArray[0] < 0) {
// Something is wrong here! Don't know what!
byte oldValue = inputByteArray[0];
inputByteArray[0] = (byte) (oldValue & 0x0F);
int nibble = (oldValue >> 4) & 0x0F;
encodedValue = new BigInteger(inputByteArray).toString(16);
inputByteArray[0] = oldValue;
encodedValue = MAP_OF_HEX.get(nibble) + encodedValue;
} else {
encodedValue = new BigInteger(inputByteArray).toString(16);
}
return encodedValue;
}
}
The legacy code outputs the encoded value as: 0ad21588c0a2c2fc68dc859197fc78cd while the correct expected value should be: a0d21588c0a2c2fc68dc859197fc78cd.
I am trying to understand what's wrong with the encoder and need some help understanding.
BigInteger(byte[]) constructor is there to handle two's complement representation of a number where the most significant bit also denotes the sign. The Hex common-codec simply translates each byte into a hex representation, there is no special meaning to the most significant bit.
Your legacy code in the if (inputByteArray[0] < 0) branch attempts to modify the first byte in the byte[] input probably to work around the representation of negative numbers in the two-complement's form e.g. -1 being represented as ff. Unfortunately this is implemented incorrectly in your legacy code:
String input = "a000000001";
byte[] bytes = Hex.decodeHex(input.toCharArray());
System.out.println(encodeHexBytesWithPadding(bytes));
System.out.println(Hex.encodeHexString(bytes));
will print
00000000a1
a000000001
showing that the legacy code values are completely wrong.
There is not much to salvage here IMO, instead use Hex.encodeHexString() instead or check other options discussed in this question.
What is the formula to convert the value of a textfield from hex to little endian?
Example input: 5A109061
Example output: 1636831322
Get the value from the EditText as a String.
Parse the string value as hex, using Integer.parseInt(...) and radix 16.
Flip the byte order of the int, either using ByteBuffer (simpler) or using bit shifts (faster).
For example:
String hex = "5A109061"; // mEditText.getText().toString()
// Parse hex to int
int value = Integer.parseInt(hex, 16);
// Flip byte order using ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.asIntBuffer().put(value);
buffer.order(ByteOrder.LITTLE_ENDIAN);
int flipped = buffer.asIntBuffer().get();
System.out.println("hex: 0x" + hex);
System.out.println("flipped: " + flipped);
Output:
hex: 0x5A109061
flipped: 1636831322
Use ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(8)order(ByteOrder.LITTLE_ENDIAN).putLong(5A109061)
byte[] result = byteBuffer.array();
You can also use this extension for kotlin.
Example :
val str = "6a3b7043"
val hex2Float = str.hex2Float
fun String.hex2Float(): Float{
val i = toLong(16)
val data = java.lang.Float.intBitsToFloat(i.toInt()) // Big endian
val buffer = ByteBuffer.allocate(4)
buffer.asFloatBuffer().put(data)
buffer.order(ByteOrder.LITTLE_ENDIAN)
val lData = buffer.asFloatBuffer().get() // Little endian
return lData
}
There are some SO quetions but no helped me. I would like to convert byte[] from org.apache.commons.codec.digest.HmacUtils to String. This code produces some weird output:
final String value = "value";
final String key = "key";
byte[] bytes = HmacUtils.hmacSha1(key, value);
String s = new String(bytes);
What am I doing wrong?
Try to use:
String st = HmacUtils.hmacSha1Hex(key, value);
First, the result of hmacSha1 would produce a digest, not not a clear String. Besides, you may have to specify an encoding format, for example
String s = new String(bytes, "US-ASCII");
or
String s = new String(bytes, "UTF-8");
For a more general solution, if you don't have HmacUtils available:
// Prepare a buffer for the string
StringBuilder builder = new StringBuilder(bytes.length*2);
// Iterate through all bytes in the array
for(byte b : bytes) {
// Convert them into a hex string
builder.append(String.format("%02x",b));
// builder.append(String.format("%02x",b).toUpperCase()); // for upper case characters
}
// Done
String s = builder.toString();
To explain your problem:
You are using a hash function. So a hash is usually an array of bytes which should look quite random.
If you use new String(bytes) you try to create a string from these bytes. But Java will try to convert the bytes to characters.
For example: The byte 65 (hex 0x41) becomes the letter 'A'. 66 (hex 0x42) the letter 'B' and so on. Some numbers can't be converted into readable characters. Thats why you see strange characters like '�'.
So new String(new byte[]{0x41, 0x42, 0x43}) will become 'ABC'.
You want something else: You want each byte converted into a 2 digit hex String (and append these strings).
Greetings!
You may need to have an encoding format. Check out this link here.
UTF-8 byte[] to String
A trading partner has asked me to send an HMAC SHA1 hash as lowercase heaxits. The only reference I can find to them is in relation to PHP. I can do the hashing in .NET and Java but how do I output "lowercase hexits" with them? Lowercase hexits doesn't appear to be equivalent to Base64.
Ah! I love simplicity. Here's the solution.
Public Shared Function Encrypt(ByVal plainText As String, ByVal preSharedKey As String) As String
Dim preSharedKeyBytes() As Byte = Encoding.UTF8.GetBytes(preSharedKey)
Dim plainTextBytes As Byte() = Encoding.UTF8.GetBytes(plainText)
Dim hmac = New HMACSHA1(preSharedKeyBytes)
Dim cipherTextBytes As Byte() = hmac.ComputeHash(plainTextBytes)
Dim strTemp As New StringBuilder(cipherTextBytes.Length * 2)
For Each b As Byte In cipherTextBytes
strTemp.Append(Conversion.Hex(b).PadLeft(2, "0"c).ToLower)
Next
Dim cipherText As String = strTemp.ToString
Return cipherText
End Function
This is compatible with the PHP hash_hmac function with FALSE in the raw_output parameter.
For lowercase hex digits (hexits) use:
public static String toHex(byte[] bytes) {
BigInteger bi = new BigInteger(1, bytes);
return String.format("%0" + (bytes.length << 1) + "x", bi);
}
From related question:
In Java, how do I convert a byte array to a string of hex digits while keeping leading zeros?
Here's a C# translation of sedge's solution:
private static String toHex(byte[] cipherTextBytes)
{
var strTemp = new StringBuilder(cipherTextBytes.Length * 2);
foreach(Byte b in cipherTextBytes)
{
strTemp.Append(Microsoft.VisualBasic.Conversion.Hex(b).PadLeft(2, '0').ToLower());
}
String cipherText = strTemp.ToString();
return cipherText;
}
I need to convert a salt value randomly generated and store it in the database. To store it in the database I converted it to a string. Then for retrieving the original value, I convert it back to byte. But both value are not matching. I have tried "UTF-8","UTF-16", BASE64Encoder.
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16]; // 16 bytes = 128 bits
ranGen.nextBytes(aesKey);
System.out.println(aesKey);
String a=new String(aesKey,"UTF-16");
byte[] b=new byte[16];
b=a.getBytes("UTF-16");
System.out.println(b);
Outputs for the above code(Executed it 2 times):
[B#11563ff
[B#1581593
and
[B#170888e
[B#11563ff
You really ought to use Base64 for converting binary data to Strings. There are lots of free implementations available, for example the one found in Apache Commons Codec.
Also, it's really easy to use, for example:
For encoding:
import org.apache.commons.codec.binary.Base64;
...
byte[] abValue = {...}; // Your data to encode
Base64 base64 = new Base64();
String strEncodedData = base64.encodeToString(abValue).trim();
For decoding:
import org.apache.commons.codec.binary.Base64;
...
String strEncodedData = "..."; // Your previously encoded data
Base64 base64 = new Base64();
byte[] abValue = base64.decode(strValue);
As your code is written above, printing aesKey and then b, what you are actually printing is the output of the toString method for an array object, which is just the default Object toString method. So I don't see how you can expect them to be the same.
If you really want to check they are the same you should compare them byte by byte.
In terms of your actual question regarding storing a byte[] as a String in the DB, your best bet is to Base64 encode it. I would suggest using the Apache Commons Codec library for this. See the user guide.
EDIT:
Using the BASE64Encode and BASE64Decoder you have referred to, the code would be like this:
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16]; // 16 bytes = 128 bits
ranGen.nextBytes(aesKey);
String a = new BASE64Encoder().encode(aesKey);
System.out.println(a);
byte[] b = new BASE64Decoder().decodeBuffer(a);
System.out.println(new BASE64Encoder().encode(b));
for (int i = 0; i < aesKey.length; i++) {
System.out.println(aesKey[i] + " " + b[i]);
}
Here, I have also looped through the bytes individually, to show that they are indeed equal.