Decrypt pixels of bitmap - java

I have an app that encrypts the pixels of an image and then decrypts them.
The encryption code is:
File file = new File(fullPath, "uoc" +format +".png");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteBuffer bb = ByteBuffer.allocate(image.getByteCount());
image.copyPixelsToBuffer(bb);
byte[] b = bb.array();
image.copyPixelsFromBuffer(ByteBuffer.wrap(encrypt_image(b, password)));
image.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b2 = baos.toByteArray();
bos.write(b2);
bos.flush();
bos.close();
So right now I can open the file and I see noise, that's just what I want.
Once it works I need to decrypt the content of the image with no success :(
My code is similar than the one to encrypt:
ByteBuffer bb = ByteBuffer.allocate(image_file.getByteCount());
image_file.copyPixelsToBuffer(bb);
byte[] b = bb.array();
// Decrypt
decryptedData = Security.decrypt(key,b);
image_file.copyPixelsFromBuffer(ByteBuffer.wrap(decryptedData));
The problems is in Security.decrypt(key,b) because it throws pad bloc corrupted exception.
My encryption algorythm is:
public static 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;
}
and decryption:
public static 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;
}
And I have checked that the key is the same in both cases.
Anyone has an idea about how to solve that issue?
EDIT:
I have tried the next code but it throws exception as well:
ByteBuffer bb = ByteBuffer.allocate(image.getByteCount());
image.copyPixelsToBuffer(bb);
byte[] b = bb.array();
image.copyPixelsFromBuffer(ByteBuffer.wrap(Security.encrypt(Security.getRaw(password),b)));
//****
ByteBuffer bb2 = ByteBuffer.allocate(image.getByteCount());
image.copyPixelsToBuffer(bb2);
byte[] b_aux = bb.array();
// Decrypt
image.copyPixelsFromBuffer(ByteBuffer.wrap(Security.decrypt(Security.getRaw(password),b_aux)));
//*****
image.compress(Bitmap.CompressFormat.PNG, 100, baos);
Right now I am totally lost and my final project depends on this algorithm.
Anyone can help me??

The problem is that the encrypted bytes no longer fit the image bytes, the array size is going to be lost, and so on. Can't you just write the bytes immediately to a file?

It appears that you are PNG compressing the encrypted content. You need to do this in the reverse order. Perform any compression (such as PNG), then encrypt the result.
On the client side, decrypt the content, then decompress.
i think it should look something like:
image.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] encryptedPNG = Security.encrypt(Security.getRaw(password), boas.toByteArray());
//Decrypt
byte[] decryptedPNG = Security.decrypt(Security.getRaw(password), encryptedPNG);
image2.decompress(Bitmap.CompressFormat.PNG, new ByteArrayInputStream(decryptedPNG))

"Is there any algorythm to uncompress a bitmap so I could get the correct pixels?"
---I would very much like to know the answer to this one .I couldn't find a decompress method .currently Im storing the bitmap.compressed version(as a blob field in the database) and would like to continue doing so(i'm aware of the option of storing the whole image before calling compress, dont want to shift to that as yet). I badly need the full image for full image display, the compressed one is perfect for thumbnails and storage, any thoughts welcome.

Related

How to update part of the encrypted data with newly encrypted data?

