Length of bytes changes after converting it to String - java

I want to convert bytes to String for encryption purpose and then I want to retrieve same bytes for decryption. But the problem is, after generating 16 byte of IV, I convert it to String and when I try to get the same byte from String, the length of bytes changes. Below is the sample program to reproduce the issue.
package com.prahs.clinical6.mobile.edge.util;
import java.security.SecureRandom;
import java.util.Random;
import javax.crypto.spec.IvParameterSpec;
public class Test {
public static void main(String[] args) {
Random rand = new SecureRandom();
byte[] bytes = new byte[16];
rand.nextBytes(bytes);
IvParameterSpec ivSpec = new IvParameterSpec(bytes);
System.out.println("length of bytes before converting to String: " + ivSpec.getIV().length);
String ibString = new String(ivSpec.getIV());
System.out.println("length of bytes after converting to String: " + ibString.getBytes().length);
}
}
Please can any one confirm why it is such behavior and what I need to modify in order to get the same length of byte i.e: 16 in this case.

Please do not convert a randomly generated byte array to a string as there are a lot of values that cannot get encoded to a string -
just think of x00.
The usual way of "converting" such a byte array to a string is the Base64 encoding of the byte array. This will lengthen the string about 1/3 but you can lossless redecode the string to the byte array.
Please note that you should not use Random but SecureRandom as source of those data.
output:
length of bytes before converting to String: 16
ivBase64: +wQtdbbbFdvrorpFb6LRTw==
length of bytes after converting to String: 16
ivSpec equals to ivRedecoded: true
code:
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class Main {
public static void main(String[] args) {
//Random rand = new SecureRandom();
SecureRandom rand = new SecureRandom();
byte[] bytes = new byte[16];
rand.nextBytes(bytes);
IvParameterSpec ivSpec = new IvParameterSpec(bytes);
System.out.println("length of bytes before converting to String: " + ivSpec.getIV().length);
//String ibString = new String(ivSpec.getIV());
String ivBase64 = Base64.getEncoder().encodeToString(ivSpec.getIV());
System.out.println("ivBase64: " + ivBase64);
byte[] ivRedecoded = Base64.getDecoder().decode(ivBase64);
//System.out.println("length of bytes after converting to String: " + ibString.getBytes().length);
System.out.println("length of bytes after converting to String: " + ivRedecoded.length);
System.out.println("ivSpec equals to ivRedecoded: " + Arrays.equals(ivSpec.getIV(), ivRedecoded));
}
}

Related

String of byte array to byte array (rsa and java)

