Extra character in decrypted file from java using aes - java

I have written code in vb.net to encrypt a file from a memory stream. I also decrypt the file as well as copy the memory stream to a file to assure encryption/ decryption works. My vb solution works.
However my need is to decrypt using Java. When I decrypt my file, I always get an extra "?" character at the very beginning of the file, but other than that the resullts are perfect. Has anyone seen anything like this before? I must admit, my results are from using only one set of data, but I've encrypted it twice using new keys and vectors both times.
A few details. I'm using AES, PKCS7 padding in vb, and PKCS5 padding in Java. The file can be of arbitrary length. Any help is appreciated.
I am posting this from my phone, and don't have the code handy. I can add it tomorrow. I'm just hoping that this description rings a bell with someone.
Thanks,
SH
When I wrote to the MemoryStream in VB, I declared a StreamWriter like so:
Writer = New IO.StreamWriter(MS, System.Text.Encoding.UTF8)
Here's my VB.NET encryption function.
Public Shared Function WriteEncryptedFile(ms As MemoryStream, FileName As String) As List(Of Byte())
Try
Dim original() As Byte
Dim myAes As System.Security.Cryptography.Aes = Aes.Create()
myAes.KeySize = 128
myAes.Padding = PadMode
Dim keys As New List(Of Byte())
keys.Add(myAes.Key)
keys.Add(myAes.IV)
original = ms.ToArray
Dim encryptor As ICryptoTransform = myAes.CreateEncryptor(myAes.Key, myAes.IV)
Using FileEncrypt As New FileStream(FileName, FileMode.Create, FileAccess.Write)
Using csEncrypt As New CryptoStream(FileEncrypt, encryptor, CryptoStreamMode.Write)
csEncrypt.Write(original, 0, original.Length)
csEncrypt.FlushFinalBlock()
FileEncrypt.Flush()
FileEncrypt.Close()
csEncrypt.Close()
End Using
End Using
Return keys
Catch e As Exception
MsgBox("Error during encryption." & vbCrLf & e.Message)
End Try
Return Nothing
End Function
And here's the Java decryption:
public static void DecryptLIGGGHTSInputFile(String fileIn, String fileOut, String base64Key, String base64IV) throws Exception
{
// Get the keys from base64 text
byte[] key = Base64.decodeBase64(base64Key);
byte[] iv= Base64.decodeBase64(base64IV);
// Read fileIn into a byte[]
int len = (int)(new File(fileIn).length());
byte[] cipherText = new byte[len];
FileInputStream bs = new FileInputStream(fileIn);
bs.read(cipherText, 1, len-1);
System.out.println(cipherText.length);
System.out.println((double)cipherText.length/128);
bs.close();
// Create an Aes object
// with the specified key and IV.
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Encrypt the message.
SecretKey secret = new SecretKeySpec(key, "AES");
/*
cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
cipherText = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
System.out.println(cipherText);
*/
cipher.init(Cipher.DECRYPT_MODE, secret , new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(cipherText), "UTF-8");
System.out.println(plaintext.length());
FileWriter fw = new FileWriter(fileOut);
fw.write(plaintext);
fw.close();
}

It was a BOM problem. When I created the MemoryStream with VB, I initialized it in UTF-8 encoding. The very first character in my file boosted the size and position of the stream from 0 bytes to 4 bytes, when it should have only been one. The solution was to create an encoding based on UTF-8 without Byte Order Marks, like so:
Dim UTF8EncodingWOBOM As New System.Text.UTF8Encoding(False) 'indicates to omit BOM
Writer = New IO.StreamWriter(MS, UTF8EncodingWOBOM)
I read here that there are frequently issues with encoding incompatibilities between platforms due to the presence or lack of byte order mark, as it is neither recommended or required. It's not right to use one, it's not wrong to use one. You basically have to find a way to deal with them. A plethora of other articles and postings suggested different ways to do it. The gist was, either identify them and deal with them if they exist. Since I have control of both the writing and the reading, it makes about as much sense to do away with them entirely.
SH

Related

Writing CipherInputStream to a text file