I need to encrypt an audio file while it is being generated. I am encrypting header with dummy data(because I don't know the actual size of audio data) at the starting and encrypting the audio data on the fly. My plan is to update the header at the end with actual data size of audio file.
But, When I tried to overwrite the encrypted header data with newly encrypted header data of same size by using same key and IV and try to decrypt later, I am getting junk data generated.
Why is this happening even though I am using same key and IV? In the below code I tried to simulate what I am doing. Encrypted file of size 64 bytes generated and decrypted file of size 50 bytes generated.
Without updation: abcdabcdab0123456789012345678901234567890123456789
With header updation: ABCDABCDAB÷‹þ#óMCKL­ZƒÖ^Ô234567890123456789
Expected output: ABCDABCDAB0123456789012345678901234567890123456789
Is this the right approach to achieve partial update of already encrypted data?
protected void Encrypt()
{
byte[] numBytes = {'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9', '0','1','2','3','4','5','6','7','8','9', '0','1','2','3','4','5','6','7','8','9'};
byte[] smallCase = {'a','b','c','d','a','b','c','d','a','b','c','d','a','b','c','d'};
byte[] capitalCase = {'A','B','C','D','A','B','C','D','A','B','C','D','A','B','C','D'};
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1And8BIT");
KeySpec spec = new PBEKeySpec("junglebook".toCharArray(), "Salt".getBytes(), 65536, 256);
SecretKey tmp = null;
tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encryption cipher initialization. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
Log.d("Encryption" + "iv data :", iv.toString());
/*Open two Cipher ouput streams to the same encrypted file*/
FileOutputStream os = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.encrypted");
CipherOutputStream cos = new CipherOutputStream(os,cipher);
FileOutputStream os1 = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.encrypted");
CipherOutputStream cos1 = new CipherOutputStream(os1,cipher);
int offset = 0;
Log.d("Encryption", "Writing cipher text to output file");
//Write 16 bytes header data with smallCase array
cos.write(smallCase, offset, 16);
// write 40 bytes actual data
cos.write(numBytes, offset, 40);
FileOutputStream ivStream = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/iv.dat");
if (ivStream != null) {
Log.d("Encryption", "Writing iv data to output file");
ivStream.write(iv);
}
cos.close();
// Overwrite header data with capitalCase array data
cos1.write(capitalCase, offset, 16);
cos1.close();
ivStream.close();
}catch (Exception e) {
e.printStackTrace();
}
}
protected void Decrypt()
{
byte[] dBytes = new byte[200];
try {
Log.d("Decryption", "Reading iv data ");
File f1 = new File(sdCard.getAbsolutePath()+"/Notes/iv.dat");
byte[] newivtext = new byte[(int)f1.length()];
FileInputStream readivStream = new FileInputStream(sdCard.getAbsolutePath()+"/Notes/iv.dat");
if(readivStream != null) {
readivStream.read(newivtext);
}
// Generate the secret key from same password and salt used in encryption
SecretKeyFactory dfactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1And8BIT");
KeySpec dspec = new PBEKeySpec("junglebook".toCharArray(), "Salt".getBytes(), 65536, 256);
SecretKey dtmp = dfactory.generateSecret(dspec);
SecretKey dsecret = new SecretKeySpec(dtmp.getEncoded(), "AES");
// Initialize dcipher
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, dsecret, new IvParameterSpec(newivtext));
FileInputStream inputStream = new FileInputStream(sdCard.getAbsolutePath()+"/Notes/sample.encrypted");
CipherInputStream cis = new CipherInputStream(inputStream,dcipher);
FileOutputStream os = new FileOutputStream(sdCard.getAbsolutePath() + "/Notes/sample.decrypted");
int b = cis.read(dBytes);
while(b != -1) {
Log.d("Decryption","Bytes decrypted" + b);
os.write(dBytes, 0, b);
b = cis.read(dBytes);
}
cis.close();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
I suggest you update several things:
you are opening multiple outputstreams to the SAME file, which is very strange, the runtime should not allow you to do that. So - write only with a single output if you want any predictable results.
You may read about the mode of operations see the CRT mode uses no padding and allows you to update only a portion of the ciphertext (assuming you use no authenticated encryption). So AES/CTR/NoPadding could solve your problem. (and there should be no extra bytes if you do it correctly)
you can update a portion of the file using the RandomAccessFile and overwrite portion of the ciphertext what is needed.

where to store confidential data like decryption key in android so that it can never be found by hacker

I am creating a app where i need a decryption key to decrypt videos and other data . i need to play videos offline so i need to store the key in the app . So if i use shared pref to store my key or directly in the string it can be easily hacked . and my data will not be secured any more . So where should i keep my key so that no one can find my key on decompiling the app or rooting phone to get to the key.
I am thinking about where should i store data
sqlite
shared prefrence
text file
string file
static variable
If the decryption key is at any point accessible to the application, it's accessible to any potential evildoer. This is a fact.
If your requirements are:
Videos encrypted, i.e. only playable through your app
Playable offline
Secured so you can't decrypt or view the videos through other means
Then what you have are impossible requirements.
There is a way to secure your encryption key in NDK.
Step 1
private static 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 static 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;
}
Step 2
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
byte[] keyStart = "encryption key".getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
// decrypt
byte[] decryptedData = decrypt(key,encryptedData);
Step 3
static {
System.loadLibrary("library-name");
}
public native String getSecretKey();
Step 4
And save in a file using NDK the following function:
Java_com_example_exampleApp_ExampleClass_getSecretKey(
JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "mySecretKey".");
}
Step 4
Now we can easily retrieve our key and use it to encrypt our data.
byte[] keyStart = getSecretKey().getBytes();
Reference : How to store the Credentials securely in Android
Create your own directory under the main Android default directory
In your directory create multiple directories and hide the file in one of it.
Other than the that there is no really secure way

Java and Javascript Blowfish

