Java Base64 encode gives different results than C base64 encode - java

I try to encode this byte array:
[237, 217, 204, 218, 109, 227, 157, 145, 35, 152, 85, 142, 182, 180, 120, 8]
Using Java library org.apache.commons.codec.binary.Base64.encodeBase64 and org.bouncycastle.util.encoders.Base64.encode this is the results:
[55, 100, 110, 77, 50, 109, 51, 106, 110, 90, 69, 106, 109, 70, 87, 79, 116, 114, 82, 52, 67, 65, 61, 61]
(note the double '=' padding character at the end)
Using base64.c Copyright (c) 1995-2001 Kungliga Tekniska Högskolan (Royal Institute of Technology, Stockholm, Sweden) this is the output:
[55, 100, 110, 77, 50, 109, 51, 106, 110, 90, 69, 106, 109, 70, 87, 79, 116, 114, 82, 52, 67, 66, 72, 114]
Can anyone explain why? How can I make the Java/C library works the same way?

Every Base64 ASCII character holds 6 bits information (26 = 64), so 4 Base64 characters hold 3 bytes information. You have 16 bytes, so one byte remains at the end, needing 2 Base64 characters, and to make the group up to 4 chars, two padding =s are added.
Mind: with JavaSE 8 came a class Base64 to replace several older class around.
Base64 has several fields of application, with various little changes: padding can be left out, line breaks added to limit line length, and so on. Java 8's Base64 even has an option for a non-compatible URL and file name safeversion, where + and / are replaced.

Base64 works on blocks of 3 bytes, and the = padding is there to bring the output size up to a multiple of 3. This padding is optional, and if is not there then you can simply add it manually by checking the array length before trying to decode using the Java code.

Related

Phoenix framework: How to decode Phoenix Session Cookie with Java

I am trying two different ways to decode Phoenix Session Cookie.
First one is Elixir's interaction shell, and the second one is with Java.
Please see the following examples;
IEx
iex(1)> set_cookie = "SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP.l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"
"SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP.l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"
iex(2)> [_, payload, _] = String.split(set_cookie, ".", parts: 3)
["SFMyNTY",
"g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYZFRuNUtQMkJ5YWtKT1JnWUtCeXhmNmdP",
"l0T3G-i8I5dMwz7lEZnQAeK_WeqEZTxcDeyNY2poz_M"]
iex(3)> {:ok, encoded_term } = Base.url_decode64(payload, padding: false)
{:ok,
<<131, 116, 0, 0, 0, 1, 109, 0, 0, 0, 11, 95, 99, 115, 114, 102, 95, 116, 111,
107, 101, 110, 109, 0, 0, 0, 24, 100, 84, 110, 53, 75, 80, 50, 66, 121, 97,
107, 74, 79, 82, 103, 89, 75, 66, 121, 120, 102, ...>>}
iex(4)> :erlang.binary_to_term(encoded_term)
%{"_csrf_token" => "dTn5KP2ByakJORgYKByxf6gO"}
Java
public static String decodePhoenixSessionCookie(String sessionCookie) {
String payload = sessionCookie.split("\\.")[1];
byte[] encoded_term = Base64.getUrlDecoder().decode(payload.getBytes());
return new String(encoded_term);
}
Java Output
�tm_csrf_tokenmdTn5KP2ByakJORgYKByxf6gO
What I wonder is; with the Java way, I can fully achieve field name and it's value, but some gibberish values come with them.
Do you know what's the reason for this?
Do I have a chance to get clean output like Elixir way in Java way?

Cannot use unicode filename as argument to Java program on Windows cmd, but wildcard works

I tried to pass filename with unicode characters to my java program in Windows cmd, but the filename I got in program was broken. Those unicode characters were presented as ?, and it threw IOException when I read those file.
However, if I use wildcard like *.txt, it works correctly.
For example, I have a file called [テスト]测试文件1.txt in my directory.
And I wrote a simple java program to show arguments it received, which also prints string bytes.
import java.util.Arrays;
public class FileArg {
public static void main(String[] args) {
for (String fn: args) {
System.out.println(fn);
System.out.println(Arrays.toString(fn.getBytes()));
}
}
}
My Java version is 15, and I have chcp to 65001, also running program with -Dfile.encoding=UTF-8 flag.
Then I run java -Dfile.encoding=UTF-8 FileArg "[テスト]测试文件1.txt" [テスト]测试文件1.txt *.txt.
The filename is broken if I passed them directly, but it works perfectly with wildcard.
The output:
[???]??文件1.txt
[91, 63, 63, 63, 93, 63, 63, -26, -106, -121, -28, -69, -74, 49, 46, 116, 120, 116]
[???]??文件1.txt
[91, 63, 63, 63, 93, 63, 63, -26, -106, -121, -28, -69, -74, 49, 46, 116, 120, 116]
[テスト]测试文件1.txt
[91, -29, -125, -122, -29, -126, -71, -29, -125, -120, 93, -26, -75, -117, -24, -81, -107, -26, -106, -121, -28, -69, -74, 49, 46, 116, 120, 116]
Result:
BTW, my default code page is cp950 (BIG5).
How could I get it work?

