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);
Related
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));
}
}
I am trying to write a small Java code to see how to properly use SHA1.
Following is the code snippet I came up with:
package dummyJavaExp;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Exp1 {
public static void main(String[] args) throws NoSuchAlgorithmException {
// TODO Auto-generated method stub
String str = "Hello there";
String hashstr = new String(MessageDigest.getInstance("SHA1").digest(str.getBytes()));
System.out.println("Encrypted value of " + str + " is: " + hashstr);
}
}
But the above code gives some weird characters as shown in the following output message when I run the above code:
Encrypted value of Hello there is: rlvU>?Þ¢‘4ónjòêì\Î
I thought the encrypted message will be some alphanumeric string.
Am I missing something in my code?
When you use String sample = new String(byte[] bytes) it will create a string with platform's default charset, your digest bytes may not have alphanumeric representation in that charset.
Try to use Base64 or HexString to display digest message.
For example in JAVA8:
You can encode your digest bytes to string with:
String hashstr = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA1").digest(str.getBytes("UTF-8")));
You can decode your Base64 with:
byte [] digest = Base64.getDecoder().decode(hashstr);
I want to encrypt a challenge (like 162236fe0bec620827958c8fdf7e4bc7 ) using this key C6864E7696C686 with the DES algorithm.
Here is my code :
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.xml.bind.DatatypeConverter;
import javax.crypto.spec.DESKeySpec;
def data = prev.getResponseData();
String challenge = javax.xml.bind.DatatypeConverter.printHexBinary(data);
final String strPassPhrase = "C6864E7696C686";
String param = challenge;
System.out.println("Text : " + param);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey key = factory.generateSecret(new DESKeySpec(hexStringToByteArray(strPassPhrase)));
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
String str = DatatypeConverter.printBase64Binary(cipher.doFinal(param.getBytes()));
System.out.println("Text Encryted : " + str);
cipher.init(Cipher.DECRYPT_MODE, key);
String str2 = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(str)));
System.out.println("Text Decryted : " + str2);
But i get this exception :
java.security.InvalidKeyException: Wrong key size
Edit :
I have copy this function to convert my hex string to bytes :
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
But I get the same exception ...
Your DES key should be 8 bytes (56 bits + 8 parity bits).
The string you're using as a key looks like a hexadecimal representation of 7 bytes, but instead of decoding it as hex, you're getting the bytes for the characters in the hex string.
Since there are 14 characters, you will most likely (depending on your encoding) end up with 14 bytes, which is too long for DES.
There are a couple of approaches described in this question that explain how to convert the hex string to a byte array.
That will however only get you so far, because you're still one byte short. A traditional approach seems to be to take the 56 bits you have and spread them out over 8 bytes, adding one parity bit to each byte. A Java example of how to do that is described in this answer. Another approach could be to just add a null byte at the end of the key. Which approach you should take depends on the key's intended usage, especially the way it is used by the other parties you're exchanging information with.
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";
A "data" String is encoded in Base64 (URL safe version) then crypted into a byte array. This byte array is turned into a String and sent to a REST web service. When it arrives on the web service side, the length of the String has changed. I am not a specialist, what do I do wrong?
The code:
//CLIENT SIDE
import org.apache.commons.codec.binary.Base64;
String data = "My message to be encrypted";
PublicKey pubKey = readPublicKeyFromFile();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encryptedData = cipher.doFinal(Base64.encodeBase64URLSafe(data.getBytes()));
System.out.println("length of original data string: " + data.length());
System.out.println("length of original data string turned into byte array: " + data.getBytes().length);
System.out.println("length of data string encrypted into byte array: " + encryptedData.length);
String encryptedDataToString = new String(encrypedData,"UTF-8");
System.out.println("length encrypted String into byte array, converted back to String for url: " + encryptedDataToString.length);
httpclient = new DefaultHttpClient();
builder = new URIBuilder();
builder.setScheme("http").setHost(xxx + "webresources/GetData/" + path)
.setParameter("data", encryptedDataToString);
uri = builder.build();
//SERVER SIDE
import org.apache.commons.codec.binary.Base64;
#GET
#Path("path")
#Produces("text/plain")
public String getToken(#QueryParam("data") String data) {
System.out.println("length of data: " + data.length());
System.out.println("length of data to byte array wthout decoding: " + data.getBytes().length);
System.out.println("length of data to byte decoded: " + Base64.decodeBase64(data).length);
//CONSOLE
//CLIENT SIDE
length of original data string: 37
length of original data string turned into byte array: 37
length of data string encrypted into byte array: 256
length encrypted String into byte array, converted back to String for url: 256
//SERVER SIDE
INFO: length of data: 237
INFO: length of data to byte wthout decoding: 444
INFO: length of data to byte decoded: 4
The data String as it is appearing in the URL, client side:
T%23%EF%BF%BD%0C%EF%BF%BD%EF%BF%BD7%EF%BF%BD%EF%BF%BDk%EF%BF%BDA%EF%BF%BD%10L%EF%BF%BD%EF%BF%BD%EF%BF%BD%14%EF%BF%BD%EF%BF%BD%5E%22%15B%1Ae%01%EF%BF%BDgmE%0C%EF%BF%BD%EF%BF%BDn%EF%BF%BD%3D%EF%BF%BD%EF%BF%BD%DC%80%EF%BF%BD%EF%BF%BDIM%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDeM%EF%BF%BD5%EF%BF%BD%EF%BF%BD%26%EF%BF%BD%1E%7D%EF%BF%BDNAQ%EF%BF%BDlT%EF%BF%BD%EF%BF%BDVXR%EF%BF%BDi%0E%EF%BF%BD%EF%BF%BD%EF%BF%BD3U%0C%EF%BF%BD%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%0Fep%EF%BF%BD%C3%A1b6%EF%BF%BD%CE%B5y9x%EF%BF%BD%EF%BF%BD%0D%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%18%7C%06%21%EF%BF%BD0%EF%BF%BD%EF%BF%BD%EF%BF%BD%03%EF%BF%BD%17%CB%8F%7D%EF%BF%BD%26A%EF%BF%BD%12%EF%BF%BD%EF%BF%BD%EF%BF%BD-%DE%8D%EF%BF%BD%EF%BF%BD%EF%BF%BDt%EF%BF%BD%EF%BF%BD%EF%BF%BDTezs%EF%BF%BD%0C%EF%BF%BD%2C%EF%BF%BD%EF%BF%BD%EF%BF%BD%DD%94%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%19%EF%BF%BD%25%EF%BF%BDt%EF%BF%BDQ%EF%BF%BD%7D%EF%BF%BDF%EF%BF%BD%EF%BF%BD%EF%BF%BD%1An%EF%BF%BD+%EF%BF%BD%CF%86R%EF%BF%BDW%EF%BF%BDU%EF%BF%BDxo%EF%BF%BDd%EF%BF%BD%08-%3F%EF%BF%BD%2F%EF%BF%BD%5Bg%EF%BF%BD2%EF%BF%BD%EF%BF%BDk%5E%EF%BF%BDm%EF%BF%BD%EF%BF%BD%25%EF%BF%BD%EF%BF%BD*%07%5E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDU%EF%BF%BD%7F%EF%BF%BDP%EF%BF%BD%EF%BF%BD%EF%BF%BD%7B%07%EF%BF%BDO%EF%BF%BD%EF%BF%BD%11%CB%9C%EF%BF%BD%1Dk%EF%BF%BDkL8%EF%BF%BD*%EF%BF%BD%1D%EF%BF%BDl%EF%BF%BD%EF%BF%BD%EF%BF%BD%02%EF%BF%BD
The data String as it is received, server side:
T#���7��k�A�L�����^"Be�gmE��n�=��܀��IM����eM�5��&�}�NAQ�lT��VXR�i���3U����ep�áb6�εy9x������|!�0����ˏ}�&A����-ލ���t���Tezs��,���ݔ�������%�t�Q�}�F���n� �φR�W�U�xo�d-?�/�[g�2��k^�m��%��^����U��P���{�O��˜�k�kL8���l���
(not sure these are the characters actually received, or just badly printed on the console?)
You have to encrypt it first and then encode it with base64 and send it via net.
Otherwise the encryption creates (pseuro)random data and some parts of that might not be properly encoded for transfer via web and will inevitably get corrupted.
The same applies if you want to compress your data. You do the compression before encrypting them because after the encryption it will be much harder for the algorithm to compress seemingly random data.
Generally the thing you send to a web service should be base64 encoded as a last step/alteration.