I need to send some data encrypted with Blowfish from a java-based server to a client. I can successfully encrypt data but I can't decrypt it on the client side.
Here is my java code:
byte[] kd = key.getBytes("UTF-8");
SecretKeySpec ks = new SecretKeySpec(kd, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, ks);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
String str = new String(encrypted, "UTF-8");
As for js library I decided to use this one.
out = blowfish.decrypt(code, skey, {cipherMode: 1, outputType: 0})
As a result I get some strange characters. What's wrong with my code?
UPD:
This code works perfectly:
byte[] kd = key.getBytes("UTF-8");
SecretKeySpec ks = new SecretKeySpec(kd, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, ks);
byte[] encrypted = cipher.doFinal(text.getBytes("UTF-8"));
String str = new String(Base64.encodeBase64(encrypted), "UTF-8");
JS:
out = blowfish.decrypt(code, skey, {cipherMode: 0, outputType: 0})
Sending text with \u0000 bytes in it to a browser can lead to all kinds of odd problems. That's why you should encode the data BASE64, send it to the client and then decode it locally.
Another issue is new String(encrypted, "UTF-8"); since the encoded byte array will contain illegal UTF-8 sequences. Try new String(encrypted, "iso-8859-1"); instead, it's a 1:1 encoding for arbitrary bytes. But again, the 0 bytes could confuse some component in between.

Adding the IV to the encrypted byte array as final block

I am trying to add my 16-bit IV used to encrypt my data as the final block in the byte array used to hold the encrypted data. I want to do this obviously for the decryption part so that I can use a completely random IV for each encryption/decryption call. I have the following for testing purposes:
public static String encrypt(String plainText) throws Exception {
encryptionKey = new SecretKeySpec(eKey.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, initialisationVector);
byte[] eDataAndIv = appendIvToEncryptedData(cipher.doFinal(plainText.getBytes("UTF-8")), initialisationVector.getIV());
return bytesToHexString(eDataAndIv);
}
public static String decrypt(String hexEncoded) throws Exception {
byte[] decodedBytes = hexStringToBytes(hexEncoded);
ArrayList<byte[]> al = retreiveIvFromByteArray(decodedBytes);
byte[] eData = al.get(0);
byte[] iv = al.get(1);
cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv));
return reconstructedPlainText(cipher.doFinal(eData));
}
private static byte[] appendIvToEncryptedData(byte[] eData, byte[] iv) throws Exception {
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(eData);
os.write(iv);
return os.toByteArray();
}
private static ArrayList<byte[]> retreiveIvFromByteArray(byte[] dataPlusIv) {
ByteArrayOutputStream iv = new ByteArrayOutputStream(16);
ByteArrayOutputStream eData = new ByteArrayOutputStream();
iv.write(dataPlusIv, dataPlusIv.length - 16, 16);
eData.write(dataPlusIv, 0, dataPlusIv.length - 16);
ArrayList<byte[]> al = new ArrayList<byte[]>();
al.add(eData.toByteArray());
al.add(iv.toByteArray());
return al;
}
The list of steps for encryption are:
Create IV
Encrypt data
Append IV to end of encrypted data byte array
Encode byte array using hex
The list of steps for decryption are:
Decode hex
Break encrypted data and IV from byte array
Decrypt data using IV
What I have works, but I guess what I want to know is, is there a "better" way of doing this? By that I mean, is there a set, or simpler, way to do this using the Cipher* types? I can't find them.
Thanks.
Well you can certainly avoid the long-winded retreiveIvFromByteArray code:
public static String decrypt(String hexEncoded) throws Exception {
byte[] decodedBytes = hexStringToBytes(hexEncoded);
int ivIndex = decodedBytes.length - 16;
cipher.init(Cipher.DECRYPT_MODE, encryptionKey,
new IvParameterSpec(decodedBytes, ivIndex, 16));
return reconstructedPlainText(cipher.doFinal(decodedBytes, 0, ivIndex));
}
Is that the sort of thing you were thinking of?
Likewise for the appendIvToEncryptedData you could just create a new byte array of the appropriate length, and use System.arraycopy twice.

Decrypt AES encrypted file in java

I have a file encrypted with java application using AES. I also have a key file was encrypted with. But i can't understand how to use the key to decrypt file. Most tutorials and examples create temporary random key, encrypt file and decrypt it in one place.
So, question is how to specify a key which have to be used for decryption?
EDIT:
Samples i found use following code to generate key. I have no idea where i can use my key here.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey key = kgen.generateKey();
Just to summarise my comments to Lucifer's answer.
If you don't know what padding was used to encrypt, then decrypt with 'no padding' set. That will decrypt everything, including the padding, and won't throw an error because of mismatched padding.
When you have decrypted the cyphertext, have a look at the last block of the output and see what padding was used. Different paddings leave different byte patterns, so it is usually easy enough to tell.
Set your decryption method to expect the correct type of padding, and it will be automatically removed for you.
The answer could be simply to put the key data as bytes into a SecretKeySpec like this:
SecretKeySpec aesKey = new SecretKeySpec(myKeyData, "AES");
Note that SecretKeySpec implements the Key interface, so you can use it directly in a Cipher.init() method. So there is no SecretKeyFactory needed, which you would use otherwise.
Please try following methods, if might helpful for you.
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
Complete example of encrypting/Decrypting a huge video without throwing Java OutOfMemoryException and using Java SecureRandom for Initialization Vector generation. Also depicted storing key bytes to database and then reconstructing same key from those bytes.
https://stackoverflow.com/a/18892960/185022

Categories