How to get key & value from Kafka RecordHeaders

I have a ProducerRecord object.
ProducerRecord<String, byte[]> hdr = addHeader.addMDGHeader(record);
I'm trying to write a test that checks a particular header key exists.
If I print hdr.headers().toString() I get the following RecordHeaders(headers = [RecordHeader(key = mdpHeader, value = [123, 34, 83, 101, 113, 117, 101, 110, 99, 101, 78, 111, 34, 58, 48, 44, 34, 84, 101, 109, 112, 108, 97, 116, 101, 115, 34, 58, 91, 93, 125])], isReadOnly = false).
How do I pull out mdpHeader?
The Header.value() method returns byte array byte[], and then you can convert it into string, you can see more examples here
String value = new String(header.value(), StandardCharsets.UTF_8);

Why Java do not respect given array length?

I saw problem in this piece of code:
byte[] buf = new byte[6];
buf = "abcdef".getBytes();
System.out.println(buf.length);
Array was made for 6 bytes. If I get bytes from string with length 6 I will get much more bytes. So how will all these bytes get into this array? But this is working. Moreover, buf.length shows length of that array as it is array of chars not those bytes.
Afterwards, I realized that in
byte[] buf = new byte[6];
6 does not mean much, i.e. I can put there 0 or 1 or 2 or so on and code will work (with buf.length showing length of given string not array - what I see as second problem or discrepancy).
This question is different than Why does Java's String.getBytes() uses “ISO-8859-1” because it have one aspect more, at least: variables assignment oversight (getBytes() returns new array), i.e. it don't fully address my question.
That is not how variable assignments work
Thinking that assigning a 6 byte array to a variable will limit the length of any other arrays assigned to the same variable show a fundamental lack of comprehension on what variable are and how they work.
Really think about why you think assigning a variable to a fixed length array would limit the length of being assigned to another length array?
Strings are Unicode in Java
Strings in Java are Unicode and internally represented as UTF-16 which means they are 2 or 4 bytes per character in memory.
When they are converted to a byte array the number of bytes that represents the string is determined by what encoding is used when converting to the byte[].
Always specify an appropriate character encoding when converting Strings to arrays to get what you expect.
But even then UTF-8 would not guarantee single bytes per character, and ASCII would be not be able to represent non ASCII Unicode characters.
Character encoding is tricky
The ubiquitous internet encoding standard is UTF-8 it will correct in 99.9999999% of all cases, in those cases it isn't converting UTF-8 to the correct encoding is trivial because UTF-8 is so well supported in every toolchain.
Learn to make everything final and you will a lot easier time and less confusion.
import com.google.common.base.Charsets;
import javax.annotation.Nonnull;
import java.util.Arrays;
public class Scratch
{
public static void main(final String[] args)
{
printWithEncodings("Hello World!");
printWithEncodings("こんにちは世界!");
}
private static void printWithEncodings(#Nonnull final String s)
{
System.out.println("s = " + s);
final byte[] defaultEncoding = s.getBytes(); // never do this, you do not know what you will get!
// for ASCII characters the first three will all be the same single byte representations
final byte[] iso88591Encoding = s.getBytes(Charsets.ISO_8859_1);
final byte[] asciiEncoding = s.getBytes(Charsets.US_ASCII);
final byte[] utf8Encoding = s.getBytes(Charsets.UTF_8);
final byte[] utf16Encoding = s.getBytes(Charsets.UTF_16);
System.out.println("Arrays.toString(defaultEncoding) = " + Arrays.toString(defaultEncoding));
System.out.println("Arrays.toString(iso88591) = " + Arrays.toString(iso88591Encoding));
System.out.println("Arrays.toString(asciiEncoding) = " + Arrays.toString(asciiEncoding));
System.out.println("Arrays.toString(utf8Encoding) = " + Arrays.toString(utf8Encoding));
System.out.println("Arrays.toString(utf16Encoding) = " + Arrays.toString(utf16Encoding));
}
}
results in
s = Hello World!
Arrays.toString(defaultEncoding) = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]
Arrays.toString(iso88591) = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]
Arrays.toString(asciiEncoding) = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]
Arrays.toString(utf8Encoding) = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]
Arrays.toString(utf16Encoding) = [-2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33]
s = こんにちは世界!
Arrays.toString(defaultEncoding) = [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29, -127, -81, -28, -72, -106, -25, -107, -116, 33]
Arrays.toString(iso88591) = [63, 63, 63, 63, 63, 63, 63, 33]
Arrays.toString(asciiEncoding) = [63, 63, 63, 63, 63, 63, 63, 33]
Arrays.toString(utf8Encoding) = [-29, -127, -109, -29, -126, -109, -29, -127, -85, -29, -127, -95, -29, -127, -81, -28, -72, -106, -25, -107, -116, 33]
Arrays.toString(utf16Encoding) = [-2, -1, 48, 83, 48, -109, 48, 107, 48, 97, 48, 111, 78, 22, 117, 76, 0, 33]
Always specify the Charset encoding!
.bytes(Charset) is always the correct way to convert a String to bytes. Use whatever encoding you need.
Internally supported encodings for JDK7
new byte[6]; has no effect whatsoever as the array reference buf is getting updated with reference of the array returned by "abcdef".getBytes();.
That's because String.getBytes() returns an entirely different array object which is then assigned to buf. You could have just as easily done this:
byte[] buf = "abcdef".getBytes();
System.out.println(buf.length);