I am working on a web service, and I want to send a byte array as a String, then get the original byte array.
I explain again, my server side has the role of encrypting a message, so I have a byte array.
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(cipher.ENCRYPT_MODE,clefPrivee);
byte[] cipherText= cipher.doFinal(msgEnOctets);
then to send this encrypted message, I send it as a String because I am sending an entire data frame
code :
cipherText.toString();
So I have the array as a string but nothing has changed.
How can I get my original painting back?
thanks
A common way to send byte array is to encode it in Base64 before sending it, on the other side when receiving the string it must be decoded the Base64 to get the original byte array. For example:
Sender:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(cipher.ENCRYPT_MODE,clefPrivee);
byte[] cipherText= cipher.doFinal(msgEnOctets);
return Base64.getEncoder().encodeToString(cipherText);
Receiver:
public void getMessage(String message) {
byte[] decodeMessage = Base64.getDecoder().decode(message);
//...
}
Please do NOT use the conversion from #andy jason (https://stackoverflow.com/a/63489562/8166854) as a byte array (especially when used with data used for
encryption) cannot get converted to a string and vice verse with new String(bytes, charset).
One method for a byte array -> String -> byte array conversion is to use the Base64-encoding:
result:
ByteToString and reverse test
bytes: ee99c01c47185dbd6b62dd9bcfed94d7
method as by comment andy jason
s: ��G]�kbݛ���
tab: efbfbdefbfbd1c47185defbfbd6b62dd9befbfbdefbfbdefbfbd
bytes equal to tab: false
method with base64
s2: 7pnAHEcYXb1rYt2bz+2U1w==
tab2: ee99c01c47185dbd6b62dd9bcfed94d7
bytes equal to tab2: true
code:
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class ByteToString {
public static void main(String[] args) {
System.out.println("https://stackoverflow.com/questions/63489517/string-of-byte-array-to-byte-array-rsa-and-java");
System.out.println("ByteToString and reverse test");
byte[] bytes = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(bytes);
System.out.println("bytes: " + bytesToHex(bytes));
// method by andy jason
System.out.println("\nmethod as by comment andy jason");
Charset charset = StandardCharsets.UTF_8;
String s = new String(bytes, charset);
System.out.println("s: " + s);
byte [] tab = s.getBytes (charset);
System.out.println("tab: " + bytesToHex(tab));
System.out.println("bytes equal to tab: " + Arrays.equals(bytes, tab));
// method with base64
System.out.println("\nmethod with base64");
String s2 = Base64.getEncoder().encodeToString(bytes);
System.out.println("s2: " + s2);
byte[] tab2 = Base64.getDecoder().decode(s2);
System.out.println("tab2: " + bytesToHex(tab2));
System.out.println("bytes equal to tab2: " + Arrays.equals(bytes, tab2));
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
If you want to convert your byte array to String reversibly, you have to use the String constructor which expects a byte array:
String s = new String(bytes, charset);
Then to find your byte array, you have to be careful to use the same charset:
byte [] tab = s.getBytes (charset);

GNU Crypto Encrypt returns blank string

I'm using GNU Crypto library to encrypt simple strings. I believe I have followed to documentation correctly, but the problem is that it just returns an blank string (in this case 5 characters) of spaces. I'm not sure whether I miss coded it or if its some encoding issue. I hope its not something embarrassingly simple.
import gnu.crypto.cipher.CipherFactory;
import gnu.crypto.cipher.IBlockCipher;
import java.util.HashMap;
import java.util.Map;
public class FTNSAMain {
public static void main(String[] args) throws Exception {
String data = "Apple";
String key = "ABCDEFGHIJKLMNOP";
byte[] temp = Encrypt(data.getBytes(), key.getBytes(), "AES");
System.out.println(new String(temp));
}
public static byte[] Encrypt(byte[] input, byte[] key, String algorithm) throws Exception {
byte[] output = new byte[input.length];
IBlockCipher cipher = CipherFactory.getInstance(algorithm);
Map attributes = new HashMap();
attributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, 16);
attributes.put(IBlockCipher.KEY_MATERIAL, key);
cipher.init(attributes);
int bs = cipher.currentBlockSize();
for (int i = 0; i + bs < input.length; i += bs) {
cipher.encryptBlock(input, i, output, i);
}
return output;
}
}
GNU Crypto documentation have the following to say about the void encryptBlock(..) methode:
Encrypts a block of bytes from plaintext starting at inOffset, storing
the encrypted bytes in ciphertext, starting at outOffset. It is up to
the programmer to ensure that there is at least one full block in
plaintext from inOffset and space for one full block in ciphertext
from outOffset. A java.lang.IllegalStateException will be thrown if
the cipher has not been initialized.
Your input:
String data = "Apple";
Is not a full datablock as AES needs data in blocks of 16 bytes. Also, your output buffer is also too short.
For starters, try encrypting with an input that ends up as 16 bytes like:
String data = "Apple56789abcdef";

Java AES String decrypting "given final block not properly padded"

For all haters, I READ MANY topics like this one, and non of them was helpful.
eg. here javax.crypto.BadPaddingException: Given final block not properly padded error while decryption or here Given final block not properly padded
I want to encrypt and then decrypt Strings. Read many topics about
"Given final block not properly padded" exception, but non of these solutions worked.
My Class:
package aes;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class EncryptionExample {
private static SecretKeySpec key;
private static IvParameterSpec ivSpec;
private static Cipher cipher;
private static byte[] keyBytes;
private static byte[] ivBytes;
private static int enc_len;
public static void generateKey() throws Exception
{
String complex = new String ("9#82jdkeo!2DcASg");
keyBytes = complex.getBytes();
key = new SecretKeySpec(keyBytes, "AES");
complex = new String("#o9kjbhylK8(kJh7"); //just some randoms, for now
ivBytes = complex.getBytes();
ivSpec = new IvParameterSpec(ivBytes);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
public static String encrypt(String packet) throws Exception
{
byte[] packet2 = packet.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted = new byte[cipher.getOutputSize(packet2.length)];
enc_len = cipher.update(packet2, 0, packet2.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);
return packet = new String(encrypted);
}
public static String decrypt(String packet) throws Exception
{
byte[] packet2 = packet.getBytes();
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(packet2, 0, enc_len, decrypted, 0);
HERE EXCEPTION>>>>> dec_len += cipher.doFinal(decrypted, dec_len); <<<<<<<<<
return packet = new String(decrypted);
}
// and display the results
public static void main (String[] args) throws Exception
{
// get the text to encrypt
generateKey();
String inputText = JOptionPane.showInputDialog("Input your message: ");
String encrypted = encrypt(inputText);
String decrypted = decrypt(encrypted);
JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
"Encrypted: " + new String(encrypted) + "\n"
+ "Decrypted: : " + new String(decrypted));
.exit(0);
}
}
The thing is, when I decrypt strings (about 4/10 of shots), I get that exception:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:479)
at javax.crypto.Cipher.doFinal(Cipher.java:2068)
at aes.EncryptionExample.deszyfrujBez(EncryptionExample.java:HERE tag)
at aes.EncryptionExample.main(EncryptionExample.java:Main starting)
Does anybody know what to change here (key? *.doFinal() method?) to make it work?
# for those curious - methods have to be static, as this is a part of something bigger ;)
When you use byte[] packet2 = packet.getBytes() you are converting the string based on the default encoding, which could be UTF-8, for example. That's fine. But then you convert the ciphertext back to a string like this: return packet = new String(encrypted) and this can get you into trouble if this does not round-trip to the same byte array later in decrypt() with another byte[] packet2 = packet.getBytes().
Try this instead: return packet = new String(encrypted, "ISO-8859-1"), and byte[] packet2 = packet.getBytes("ISO-8859-1") -- it's not what I would prefer, but it should round-trip the byte arrays.
The result of encryption is binary data. In most cases it cannot be interpreted as a valid string encoding. So the call to new String(encrypted) will most likely distort the encrypted bytes and after doing packet.getBytes() you end up with a byte array with different content.
The decryption now fails because the cypher text has been changed. The padding bytes are not correctly recovered and cannot be removed.
To fix that, don't convert the cypher text to a string, keep the byte array.

