byte array to stream encoding issue using google tink - java

I am trying to encrypt a string and using the Google's Tink library.
When I call the method encrypt and the encrypted string returns something like \<Ï~ß¾Ò0ÑP[oxRæ±E*;ÑRÂÉD«Øô§½:î. I tried Base64.DEFAULT, UTF-8 ISO-8859-1, US-ASCI and even StandarCharset.UTF_8 and similar charsets but nothing works. Please help, here is the snippet.
...
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);
byte [] str1 = str.getBytes("UTF-8");
...
byte [] output = aead.encrypt(str1, str2);
String outputStr = new String(output, "UTF-8");
...

If you need the binary output from the encryption method as a string, you can use java.util.Base64 to encode it as such.
In practice, it would look something like:
import java.util.Base64;
...
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);
byte [] str1 = str.getBytes("UTF-8");
...
byte [] output = aead.encrypt(str1, str2);
String outputStr = Base64.getEncoder().encodeToString(output);
...

Related

Why am I not getting the output in the string format?

In the following snippet I try to print encrypted array in a simple string format.
KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String input = "password";
byte encrypted[] = cipher.doFinal(input.getBytes());
String s = new String(encrypted);
System.out.println(s);
But what I get is `┐╫Y²▓ô┴Vh¬∙:╪⌡¶ . Why is it ? How can I print it in the proper string format ?
You could use Base64 encoding from common-codec.
KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String input = "password";
byte encrypted[] = cipher.doFinal(input.getBytes());
System.out.println(new String(Base64.encodeBase64(encrypted)));
Output:
8KA8ahr6INnY4qqtzjAJ8Q==
Encode the bytes in Base64 encoding (How do I convert a byte array to Base64 in Java?)
Or Hex: How to convert a byte array to a hex string in Java?
System.out.println( Hex.encodeHexString( bytes ) );
Most cryptographic algorithms (including blowfish) deal with binary data meaning that it will take binary data in and split out binary data that has been transformed by the algorithm (with the provided specs).
Binary data, as you know is != to string data, however binary data can be represented as string data (using hex, base64, etc).
If we look at your example code we can see this line:
byte encrypted[] = cipher.doFinal(input.getBytes());
This is what it is doing step by step:
It first converts string data into a binary data equivalent using the platform's default charset (NOT RECOMMENDED, but irrelevant).
It is passing the binary data (in form of a byte array) to the method doFinal().
The doFinal() method is processing this byte array via the specifications specified in the statements prior to this line (Blowfish, encryption).
The doFinal() statement is returning a byte array which represents the processed (encrypted, in your case) data.
The fact that the data originally came from a string is no longer relevant because of the nature of the encryption operation does not account for the source or type of the data. The encrypted byte array now contains data that may not be valid charset encoded string. Trying to use a character set to decode the string would most likely result in garbage output as the binary data is no longer a valid string.
However, binary data can be represented directly by outputting the VALUE of the actual bytes rather than what the charset equivalent mapping is (e.g A byte may have the value of 97, which represented in hex is: 0x61 but decoded via ASCII results in the character 'a').
Consider this code to output your encrypted data in hex:
KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String input = "password";
byte encrypted[] = cipher.doFinal(input.getBytes());
StringBuilder str = new StringBuilder();
for(byte b:encrypted){
str.append(String.format("%02x", b));
}
String encData = str.toString();
System.out.println(encData);
P.S: Don't use getBytes() without any arguments! Supply your own charset like UTF-8. Do as follows:
byte encrypted[] = cipher.doFinal(input.getBytes(Charset.forName("UTF-8")));
You can try with:
new String(bytes, StandardCharsets.UTF_8)

byte[] to String {100,25,28,-122,-26,94,-3,-26}

How can I convert this byte[] to String :
byte[] mytest = new byte[] {100,25,28,-122,-26,94,-3,-26};
i get this : "d��^�" when I use :
new String( mytest , "UTF-8" )
Here is code java for creation of key :
m_key = new javax.crypto.spec.SecretKeySpec(new byte[] {100,25,28,-122,-26,94,-3,-26}, "DES");
Thanks.
In order to decode the byte array into something like ASCII, you need to know its original encoding. Otherwise you would need to treat it as binary.
Note: Base64 is intended for transferring binary data across networks.
I would suggest Base64 encoding your byte array. Then in your PHP code decoding the Base64 string back into a UTF-8 string.
In Java, here's how to Base64 encode your byte array and then decode it back to UTF-8:
import org.apache.commons.codec.binary.Base64;
public class MyTest {
public static void main(String[] args) throws Exception {
byte[] byteArray = new byte[] {100,25,28,-122,-26,94,-3,-26};
System.out.println("To UTF-8 string: " + new String(byteArray, "UTF-8"));
byte[] base64 = Base64.encodeBase64(byteArray);
System.out.println("To Base64 string: " + new String(base64, "UTF-8"));
byte[] decoded = Base64.decodeBase64(base64);
System.out.println("Back to UTF-8 string: " + new String(decoded, "UTF-8"));
/* the decoded byte array is the same as the original byte array */
for (int i = 0; i < decoded.length; i++) {
assert byteArray[i] == decoded[i];
}
}
}
The output from the above code is:
To UTF-8 string: d��^�
To Base64 string: ZBkchuZe/eY=
Back to UTF-8 string: d��^�
So if you wanted to use the same binary data in your PHP code, cut and paste the Base64 string into your PHP code and decode it back to UTF-8. Something like this:
<?php
$str = 'ZBkchuZe/eY=';
$key = base64_decode($str);
echo $key;
?>
I don't code in PHP, but you should be able to decode Base64 using this method:
http://php.net/manual/en/function.base64-decode.php
The above code should echo back the original binary data as UTF-8 (albeit with funny characters). The point is that the funny-looking string in the $key variable is representing the same binary data you had in the Java byte array:
d��^�
You should be able to pass the $key variable into your PHP encryption method.
with the way you are doing it makes no sense imo. you are creating a new string with the byte[] as an argument. i dont think that function is suppose to parse. so what you end up with is a lot of junk. but a little bit of googling got me this: http://www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/
Would m_key.getEncoded() give you the desired result.
Javadocs - SecretKeySpec
If not, you have to identify the Key provider that was used for the encoding (which resulted in the byte array that you have now) and decode.

Convert byte[] to String, Send as SMS, return byte[] to String

I have some code that is working correctly in Java but when I try to use it in Android it is having problems.
I am attempting to encrypt an SMS text message with the Blowfish algorithm.
The problem with this code (on android) is that it will not accept the byte[] and will not decrypt the message.
SENDING THE SMS
sMessage = "hello this is a message"
byte[] byteArray = EncryptBlowfish(sMessage);
//Convert the byte[] into a String which can be sent over SMS
StringBuffer strb = new StringBuffer();
for( int x = 0; x<byteArray.length; x++){
strb.append(byteArray[x]).append(",");
}//for loop
sMessage = strb.toString();
(sMessage is then sent via SMS)
RECIVING THE SMS
//Convert the String back to a byte[] which can be decrypted
String[] strArray = sMessage.split(",");
byte[] byteArray = new byte[strArray.length];
int hold;
for (int x = 0; x < strArray.length; x++) {
hold = Integer.parseInt(strArray[x]);
byteArray[x] = (byte) hold;
}//for loop
sMessage = DecryptBlowfish(byteArray);
Encryption Method
public static byte[] EncryptBlowfish(String msg){
byte[] encrypted =null;
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretkey);
encrypted = cipher.doFinal(msg.getBytes());
} catch (){ //NoSuchAlgorithmException, NoSuchPaddingException..etc
}
return encrypted;
}
Decryption Method
public static String DecryptBlowfish(byte[] msg){
byte[] decrypted =null;
try {
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, secretkey);
decrypted = cipher.doFinal(msg);
} catch (){ //NoSuchAlgorithmException, NoSuchPaddingException..etc
}
return decrypted;
}
The message is being encrypted, this creates a byte[], I have then converted the byte[] to a string, the string's output will look something like this...
46,77,52,11,-108,91,-106,88,-81,-43,14,111,-118,-128,-92,-50,69,-44,100,-94,71,92,-49,116,
this output is then sent over SMS. The string is then convert back into a byte[]
but this byte array is not decrypting.
Questions:
Why would this code work in a Java app, but not Android?
Is there a way of making this work in Android?
Is there a better method of converting the byte[] to a String and back.
(Please comment if anymore information is require, Thanks)
I think the answer involves what the default character encoding is on Android vs standard Java. What happens if you specify the character encoding using msg.getBytes(Charset c), and for decoding new String(byte [], Charset c).
Example:
// String => byte []
byte[] bytes = message.getBytes(Charset.forName("ISO-8859-1"));
// byte [] => String
String foo = new String(bytes, Charset.forName("ISO-8859-1"));
You can find what character sets are available from:
for (String c : Charset.availableCharsets().keySet()) {
System.out.println(c);
}
I think there is a problem when you make byte -> string -> byte conversion. Try to send an unencrypted string and retrieve it and check if it is correct.
You should probably specify the encoding at each step.
To convert from a byte array to a string use this
Base64.encodeToString(byteArray, Base64.NO_WRAP);
To convert from a string to a byte array use this
Base64.decode(string, Base64.DEFAULT);
The Base64 class is in the android.util package.

byte to string and vice versa

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.

How do I convert a byte array to Base64 in Java?

Okay, I know how to do it in C#.
It's as simple as:
Convert.ToBase64String(byte[])
and Convert.FromBase64String(string) to get byte[] back.
How can I do this in Java?
Java 8+
Encode or decode byte arrays:
byte[] encoded = Base64.getEncoder().encode("Hello".getBytes());
println(new String(encoded)); // Outputs "SGVsbG8="
byte[] decoded = Base64.getDecoder().decode(encoded);
println(new String(decoded)) // Outputs "Hello"
Or if you just want the strings:
String encoded = Base64.getEncoder().encodeToString("Hello".getBytes());
println(encoded); // Outputs "SGVsbG8="
String decoded = new String(Base64.getDecoder().decode(encoded.getBytes()));
println(decoded) // Outputs "Hello"
For more info, see Base64.
Java < 8
Base64 is not bundled with Java versions less than 8. I recommend using Apache Commons Codec.
For direct byte arrays:
Base64 codec = new Base64();
byte[] encoded = codec.encode("Hello".getBytes());
println(new String(encoded)); // Outputs "SGVsbG8="
byte[] decoded = codec.decode(encoded);
println(new String(decoded)) // Outputs "Hello"
Or if you just want the strings:
Base64 codec = new Base64();
String encoded = codec.encodeBase64String("Hello".getBytes());
println(encoded); // Outputs "SGVsbG8="
String decoded = new String(codec.decodeBase64(encoded));
println(decoded) // Outputs "Hello"
Spring
If you're working in a Spring project already, you may find their org.springframework.util.Base64Utils class more ergonomic:
For direct byte arrays:
byte[] encoded = Base64Utils.encode("Hello".getBytes());
println(new String(encoded)) // Outputs "SGVsbG8="
byte[] decoded = Base64Utils.decode(encoded);
println(new String(decoded)) // Outputs "Hello"
Or if you just want the strings:
String encoded = Base64Utils.encodeToString("Hello".getBytes());
println(encoded); // Outputs "SGVsbG8="
String decoded = Base64Utils.decodeFromString(encoded);
println(new String(decoded)) // Outputs "Hello"
Android (with Java < 8)
If you are using the Android SDK before Java 8 then your best option is to use the bundled android.util.Base64.
For direct byte arrays:
byte[] encoded = Base64.encode("Hello".getBytes());
println(new String(encoded)) // Outputs "SGVsbG8="
byte [] decoded = Base64.decode(encoded);
println(new String(decoded)) // Outputs "Hello"
Or if you just want the strings:
String encoded = Base64.encodeToString("Hello".getBytes());
println(encoded); // Outputs "SGVsbG8="
String decoded = new String(Base64.decode(encoded));
println(decoded) // Outputs "Hello"
Use:
byte[] data = Base64.encode(base64str);
Encoding converts to Base64
You would need to reference commons codec from your project in order for that code to work.
For java8:
import java.util.Base64
Additionally, for our Android friends (API Level 8):
import android.util.Base64
...
Base64.encodeToString(bytes, Base64.DEFAULT);
In case you happen to be using Spring framework along with java, there is an easy way around.
Import the following.
import org.springframework.util.Base64Utils;
Convert like this.
byte[] bytearr ={0,1,2,3,4};
String encodedText = Base64Utils.encodeToString(bytearr);
To decode you can use the decodeToString method of the Base64Utils class.

Categories

Resources