To keep this short, are there known issues when passing results of gzencode (or other non-text data) to mcrypt_encrypt functions?
Details:
Basically I have an issue where encryption/decryption is working for plain text but I get errors unzipping if I pass compressed data to the encryption function, then decrypt and unzip.
So in PHP, I'm passing results of gzencode() to the encrypt function. Then I base64 encode for showing results on a web service web page. Then in a Java app I'm decoding base64, decrypting, and unzipping using GZIPInputStream. I get errors in the last step.
But everything works fine if I skip the compression step (just pass plain text to the encrypt function). Everything also works fine if I skip encryption and just do compression. So those functions seem to work fine on both PHP and Java sides if I don't combine them.
public static function encrypt($str,$key,$iv) {
$str=Crypto2::pkcs5Pad($str,mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
$encrypted=mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key,$str,MCRYPT_MODE_CBC,$iv);
return $encrypted;
}
public static function pkcs5Pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
$padded=$text . str_repeat(chr($pad), $pad);
return $padded;
}
Java functions:
public static byte[] decrypt(byte[] inputbuffer) throws Exception {
Key key = new SecretKeySpec(keybyte, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, key, ivSpec);
c.getBlockSize();
System.out.println("Block size="+c.getBlockSize());
int outlen = c.getOutputSize(inputbuffer.length);
System.out.println("Output length will be:"+outlen);
byte[] result=c.doFinal(inputbuffer);
return result;
}
public static byte[] decodeBase64(String data) throws IOException{
BASE64Decoder decoder = new BASE64Decoder();
byte[] decodedBytes = decoder.decodeBuffer(data);
return decodedBytes;
}
public static void unzipPrint(byte[] data) throws Exception{
InputStream is=new GZIPInputStream(new ByteArrayInputStream(data));
int ch2;
while((ch2=is.read())!=-1) {
System.out.print((char)ch2);
}
}
So if I do this in PHP:
base64_encode(encrypt(gzencode($plain_text)));
and this in Java
unzipPrint(decrypt(decodeBase64(data)));
I get the dreaded: "java.util.zip.ZipException: oversubscribed dynamic bit lengths tree" during the unzip phase.
Again, if I skip the compression/decompression steps at both ends everything is fine. And if I skip encryption at both ends then compression/decompression works fine.
EDIT: Ok weird, but after checking byte by byte the resulting byte array of the compressed data (after decoding base64 and decryption), I found a SINGLE byte that was off (compared to the original PHP byte array) by a value of 1. It was byte number 14 (index 13 in Java) and it had a value of 110 instead of 111. I have absolutely no idea how this could be the case.
So if I change that single byte to from 110 to 111, then I can successfuly use GZIPOutputStream to uncompress the data.
So I know what is wrong but not why.
EDIT 2: This is SOLVED ->Thanks to comment by Owlstead I double checked the IV values and found that there was minor descrepancy between the php and java code. How this can lead to only one byte of difference in the resulting decrypted data I have no idea.
That was one wasted day over a single 0x13 instead of 0x12 in my IV.
You should check the IV, as it may change only the first block of cipher text, which holds the ZIP header.
(to close the question, glad you got it solved, any day that you solve an issue is not one wasted in my opinion :) )
Related
I'm having some issued decrypting data using AES/CBC/PKCS5Padding in Java.
I am encrypting two values A and B and then data from a file. The encrypted values are written in file in the described sequence.
When decrypting the bytes for the respective pieces are properly located (confirmed via debugging) and the inputs to the decrypting functions are correct, no padding issues there.
Encryption code:
byte[] iv = {..........};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, fileKey, ivParameterSpec);
byte[] encryptedA = cipher.update(A);
byte[] encryptedB = cipher.update(B);
while( true){
if( blocks > 1 ) {
encrypted = cipher.update(data);
}
else {
encrypted = cipher.doFinal(data);
}
blocks--;
//write bytes to file
}
When encrypting I can see that the vector inside the Cipher is updated after every update() as expected (the last ciphertext is the vector for subsequent updates. for example encryptedA is the vector of the cipher at the time I call update(B)
Decryption code
Cipher cipherB = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherB.init(Cipher.DECRYPT_MODE, fileKey, ivParameterSpecB);
byte[] decryptedA = cipherB.update(encryptedA);
byte[] decryptedB = cipherB.update(encryptedB);
while( true){
if(blocks > 1 ) {
decrypted = cipherB.update(encryptedBytes);
}
else {
decrypted = cipherB.doFinal(encryptedBytes);
}
blocks--;
//write bytes to file
}
What's happening at this point very odd.
The first call to cipherB.update(encryptedA) does nothing at all. It returns an empty array and doesn't update the vector inside the cipher.
The second call to cipherB.update(encryptedB) returns the value I was expecting from the previous call ( cipherB.update(encryptedA) which is the original value: A) and sets the vector to the value of encryptedA
Can you spot anything wrong in my approach? Are there any known issues in AES/CBC/PKCS5Padding when using the default SunJCE provider?
Update:
After reading some of the comments, let me add some extra clarification
the conditions are around the blocks used for encrypting the payload. the first condition is simple while(true) and the second one is if(blockCount > 1). there's a block counter that's been decreased in every loop. Code updated
If A and B are omitted from the encryption/decryption, the file data is properly been decrypted
I tried decrypting the direct outcome of the encryption
for example:
cipherB.update(cipher.update(A))
but I'm still getting the same empty array instead of A
I cannot rely on running updates twice, after getting back B by running cipherB.update(encryptedB) something is going wrong and the file data decryption is affected by the vector in the cipher.
The data I get back is something like
(12 random bytes)Lorem Ipsum etc
The plaintext and ciphertext chunks do not correspond to each other 1 to 1. You need to capture the full output in a byte[] and unpack it yourself.
The AES/CBC/PKCS5Padding mode works on blocks, so the update will return you only "filled" blocks and doFinal will return you the rest. AES is using 128 bit block, so the update method returns only multiples of 16 bytes. As well there is a last block with padding. So your assumption cipherB.update(cipher.update(A)) doesn't work in this case.
I'm not really following what are you trying to achieve by the condition if(blocks > 1 )
You could use following code to process cipher blocks (simplified version):
byte[] decrypted = null;
byte[] buffer = new byte[BUFFER_SIZE];
InputStream in = ..;
for (int bytesRead=in.read(buffer); bytesRead>=0; bytesRead=in.read(buffer)) {
decrypted = cipher.update(buffer, 0, bytesRead);
// process the chunk
}
decrypted = cipher.doFinal();
// process the chunk
this way it doesn't matter if you process a single block or not.
There are as well "stream ciphers" or modes when the update method directly returns encrypted or decrypted chunk regardless of the input size, such as AES/CTR mode or Salsa20 cipher
I try to decrypt an encrypted data that I receive from a web service.
The encryption is done using AES 128.
I use the following code to decrypt the data:
public static String decrypt(String strToDecrypt)
{
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); //AES/CBC/PKCS7Padding
SecretKeySpec secretKey = new SecretKeySpec(AppConstants.AESEncryptionKey.getBytes("UTF8"), "AES");
int blockSize = cipher.getBlockSize();
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[blockSize])); //new IvParameterSpec(new byte[16])
byte decBytes[] = cipher.doFinal(Base64.decode(strToDecrypt, 0));
// byte decBytes[] = cipher.doFinal(Base64.decodeBase64(strToDecrypt));
String decStr = new String(decBytes);
System.out.println("After decryption :" + decStr);
return decStr;
}
catch (Exception e)
{
System.out.println("Exception in decryption : " + e.getMessage());
}
return null;
}
At
cipher.doFinal()
I got the following Exception:
javax.crypto.badpaddingexception pad block corrupted
I went through my post but ended up with no solution. I am badly stuck over here.
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG","Crypto");
works perfectly
Note: This code works only on devices up to Android 6. Starting with Android 7.0 the "Crypto" provider has been removed, therefore this code will fail.
AES keys should consist of random data. If you store them as a String then you are likely to loose information, especially if you use encodings such as UTF-8. Your line:
AppConstants.AESEncryptionKey.getBytes("UTF8")
Makes it likely that you've lost data during conversion to/from a string. Use hexadecimals instead if you require a string, or simply store the key as a byte array.
Note that this answer doesn't indicate any security related hints. In general you only want to derive keys or store them in containers. You don't want to use CBC over an insecure channel either.
In my case issue is came because encrypted key and decrypted key both are different, when I check both key with same value then issue is not came
So I have these large files (6GB+) that I need to decrypt on a 32 bit computer. The general procedure that I used previously was to read the entire file in memory, then pass it on to the decrypt function and then write it all back to a file. This doesn't really work due to memory limitations. I did try passing the file in parts to the decrypt function but it seems to mess up around the boundaries of where I break up the file before sending it to the decrypt function.
I've tried breaking up the file in parts relative to key size but that doesnt seem to matter. I tried a byte array of size 2048 as well as a byte aray of size 294 thinking that might be the special boundary but, no luck. I can see parts of the file correctly decrypted but parts which are total gibberish.
Is it just NOT POSSIBLE to decrypt the file in chunks? If there is a way, then how?
Here is my decryption function / my attempt to decrypt in parts.
private Path outFile;
private void decryptFile(FileInputStream fis, byte[] initVector, byte[] aesKey, long used) {
//Assume used = 0 for this function.
byte[] chunks = new byte[2048]; //If this number is greater than or equal to the size of the file then we are good.
try {
if (outFile.toFile().exists())
outFile.toFile().delete();
outFile.toFile().createNewFile();
FileOutputStream fos = new FileOutputStream(outFile.toFile());
OutputStreamWriter out = new OutputStreamWriter(fos);
IvParameterSpec spec = new IvParameterSpec(Arrays.copyOfRange(initVector, 0, 16));
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, spec);
int x;
while ((x = fis.read(chunks, 0, chunks.length)) != -1) {
byte[] dec = cipher.doFinal(Arrays.copyOfRange(chunks, 0, x));
out.append(new String(dec));
}
out.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
LOG.error(ExceptionUtils.getStackTrace(e));
}
}
Consider using Cipher#update(byte[], int, int, byte[], int) instead of doFinal() for multipart operations. This will take care of part boundaries for you.
The last part of the deciphered data can be obtained by calling the doFinal(byte[] output, int outputOffset) method.
I have a question regarding encrypting and decrypting a string
I have to send a encrypted string over the network.(an android app is the client) this is what i did so far
byte[] input = getByteArray(filePath);//get the message stored in a file as a byte array
by going through some tutorial i managed to get the String message to a byte array and
encrypted it using javax.crypto
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
encrypted msg is retrived as a byte array
byte[] encrypted
i even managed to decrypt it using the reverse method and get the message again
but my problem comes when i try to convert this encrypted byte array to strings (to pass it over the network)
and then reconvert it to a byte array
i tryed this
String encryptedStrn = new String(encrypted); // convert to string
when i convert it to the byte array by
byte[] enc = encryptedStrn.getBytes();
and use this enc array to decrypt but the output does not come correctly.
Have i missed some basic stuff regarding converting. Please help me.
thanks in advance
As CodeInChaos wrote in a comment, you shouldn't use the String(byte[]) constructor to create a string from opaque binary data. The string constructors are intended for text data which has been encoded using an encoding like ASCII, UTF-8 etc. Opaque binary data such as the result of encryption, or an image file, is not encoded text data in the same way, so you end up losing information.
You should use base64 instead, which encodes any binary data into ASCII. There are various 3rd party libraries for this, including a good public domain one. Alternatively, on Android you can just use the Base64 class.
Additionally, even when you are encoding or decoding real text, you shouldn't use String.getBytes() and the String(byte[]) constructor anyway - they use the platform default encoding, which is almost always the wrong choice. Instead, you should use the overloads which explicitly take a CharSet or the name of a character encoding. UTF-8 is typically a good encoding to use if you're able to control both ends - if you're only controlling one end, you need to know which encoding the other end is expecting.
You should base64-encode the cipher text. Don't just convert it to a String. String is not a container for binary data.
public string EncryptUser(string userID)
{
using (var cryptoProvider = new DESCryptoServiceProvider())
using (var memoryStream = new MemoryStream())
using (var cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(DESKey, DESInitializationVector), CryptoStreamMode.Write))
using (var writer = new StreamWriter(cryptoStream))
{
writer.Write(userID);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
}
public string DecryptUserID(string userID)
{
using (var cryptoProvider = new DESCryptoServiceProvider())
using (var memoryStream = new MemoryStream(Convert.FromBase64String(userID)))
using (var cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateDecryptor(DESKey, DESInitializationVector), CryptoStreamMode.Read))
using (var reader = new StreamReader(cryptoStream))
{
return reader.ReadToEnd();
}
}
I have an encrypt method in Java.
public static String encrypt(String orignal){
SecretKeySpec key = new SecretKeySpec(keyString.getBytes(), "AES");
IvParameterSpec initalVector = new IvParameterSpec(initialVectorParam.getBytes());
try{
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
/////////////// encrypt /////////////////
cipher.init(Cipher.ENCRYPT_MODE, key, initalVector);
Log.d("AES", "oriByte: "+ orignal.getBytes());
int length = orignal.length();
for(int i=0; i<length; i++){
}
byte[] test = cipher.doFinal(orignal.getBytes());
Log.d("AES", "encByte: "+ test);
return bytes2Hex(test);
}catch (Exception e) {
Log.d("AES", "Encrypt Exception:"+orignal);
return "";
}
}
For compatibility with PHP, I use "AES/CFB8/NoPadding" options.
In PHP: $sCipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $sKey, $sStr, MCRYPT_MODE_CFB, $sIV);
And I have a Objective-c Cipher code from here.
https://gist.github.com/838614
I found that there is no IvParameterSpec in Objective-c Cipher like java.
Besides, the getBytes method returns a different value with java.
(I think this is because java uses different encoding way.)
So, how can I apply IvParameterSpec in Objective-c.
And is there any way to get 'getBytes' value like java in Objective-c?
For the initialization vector, see line 24 in your pastie:
NULL /* initialization vector (optional) */,
That's where you would pass your IV.
But without knowing the string encoding the Java code used to create the bytes used as the IV, you won't be able to seed the encryption properly to decrypt the data, even if you know what the string displays to the screen as. Put another way, just because the IV looks like "abc123" doesn't mean the bytes Java is writing to the IV buffer are going to be the same bytes you'll get if you strncpy() from a C character literal buffer. You have to agree on the encoding as part of the protocol for handling the data.
You will also need to agree on a key size. Your Java code does not specify how many bits are in the AES key.
Once you've got that worked out, you'll want to use a call like:
const void *key = /* KEY BYTES */;
const void *iv = /* IV BYTES */;
const void *text = /* CIPHER TEXT */;
size_t textlen = /*...*/;
size_t outlen = 0;
(void)CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0/*use CBC mode*/,
key, kCCKeySizeAES128, iv,
text, textlen,
&text, textlen, &outlen);
The plaintext will be written over the ciphertext, assuming all goes well. The amount o data written to text during decryption will be stored in outlen. Error checking is your responsibility; the header is well-commented.
Once you have the data, you'll want to slurp it into an NSString with the correct encoding (+[NSString initWithData:encoding:] would work), and then you have a string you can work with from Obj-C like any other string.