sending rc4 encrypted api call in java

I am trying to make an api call in java using these steps:
json encode
RC4 encryption
base64 encoding
I am currently using the same system in php and its working correctly:
$enc_request = base64_encode(openssl_encrypt(json_encode($request_params), "rc4", $this->_app_key));
But when I use the same system in java, the results are not as expected. Here's my code:
//json encoding
JSONObject obj = new JSONObject();
obj.put("email", username);
obj.put("password", password);
obj.put("action", "login");
//function to encode base64
private String getBase64Encoded(String encryptedJsonString)
{
byte[] encoded = Base64.encodeBase64(encryptedJsonString.getBytes());
String encodedString = new String(encoded);
return encodedString;
}
//function to encrypt in RC4
private String getRC4EncryptedString2(String string, String key) throws Exception
{
Cipher cipher = Cipher.getInstance("RC4");
SecretKeySpec rc4Key = new SecretKeySpec(key.getBytes(), "RC4");
cipher.init(Cipher.ENCRYPT_MODE, rc4Key);
byte[] cipherText = cipher.update(string.getBytes());
return new String(cipherText);
}
I was able to identify the problem upto the RC4 encryption which is not returning the same result as the php version.
I've been battling this for 2 days now. I hope I have not missed any stupid thing because this should be straight-forward.
Thanks
You should use a byte[] not a String to hold intermediate byte array values. A String is for text, not raw data, and will attempt to decode the bytes as character data using your system's default character set (at least, the single-parameter String constructor will). Same with String.getBytes().
Just return cipherText directly from getRC4EncryptedString2(), and pass it directly to getBase64Encoded(). There's a reason those encoders operate on byte arrays, and that reason is not so that you can garble the data by applying a character encoding to it in between.
The same goes for the key you are passing to getRC4EncryptedString2(). At the bare minimum use String.getBytes("ISO-8859-1") or something (assuming that your key is actually text and not yet another garbled byte array). The no-parameter version of getBytes() returns the text encoded using your system's default character set, which is not guaranteed to be what you want.
That also all applies to the String you are returning from your base 64 encoder. I don't know what base 64 encoder you are using, but make sure you specify the character set to the String constructor. Most likely you will be OK, purely by coincidence, but you should always specify a character set when converting to/from String and raw bytes. And that, of course, assumes that your base 64 encoder returns text, rather than bytes in the range 0-63.
The general point here is you can't just convert back and forth from String to byte[]. A String is for text and it's representation as a byte[] depends on the character encoding.
I am able to achieve this using the following code. Hope this helps!
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.codec.DecoderException;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONException;
import org.json.JSONObject;
public class RC4Algo {
public static void main(String args[])throws IOException, NoSuchAlgorithmException, DecoderException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, JSONException
{
decryptRC4();
}
static String decryptRC4() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, JSONException{
//byte[] plainBytes = "testString".getBytes();
//json encoding
JSONObject obj = new JSONObject();
obj.put("email", "username");
obj.put("password", "password");
obj.put("action", "login");
byte [] plainBytes = obj.toString().getBytes();
String hashedKey = hashedData("thisismysecretkey");
//Generate a new key using KeyGenerator
/*KeyGenerator rc4KeyGenerator = KeyGenerator.getInstance("RC4");
SecretKey key = rc4KeyGenerator.generateKey();*/
Key key = new SecretKeySpec(Hex.decode(hashedKey), "RC4");
// Create Cipher instance and initialize it to encrytion mode
Cipher cipher = Cipher.getInstance("RC4"); // Transformation of the algorithm
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherBytes = cipher.doFinal(plainBytes);
String encoded = encodeBase64(cipherBytes);
String decoded = decodeBase64(encoded);
// Reinitialize the Cipher to decryption mode
cipher.init(Cipher.DECRYPT_MODE,key, cipher.getParameters());
byte[] plainBytesDecrypted = cipher.doFinal(Hex.decode(decoded));
System.out.println("Decrypted Data : "+new String(plainBytesDecrypted));
return new String(plainBytesDecrypted);
}
static String decodeBase64(String encodedData){
byte[] b = Base64.getDecoder().decode(encodedData);
String decodedData = DatatypeConverter.printHexBinary(b);
return decodedData;
}
static String encodeBase64(byte[] data){
byte[] b = Base64.getEncoder().encode(data);
String encodedData = new String(b);
/*String encodedData = DatatypeConverter.printHexBinary(b);*/
return encodedData;
}
static String hashedData(String key) throws NoSuchAlgorithmException{
String password = key;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte byteData[] = md.digest();
//convert the byte to hex format method 1
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
//convert the byte to hex format method 2
StringBuffer hexString = new StringBuffer();
for (int i=0;i<byteData.length;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
Output:

javax.crypto.BadPaddingException: error

I am trying to run a simple encryption/decryption program. I am getting a padding exception. There must be something hidden that I am not aware. I basically encrypted a string write it to a file, read it back, and decrypted it. The original encrypted array was decrypted without a problem. I compared the original encrypted array with the array read back from the file, they were identical from what I can see. The buffer from the file does not work, so there must be something difference. I don't know what to do.
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
public class sample
{
private static String _algo = "AES";
private static byte[] _key = new byte[16];
public static byte[] encrypt (String val) throws Exception
{
Key key = new SecretKeySpec (_key, _algo);
Cipher c = Cipher.getInstance (_algo);
c.init (Cipher.ENCRYPT_MODE, key);
byte[] encode = c.doFinal (val.getBytes());
return encode;
}
public static String decrypt (byte[] val) throws Exception
{
Key key = new SecretKeySpec (_key, _algo);
Cipher c = Cipher.getInstance (_algo);
c.init (Cipher.DECRYPT_MODE, key);
byte[] decode = c.doFinal (val);
String decodeStr = new String (decode);
return decodeStr;
}
public static void main (String[] args) throws Exception
{
String str = "Good bye cruel world";
//
// get password from command line
//
_key = args[0].getBytes();
byte[] encodeArray = sample.encrypt (str);
//
// write encrypted array to file
//
FileOutputStream os = new FileOutputStream ("data");
os.write (encodeArray);
os.close();
//
// decode and print out string
//
String decodeStr = sample.decrypt (encodeArray);
System.out.println ("decodeStr = " + decodeStr);
//
// read back encrypted string
byte[] buffer = new byte[64];
FileInputStream is = new FileInputStream ("data");
is.read (buffer);
is.close();
decodeStr = sample.decrypt (buffer);
System.out.println ("decodeStr = " + decodeStr);
}
}
Output:
java sample 1234567890123456
decodeStr = Good bye cruel world
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at sample.decrypt(sample.java:32)
at sample.main(sample.java:70)
The problem is that the byte buffer with a size of 64, which you are reading the file into, is too big. Change it to 32.
Or use the length of the file like this:
byte[] buffer = new byte[(int)new File("data").length()];

Categories