Spring AMQP : CorrelationId changes between moment of sending and receiving a message

While I was testing the send and receive methods which I created for my project I ran into a strange problem.
When I send a certain message using a correlationId that is based on a UUID object, the receiving party gets a slightly modified version of this correlationId (which cannot be deserialised).
On the sending side I do this:
MessageProperties properties = new MessageProperties();
properties.setCorrelationId(MessageSerializer.serialize(UUID.randomUUID().toString()));
On my last test the UUID generated was: "d4170243-9e7e-4c42-9168-f9da4debc5bb"
This generates the following correlationId (when serialized):
[-84, -19, 0, 5, 116, 0, 36, 100, 52, 49, 55, 48, 50, 52, 51, 45, 57, 101, 55, 101, 45, 52, 99, 52, 50, 45, 57, 49, 54, 56, 45, 102, 57, 100, 97, 52, 100, 101, 98, 99, 53, 98, 98]
When I receive the message on the other side this id is slightly changed:
[-17, -65, -67, -17, -65, -67, 0, 5, 116, 0, 36, 100, 52, 49, 55, 48, 50, 52, 51, 45, 57, 101, 55, 101, 45, 52, 99, 52, 50, 45, 57, 49, 54, 56, 45, 102, 57, 100, 97, 52, 100, 101, 98, 99, 53, 98, 98]
When using the RabbitMQ management plugin I noticed that the id already changed upon arrival at the queue.
Tracing my code on the sending side brings me to the send option of the RabbitTemplate class.
RabbitTemplate template = new RabbitTemplate(connection);
template.setExchange("amq.direct");
template.setRoutingKey("some.route");
template.send(message);
But I can't figure out what's causing this problem. I guess it's just me using the correlationId option the wrong way. Could someone help me out?
Appreciate it.
Explanation is the following:
You serialize the UUID string to a byte array
Your serialization prepends non ascii character to this array ([-17, -65, -67, -17, -65, -67, 0, 5, 116, 0, 36,...])
The reference documentation states that the correlation id is a shortstr. RabbitMQ client converts this byte array to a string using
new String(yourArray , "UTF-8").
The non ascii character "corrupt" the conversion from byte[] to string
You can get the same result with the following code:
new String(MessageSerializer.serialize(UUID.randomUUID().toString()) , "UTF-8").getByte("UTF-8")
Which will return:
[-17, -65, -67, -17, -65, -67, 0, 5, 116, 0, 36, 100, 52, 49, 55, 48, 50, 52, 51, 45, 57, 101, 55, 101, 45, 52, 99, 52, 50, 45, 57, 49, 54, 56, 45, 102, 57, 100, 97, 52, 100, 101, 98, 99, 53, 98, 98]

Categories

Resources