I have a Serialized class that I need to send as an object via Bluetooth, and also implements Runnable. As such, I set a few variables first, then send it as an object to be executed by another Android device, which will then set its result into a variable, and send back the same object with the result in one of the variables. I have been using the following two methods to serialize my object and get a ByteArray before sending them through the OutputStream of my BluetoothSocket, and to deserialize that ByteArray to get back my object.
public static byte[] serializeObject(Object o) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(o);
out.flush();
out.close();
// Get the bytes of the serialized object
byte[] buf = bos.toByteArray();
return buf;
}
public static Object deserializeObject(byte[] b) throws OptionalDataException, ClassNotFoundException, IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInputStream in = new ObjectInputStream(bis);
Object object = in.readObject();
in.close();
return object;
}
I still got the same error from these two methods, so I tried to merge it with the methods I use to send my ByteArray via BluetoothSocket's OutputStream, as shown below.
public void sendObject(Object obj) throws IOException{
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
ObjectOutputStream out = new ObjectOutputStream( bos );
out.writeObject(obj);
out.flush();
out.close();
byte[] buf = bos.toByteArray();
sendByteArray(buf);
}
public void sendByteArray(byte[] buffer, int offset, int count) throws IOException{
bluetoothSocket.getOutputStream().write(buffer, offset, count);
}
public Object getObject() throws IOException, ClassNotFoundException{
byte[] buffer = new byte[10240];
Object obj = null;
if(bluetoothSocket.getInputStream().available() > 0){
bluetoothSocket.getInputStream().read(buffer);
ByteArrayInputStream b = new ByteArrayInputStream(buffer);
ObjectInputStream o = new ObjectInputStream(b);
obj = o.readObject();
}
return obj;
}
In the end, I get the same error when deserializing the object at the receiving device, as shown below.
java.io.StreamCorruptedException
at java.io.ObjectInputStream.readStreamHeader (ObjectInputStream.java:2102)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:372)
and so on...
Can anybody please help? I'm desperate, this has been killing me for weeks, and I need this to work in a few days. :S
According to this and this thread :
You should use single ObjectOutputStream and ObjectInputStream for the life of the socket, and don't use any other streams on the socket.
Secondly, use ObjectOutputStream.reset() to clear previous values.
Let me know if this works !
Related
I want to send a json object from my android device to the server (in post).
In my json object, I need to add an image in Base64. I cannot use to String to convert my image to Base64 because a String is too short to contain a Base64 encoded file.
I must use a BytesArray.
How to send something like that to a JSON webservice ?
{
"emergency":"gsjqsbl",
"cod_valid":"O",
"image":{
"content":"/9j/4AAQSkZJRg ## MY VERY VERY VERY VERY VERY VERY VERY LONG IMAGE IN BASE64 ## BWNn+SV5H8KQADnn9ysrKA1EVX+lNDkSJ8ai8UADCLoAR3TWVlHT95AVvcfwCvD4Hq1joP5NX3Ciat7zyP4VlZW9bnl1sf//Z",
"content_type":"image/jpg"
},
"indoor":"yes",
"access_conditional":"text",
"geo_shape":{
"type":"Point",
"coordinates":[
2.0202024,
45.799005
]
},
"lastupdate":"",
"ref_fr_sdis91":"",
"name":"TEST IN UPPERCASE WITH SPECIAL CHARACTERS ;/*-é#$~æ€",
"geo_point_2d":"45.799005,2.0202024",
"source":"soures",
"objectid":"",
"aed_location":"Spopopo"
}
I really cannot use String.
Thanks
EDIT : What i've done yet :
//output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//write text
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("json start { blabla: value");
//Write image
AssetManager assetManager = context.getAssets();
InputStream istr;
Bitmap bitmap = null;
try {
istr = assetManager.open("pictures/defib12.jpg");
bitmap = BitmapFactory.decodeStream(istr);
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos);
outputStream.write(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
//write text
OutputStreamWriter outputStreamWriter2 = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter2);
bufferedWriter.write("json end }");
HttpResponse response = restClient.executePostRequest(pushUrl, outputStream);
And :
public HttpResponse executePostRequest(String url, ByteArrayOutputStream outputStream) throws IOException {
LogWrapper.debug(DmaRestClient.class, "New HttpPost request: " + url);
DefaultHttpClient client = new DefaultHttpClient();
HttpPost request = new HttpPost(url);
request.setEntity(new ByteArrayEntity(outputStream.toByteArray()));
request.setHeader("Content-Type", "application/json");
return client.execute(request);
}
You should use a lib to do that. Check Genson, it provides two mechanisms to ser/de binary content: using the classic base64 encoded strings (but in your case it looks like it won't work) or ser/de as an array of ints. Note that you will have to use the same mechanism on the server side - but it could be another library that support this kind of format.
With Genson you would create classes to represent your json and image.content value would be the byte array. To enable this option in Genson:
Genson genson = new GensonBuilder().useByteAsInt(true).create();
genson.serialize(yourObject, theOutputStream);
If you want to do it by hand without using any lib you still can (but it is a bad practice...) use the same trick => Ser/de the byte array as an int array.
I am encrypting images in assets folder with following code and trying to decrypt in apk. (I am doing this just to avoid easy copying of images with just unzip of apk file). I know i will have the key as part of apk.
I used and tested below code for encrypting the images with stand alone java program. (I tested them by decrpyting and it is working fine in stand alone java program.
Encryption
byte[] incrept = simpleCrypto.encrypt(KEY, simpleCrypto.getImageFile("E:/aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fos = new FileOutputStream(new File("E:/out-aeroplane.png"));
fos.write(incrept);
fos.close();
Decryption
byte[] decrpt = simpleCrypto.decrypt(KEY, simpleCrypto.getImageFile("E:/out-aeroplane.png"));
//Store encrypted file in SD card of your mobile with name vincent.mp3.
FileOutputStream fosdecrypt = new FileOutputStream(new File("E:/outdecrypt-aeroplane.png"));
fosdecrypt.write(decrpt);
fosdecrypt.close();
Encrypt Decrypt logic
public byte[] getImageFile(String fileName) throws FileNotFoundException
{
byte[] Image_data = null;
byte[] inarry = null;
try {
File file = new File(fileName);
#SuppressWarnings("resource")
FileInputStream is = new FileInputStream (file); // use recorded file instead of getting file from assets folder.
int length = is.available();
Image_data = new byte[length];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = is.read(Image_data)) != -1)
{
output.write(Image_data, 0, bytesRead);
}
inarry = output.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return inarry;
}
public byte[] encrypt(String seed, byte[] cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext);
// return toHex(result);
return result;
}
public byte[] decrypt(String seed, byte[] encrypted) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = encrypted;
byte[] result = decrypt(rawKey, enc);
return result;
}
//done
private byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
In my apk file after getting the image as InputStream, I am decrypting them after converting them as byte array. Again I am converting the decrypted byte array to input stream for BitmapFactory.decode. I tried both decodeByteArray and decodeStream. Both not working.
Image is encrypted with stand alone java program and it is decrypted in apk. (If i decrypt in stand alone java program, it is working fine.)
I am getting error saying Failed to decode Stream javax.crypto.BadPaddingException: pad block corrupted
public static Bitmap readBitmap(InputStream input) {
if (input == null)
return null;
try {
String KEY = "kumar";
byte[] inarry =IOUtils.toByteArray(input);
byte[] decrpt = SquarksCryptUtil.decrypt(KEY, inarry);
InputStream cleanStream = null;
cleanStream = new ByteArrayInputStream(decrpt);
// return BitmapFactory.decodeStream(cleanStream);
return BitmapFactory.decodeByteArray(decrpt, 0, decrpt.length);
// return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.e(FILE_NAME, "Failed to decode Stream " + e);
return null;
} finally {
close(input);
}
}
The problem that you are facing is because you are assuming that the random number generates the same key on each platform. This issue is due to the fact that there is a very bad example on the internet that uses SecureRandom as a key derivation function, which it is not. SecureRandom is not even well defined if the seed is set directly. You can use PBKDF2 instead of the incorrect key derivation function on both sides, there should be enough pointers on stackoverflow on how to perform PBKDF2 key derivation using Java.
The best thing to do is to decrypt the images on the SE platform and then re-encrypt them correctly using PBKDF2. Currently, the way the key is derived is only specified in the source code of the Sun implementation of "SHA1PRNG". That's not a good foundation at all.
Furthermore you need to assure that there are no platform dependencies during encryption/decryption, as others already have pointed out.
Hi I have problem in my android application. I wrote client which used tcp and udp. Data are encrypt by AES. BUT my application return exception when i try to receive datagram.
this is my code:
protected Void doInBackground(Void... params)
{
try
{
udp = new DatagramSocket(2500);
boolean flagaBYE = true;
String w = "";
while(flagaBYE)
{
byte[] receiveData= new byte[1024];
DatagramPacket pakiet = new DatagramPacket(receiveData, receiveData.length);
udp.receive(pakiet);
String wiadomosc = new String(pakiet.getData(),"utf-8");
publishProgress(wiadomosc);
String szyfr = main.preferences.getString("SZYFR_TCP", "");
if(!szyfr.equals("1"))
{
Encryptor enc = new Encryptor(getKeyBytes(key), getCode());
wiadomosc = enc.decrypt(wiadomosc);
}
My exception is pad block corrupted
I generate key like this:
String key = "tojestkluczwlasnie";
And next parse string to byte[]
public static byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= key.getBytes("UTF-8");
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
You should not treat bytes as being UTF-8 strings, and you should not treat passwords if they are keys. Its likely that you lose information during the transformations between the data formats (encoding/decoding).
I want to consume a BasicHttp WCF web service with ksoap2 that is compressed by GZIP.
Is there a way to do this in the Android version of ksoap2 (http://code.google.com/p/ksoap2-android/) or is there another way?
I have create a class that extend the HTTPTransportSe and overrode the call() method and added this line to the code (taken from here https://github.com/mosabua/ksoap2-android/blob/master/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java)
connection.setRequestProperty("Accept-Encoding","[Here you have to put the encoding]");
Then when I get the InputStream I use the retHeaders variable to check if there's the encoding.
if (retHeaders != null){
Iterator it = retHeaders.iterator();
boolean found = false;
String encoding = "";
while (it.hasNext()){
HeaderProperty temp = (HeaderProperty) it.next();
if (temp.getKey().contentEquals("content-encoding")){
found = true;
encoding = temp.getValue();
}
}
if (found){
is = new GZIPInputStream(is, new Inflater(true));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[256];
while (true) {
int rd = is.read(buf, 0, 256);
if (rd == -1)
break;
bos.write(buf, 0, rd);
}
bos.flush();
buf = bos.toByteArray();
is.close();
is = new ByteArrayInputStream(buf);
}
}
parseResponse(envelope, is);
And then you have to pass the "is" to the parser.
If there's a better way to code it I will happy to know about it. .))
I have an application where i need to download a large amount of data via a SOAP call to a webservice into the application when it is first run. The response is then sent to a function which converts the XML and stores the data in a db file.
The data is more than 16MB in size and i have a java.lang.OutOfMemoryError everytime.
Modifying the webservice to give out smaller amounts of data is not an option.
Is there a way to be able to download the large data? Something like an InputStream perhaps?
This is my code
public Protocol[] getProtocols() {
String METHOD_NAME = "GetProtocols";
String SOAP_ACTION = "urn:protocolpedia#GetProtocols";
Log.d("service", "getProtocols");
SoapObject response = invokeMethod(METHOD_NAME, SOAP_ACTION);
return retrieveProtocolsFromSoap(response);
}
private SoapObject invokeMethod(String methodName, String soapAction) {
Log.d(TAG, "invokeMethod");
SoapObject request = GetSoapObject(methodName);
SoapSerializationEnvelope envelope = getEnvelope(request);
return makeCall(envelope, methodName, soapAction);
}
Can anyone suggest what should be done in this case?
Thanks and regards
Mukul
Just an update, I found that the "call" method in AndroidHttpTransport was running out of memory at this line -
if (debug) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[256];
while (true) {
int rd = is.read(buf, 0, 256);
if (rd == -1)
break;
bos.write(buf, 0, rd);
}
bos.flush();
buf = bos.toByteArray(); //Goes out of memory here
responseDump = new String(buf);
is.close();
is = new ByteArrayInputStream(buf);
the call to toByteArray takes a lot of memory, so to overcome this, instead of converting the response to a byte array, i now directly write it to an XML file, and this is saved at a location of my choice. Here -
if (debug) {
FileOutputStream bos = new FileOutputStream("/data/data/com.mypackage.myapp/response.xml");
byte[] buf = new byte[1048576];
int current = 0; int i=0; int newCurrent = 0;
while ((current = inputStream.read(buf)) != -1) {
newCurrent = newCurrent + current;
Log.d("current", "Current = " + current + " total = "+newCurrent+" i = "+i++);
bos.write(buf, 0, current);
}
bos.flush();
}
The device no longer runs out of memory, and i have a custom parse method that takes this XML and writes it to the DB.
Two strategies to help you solve this problem:
Save your SOAP XML stream directly to disk as you download it. Don't store it in memory.
Parse it using a SAX-style parser, where you don't load the whole DOM in memory, but rather parse it in chunks.
Depending on the kind of XML you are handling, using SAX parsers is usually harder in code; you will have to keep track of many things yourself, and you won't be able to "jump" from section to section of your DOM tree. But the memory consumption will be way lower.
Take note, however, that many "high-level" network communication libraries usually load the whole XML DOM in memory, which might be the case here. You will probably have to create and manage the HTTP connection yourself, and then manually parse the result.
Fixed!
I downloaded/copied HttpTransportSE java class from here (after copied, some code errors can occur, but they are all quick fixable) and added to my package:
https://github.com/mosabua/ksoap2-android/blob/master/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
removed from my Connection class this row:
import org.ksoap2.transport.HttpsTransportSE;
and substituted this code in my new HttpTransportSE.java file:
if (debug) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[256];
while (true) {
int rd = is.read(buf, 0, 256);
if (rd == -1)
break;
bos.write(buf, 0, rd);
}
bos.flush();
buf = bos.toByteArray(); //Goes out of memory here
responseDump = new String(buf);
is.close();
is = new ByteArrayInputStream(buf);
}
with this
if (debug) {
FileOutputStream bos = new FileOutputStream(file);
byte[] buf = new byte[256];
while (true) {
int rd = is.read(buf, 0, 256);
if (rd == -1) {
break;
}
bos.write(buf, 0, rd);
}
bos.flush();
}
where "file" is a simple file object like new File("/sdcard/","myFile.xml") for example