I'm trying to decrypt an encrypted data which is stored in a text file. I use the same key and IV to encrypt and decrypt, and transfer it via configuration file.
When I print CipherInputStream to the console I do get some content, but when I try to write it to a text file I don't get any content in it.
This is the piece of code refferes to my problem:
File encryptedData = new File("C:\\Users\\Victoria\\Desktop\\encryptedData.txt");
File decryptedData = new File("C:\\Users\\Victoria\\Desktop\\decryptedData.txt");
FileInputStream inputStream = new FileInputStream(encryptedData);
byte[] inputBytes = new byte[(int) decryptedData.length()];
inputStream.read(inputBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
FileOutputStream outputStream = new FileOutputStream(decryptedData);
System.out.println("cipherInputStream: " + cipherInputStream);
// Writing the decrypted content to an output file
byte[] buff = new byte[1024 * 10];
int length;
while ((length = cipherInputStream.read(buff)) > 0) {
outputStream.write(buff, 0, length);
}
bufin.close();
outputStream.close();
cipherInputStream.close();
Any solutions?
Thank you!
Encrypted data which is stored in a text file
This is already a contradiction in terms. Encrypted data is binary, not text, and should not be stored in files with the .txt extension.
byte[] inputBytes = new byte[(int) decryptedData.length()];
This line of code is meaningless. You don't yet know how long the decrypted data will be. The decrypted file may not even exist, in which case this will produce a zero length array; or it may be different from what is about to be produced, in which case it is the wrong length.
inputStream.read(inputBytes);
Remove this line and the one before it.
It reads into an array which is at best sized to the size of the decrypted data, which is the wrong size for encrypted data, and at worst is just the wrong size, or even zero length, as shown above.
It reads the input probably until that wrongly sized buffer is full, and you then (a) completely ignore the data read and (b) attempt to read the same stream further, which will fail in the decryption loop, or at best produce incorrect output, as you may not be decrypting all the data.
When I print CipherInputStream to the console I do get some content
No you don't. You get a piece of data of the general form CipherInputStream#0011223344, which is just the result of calling CipherInputStream.toString(), which does not contain any 'content'.

'javax.crypto.BadPaddingException' while using cipherInputStream

I'm writing a program to encrypt and decrypt data.
for encrypting,
I created a symmetric key using keyGenerator.
I transferred the key to the cipher, and created a string version of the key:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
in order to store it in a configuration file (so I can retrieve the key in the decrypt function).
Now, in the decrypt function I need to get that string back to key format, so I can send it as a parameter to the cipher in dercypt mode.
I convert it back to key this way:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
Key newkey = new SecretKeySpec(keyBytes,0,keyBytes.length, "AES");
And I transffer it to the cipher and write the output (the decrypted data) using CipherInputStream:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(encryptedBytes), cipher);
ArrayList<Byte> decryptedVal = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
decryptedVal.add((byte) nextByte);
}
byte[] bytes = new byte[decryptedVal.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = decryptedVal.get(i);
}
String decryptedData = new String(bytes);
cipherInputStream.close();
System.out.println("decryptedData: " + decryptedData);
I get this error:
Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
So I suspect that there might be a problem with the way I treat the key.
Any suggestions? help would be appreciated!
I think you have not sent IV to decryption function. For decryption in CBC mode, you must provide an IV which is used in encryption process.
Update:
IV will affect only first block in CBC decryption mode. So my answer may affect the unpadding if your data is less than 1 block. It will just change the decrypted plaintext of the first block otherwise.
Of course you get this error: first you apply base 64 encoding:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
and then you use character-encoding to turn it back into bytes:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
which just keeps be base64 encoding, probably expanding the key size from 16 bytes to 24 bytes which corresponds with a 192 bit key instead of a 128 bit key. Or 24 bytes key to a 32 bytes key of course - both seem to work.
To solve this you need to use Base64.getDecoder() and decode the key.
Currently you get a key with a different size and value. That means that each block of plaintext, including the last one containing the padding, will decrypt to random plaintext. As random plaintext is unlikely to contain valid padding, you will be greeted with a BadPaddingException.
Reminder:
encoding, e.g. base 64 or hex: encoding bytes to a text string
character-encoding, e.g. UTF-8 or ASCII: encoding a text string into bytes
They are not opposites, that would be decoding and character-decoding respectively.
Remarks:
yes, listen to Ashfin; you need to use a random IV during encryption and then use it during decryption, for instance by prefixing it to the ciphertext (unencrypted);
don't use ArrayList<Byte>; that stores a reference to each separate byte (!) - use ByteArrayOutputStream or any other OutputStream instead;
you can better use a byte buffer and use that to read / write to the streams (note that the read function may not fill the buffer, even if at the start or in the middle of the stream) - reading a single byte at the time is not performant;
lookup try-with-resources for Java;
using a KeyStore may be better than storing in a config file;
GCM mode (AES/GCM/NoPadding) also authenticates data and should be preferred over CBC mode.

Java AES Encryption (working) and Python Decryption (not working)

I'm trying to encrypt a message in Java and decrypt it in Python. Unfortunately i'm just starting with python and am not able to get the decryption working.
That's my Java Code:
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = sr.generateSeed(16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKey aesKey = keygen.generateKey();
//save byte array in text file to recreate key later
byte[] encodedKey = aesKey.getEncoded();
new File("myPath\\AESKey.txt");
FileOutputStream fos = new FileOutputStream("myPath\\AESKey.txt");
//save AesKey in first 16 bytes and Initial Vector in next 16 bytes
fos.write(encodedKey);
fos.write(iv);
fos.close();
String secretText = "Hello cryptography";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
byte[] encrypted = cipher.doFinal(secretText.getBytes());
BASE64Encoder myEncoder = new BASE64Encoder();
String encodedSecretText = myEncoder.encode(encrypted);
new File("myPath\\encodedSecretText.txt");
FileOutputStream fos2 = new FileOutputStream("myPath\\encodedSecretText.txt");
fos2.write(encodedSecretText.getBytes());
fos2.close();
I was able to decrypt the message with java, but not with python. I hope someone can show me how to do this.i copied the part with padding from another answer and assume that's the problem.
I get the message: ord() expected string of length 1, but int found.
Python:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
#read bytes of aesKey
file = open("myPath/AESKey.txt","rb")
aesKey = file.read(16)
iv = file.read(16)
file.close()
sec = open("myPath/encodedSecretText.txt")
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
class AESCipher:
def __init__( self, key ):
self.key = key
def encrypt( self, raw ):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( self, enc ):
enc = base64.b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] ))
aes = AESCipher(aesKey)
print(aes.decrypt(encodedSecretText))
Thanks for any hint.
You're calling ord on an integer. Which is obviously illegal. The whole point of ord is that you give it a Unicode character, as a string, and it gives you back the numerical value of the code point.
So, why do you have a number? Well, I'm not sure what you expected to have, but let's look at what you actually have. If s[-1] is an integer, then s is some kind of sequence of integers. And s is the result of calling cipher.decrypt(). As the documentation for that function says, it returns a byte string. This isn't a specific type, just a description of a type—you can find out what the actual return value is with some basic debugging, maybe like this:
cipher = AES.new(self.key, AES.MODE_CBC, iv )
plaintext = cipher.decrypt(enc[16:])
print(type(plaintext), repr(plaintext))
return unpad(plaintext)
But I'm going to guess that it's a bytes object, which (quoting from http://docs.python.org/3/library/functions.html#bytes) …
is an immutable sequence of integers in the range 0 <= x < 256.
So, s[-1] is an integer in the range [0, 256). Hence the error.
So, what should you be doing instead? Well, why are you trying to call ord? You have a byte. Presumably what you want is a byte. So… just don't call anything there.
Meanwhile, there's at least one other serious error in your code:
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
As pasted, this will raise an IndentationError. And if you indent both the second and third lines, you'll get an error for reading from a closed file. So presumably you want to indent just the second one. In which case, what you're doing is going through all of the lines, stripping the trailing whitespace off each, and then doing nothing with them. At the end of the loop, encodedSecretText holds the last line of encoded text, and all of the other lines are long forgotten and unrecoverable.
If you want to read all of the text into a list of lines, you will want something like this:
encodedSecretText = []
for line in sec:
encodedSecretText.append(line.rstrip())
Or, if you want to read it into one big string, with the newlines removed, you could do the above and then encodedSecretText = b''.join(encodedSecretText), or just do skip the whole loop and do encodedSecretText = sec.read().replace(b'\n', b'').

Encrypting/decrypting with RSA Bouncy Castle not working properly

I'm trying to encryt/decrypt using RSAEngine library at bouncy castle, with a 2048 bit length key. I'm able to create the keys, store in different files and get from the files, but when I decrypt an image it makes something that I don't know that the file decrypted is not shown correctly.Files are created correctly,and I think the problem is at processBlock method while encrypting and/or decrypting.The code is the following to encrypt:
InputStream clearTextFile;
FileOutputStream textFileProcessed=new FileOutputStream(fileName);
//getKey is a method I implemented and works correctly
RSAKeyParameters key=getKey(keyFileName);
RSAEngine rsaEngine=new RSAEngine();
rsaEngine.init(true,key);
clearTextFile=new FileInputStream(nameClearTextFile);
byte[] bytesReaded;
int nBytesReaded;
int inputBlockSize=rsaEngine.getInputBlockSize();
do
{
bytesReaded = new byte[inputBlockSize];
nBytesReaded=clearTextFile.read(bytesReaded);
if(nBytesReaded>-1)
{ //This is for the last block if it's not 256 byte length
if(nBytesReaded<inputBlockSize)
{
byte[] temp=new byte[nBytesReaded];
for(int i=0;i<nBytesReaded;i++)
{
temp[i]=bytesReaded[i];
}
byte[] encryptedText=rsaEngine.processBlock(temp,0,nBytesReaded);
textFileProcessed.write(encryptedText);
}
else
{
byte[] encryptedText=rsaEngine.processBlock(bytesReaded,0,inputBlockSize);
textFileProcessed.write(encryptedText);
}
}
}while(nBytesReaded>-1);
textFileProcessed.flush();
textFileProcessed.close();
textFileProcessed.close();
And to decrypt:
InputStream encryptedTextFile=new FileInputStream(nameOfFile);
OutputStream decryptedTextFile=new FileOutputStream(nameOfFile);
RSAKeyParameters key=getKey(nameKeyFile);
RSAEngine rsaEngine=new RSAEngine();
rsaEngine.init(false,key);
byte[] bytesReaded;
int nBytesReaded;
int inputBlockSize=rsaEngine.getInputBlockSize();
do
{
bytesLeidos = new byte[inputBlockSize];
nBytesReaded=encryptedTextFile.read(bytesReaded);
if(nBytesReaded>-1)
{
byte[] decryptedText=rsaEngine.processBlock(bytesReaded,0,inputBlockSize);
decryptedTextFile.write(decryptedText);
}
}while(nBytesReaded>-1);
decryptedTextFile.flush();
decryptedTextFile.close();
encryptedTextFile.close();
Thanks in advance
RSAEngine does not add padding, you will lose any leading zeros in your data blocks as a result. You need to use one of the encoding modes available as well.
I'd recommend using a symmetric key algorithm as well and just using RSA to encrypt the symmetric key. It will be a lot faster, and depending on your data, safer as well.
Regards,
David
I think you need to change this line:
if(nBytesReaded>1)
to this
if(nBytesReaded>-1)
And change this in the decypt part, maybe:
rsaEngine.init(false,clave);
to this
rsaEngine.init(false,key);
But there may be more. You aren't encrypting the whole input if the last block isn't full size.

BadPaddingException: pad block corrupted

I am trying to decrypt a file in Java which was encrypted in C# using Rijndael/CBC/PKCS7. I keep getting the following exception:
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at AESFileDecrypter.decrypt(AESFileDecrypter.java:57)
when the doFinal(inpbytes) method is called by the web server for the first byte[]. I am guessing this is a problem with the key or IV. I have the encrypted files on my file system for testing. Is there anything that anyone can see glaringly wrong with my code below?
***keyStr is base64 encoded
public AESFileDecrypter(String keyStr){
try {
Security.addProvider(new BouncyCastleProvider());
convertIvParameter();
key = new sun.misc.BASE64Decoder().decodeBuffer(keyStr);
//use the passed in Base64 decoded key to create a key object
decryptKey = new SecretKeySpec(key, "AES");
//specify the encryption algorithm
decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
//make a parameter object for the initialization vector(IV)
IvParameterSpec ivs = new IvParameterSpec(_defaultIv);
//initialize the decrypter to the correct mode, key used and IV
decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, ivs);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void convertIvParameter() {
int[] iv = new int[] {11, 190, 165, 33, 68, 88, 11, 200, 245, 35, 68, 23, 60, 24, 223, 67};
_defaultIv = new byte[16];
for(int x = 0; x < _defaultIv.length; x++) {
_defaultIv[x] = (byte)iv[x];
}
}
public void decryptUpdate(byte[] inpBytes) throws Exception {
//decrypt the byte passed in from the web server
decryptCipher.update(inpBytes);
}
public byte[] decryptFinal() throws Exception {
//decrypt the byte passed in from the web server
return decryptCipher.doFinal();
}
//sends bytes to the client for diaply
private void sendBytes(FileInputStream fis, OutputStream os)throws Exception {
//set the buffer size to send 4k segments of data
aesFileDecrypter = new AESFileDecrypter(<Insert Key string here>);
byte[] buffer = new byte[4096];
int bytes = 0, totalBytes = fis.available();
//while there is still data to be sent keep looping and write the data
//to the output stream as the buffer is filled
try {
while ((bytes = fis.read(buffer)) != -1) {
aesFileDecrypter.decryptUpdate(buffer);
//os.write(buffer, 0, bytes);
}
os.write(aesFileDecrypter.decryptFinal(), 0, totalBytes);
}
catch(Exception e) {
e.printStackTrace();
}
}
Firstly, just to be clear, from comments below, you shouldn't call doFinal() on every block, because doFinal() expects any padding at the end, which obviouslly won't be there in intermediate blocks. Either (a) call update() on intermediate data, then doFinal() at the end, or (b) just arrange to have all your data in one buffer or byte array, and call doFinal() once on the whole job lot.
It's not clear from the code you posted that that's actually what you're doing, but it should be mentioned just in case.
Failing that, then as a first step to debugging, I'd suggest whichever of these two is easier for you:
Decrypting in ECB mode with no padding and seeing what you get. Look at the first block of data this brings back. If you can XOR this with your IV bytes and get the expected decrypted data, you know your key is OK.
Dumping out the actual key bytes from C# before base 64 encoding and Java after decoding and checking they are the same.
As I recall, C# has unsigned bytes (whereas Java signed) so there are a few places where there's room for things subtly going wrong with byte signedness.
I have encountered this problem before.
When I wrote some code to do encryption and decryption like this:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(sec, "AES"),new IvParameterSpec(new byte[cipher.getBlockSize()]));
byte[] encode = cipher.doFinal(data);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sec, "AES"), new IvParameterSpec(new byte[cipher.getBlockSize()]));
byte[] decode = cipher.doFinal(encode);
I forgot the first IvParameterSpec(new byte[cipher.getBlockSize()]) when encrypting data, then I got an exception "pad block corrupted", so maybe you should check you encryption code.
As far as I know AES is based on Rijndael, but the specification is not exactly the same. I would suggest to check the key and block size you are using to cipher in C# and the sizes being use in Java. (.Net differences between Rijndael and AES).
The doFinal() was the undoing of the code above, and I ended up just using cipher streams instead of the update/doFinal approach. This way I could use the FileInputStream and my cipher as parameters for the CipherInputStream, and then pass the output to the web browser through an OutputStream. Breaking the update and doFinal out into their own method calls made the task much more difficult and both methods were deleted from the decrypter class (leaving a single while loop that read in chunks of data and output it to the browser). The Bouncy Castle Provider was also not needed in this case and PKCS5Padding was enough, which was given by the SunJCE.

Categories