I'm having some issues with my code whereby I am not using the same IV for encryption and decryption. I know in order to do this correctly I have to write the IV to my output file before the data however I am struggling implementing this. Could anyone help me with this issue?
Edited code code again to show full scope
public class TestFileEncryption {
private static void mainCrypto(int cipherMode, File inputFile, File outputFile) throws Exception{
//Let the user enter the key they wish to use
Key secretKey = new SecretKeySpec(UITest.getStoreKey().getBytes(), UITest.getSendAlg()); //Generates a key based on the default keysize for the specified algorithm
//Generate an Initialization Vector (IV)
final int ALG_KEYLENGTH = UITest.getStoreKey().length(); //Change this as desired for the security level you want
byte[] iv = new byte[ALG_KEYLENGTH]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_KEYLENGTH];
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
}
public static void encrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void decrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void main(String[] args) {}
}
You just have to write the IV before the ciphertext:
outputStream.write(iv);
outputStream.write(outputBytes);
Then, when decrypting, read the IV and the ciphertext:
byte[] iv = new byte[ALG_BLOCKSIZE];
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_BLOCKSIZE];
inputStream.read(iv);
inputStream.read(inputBytes);
Here ALG_BLOCKSIZE needs to be 16 for AES-CBC.
Just extending answer of #Javier.
it looks like you'd like to use the same method for encryption and decrpytion (depending on the mode) however there's a difference in handling the IV.
You generated a random IV, then you overwrote it with the input of the (plain) input and at the end you wrote it to the output (regardless it's decryption).
So you have to distinguish if the mode is
encryption - the IV is generated and written to the output before ciphertext
decryption - the IV is read from the input and used for decryption, but not written to the output
something like that:
private void encrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
byte[] iv = new byte[16]; // 16 for AES-CBC
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
}
private void decrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()-16];
byte[] iv = new byte[16]; // 16 for AES-CBC
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
To leave out some detail, maybe you could directly use Java CipherOutputStream and CiptherInputStream and the implementation will handle these details for you (if you don't care about exact format).
Next what are you missing is an authentication tag, at least hash of the plaintext assuring integrity of the ciphertext. (it's called authenticated encryption)
Related
I have created encrypted file with combination of AES encryption, intilizatio vector and original data.
So my encrypted file contains above 3 elements in encrypted form.
Now during decryption i am stuck at separating all these 3 elements again, moreover i have to use AES key length hard coded during decryption that was generated during encryption.
public static void encrypt() throws Exception {
// RSA with ECB mode
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, generatePublicKey(readKeysFromFile("My_public.pub")));
// AES key generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, srandom);
SecretKey skey = kgen.generateKey();
// Initialization vector 16 byte
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
try (FileOutputStream out = new FileOutputStream("dataFile" + ".enc")) {
{
byte[] b = cipher.doFinal(skey.getEncoded());
out.write(b);
System.err.println("AES Key Length: " + b.length);
}
out.write(iv);
System.err.println("IV Length: " + iv.length);
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
File inputDataFile = new File("dataFile.xml");
try (DataInputStream in = new DataInputStream(new FileInputStream(inputDataFile))) {
byte[] buffer = new byte[(int)inputDataFile.length()];
in.readFully(buffer);
in.close();
byte[] encryptedData = ci.doFinal(buffer);
out.write(encryptedData);
out.close();
}
}
}
public static void decryptRSAAES() throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, generatePrivateKey(readKeysFromFile("My_private.key")));
File file2 = new File("dataFile.enc");
RandomAccessFile raf = new RandomAccessFile(file2, "r");
// length of AES key
byte[] c = new byte[384];
// read the AES key from file
raf.read(c, 0 , 384);
byte[] fileContent = Files.readAllBytes(file2.toPath());
byte[] keyb = cipher.doFinal(c);
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");
// read the initializatoin vector
byte[] iv = new byte[128/8];
raf.seek(384);
raf.read(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
raf.seek(400);
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
try (FileOutputStream out = new FileOutputStream("decryptedFileTest"+".xml")){
byte[] decryptedData = ci.doFinal(fileContent);
out.write(decryptedData);
out.close();
//processDecryptFile(ci, in, out);
}
}
Actual result: decrypted file is created with AES key and original plain data
Expected result: write only original plain data in output removing AES and initilization vector.
Let's simplify this and use the functions newly available in Java's InputStream classes:
public static void encrypt(RSAPublicKey publicKey) throws Exception {
try (FileOutputStream out = new FileOutputStream("dataFile" + ".enc")) {
// --- RSA using PKCS#1 v1.5 padding
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// --- AES key generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
// --- write encrypted AES key
byte[] encryptedSKey = cipher.doFinal(skey.getEncoded());
out.write(encryptedSKey);
// --- Initialization vector 16 byte
SecureRandom srandom = new SecureRandom();
byte[] iv = new byte[128/8];
srandom.nextBytes(iv);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// --- write IV
out.write(iv);
// --- initialize AES cipher
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, skey, ivspec);
// --- convert file by copying to memory
try (FileInputStream in = new FileInputStream("dataFile.xml")) {
byte[] buffer = in.readAllBytes();
byte[] encryptedData = ci.doFinal(buffer);
out.write(encryptedData);
}
}
}
public static void decrypt(RSAPrivateKey privateKey) throws Exception {
try (FileInputStream in = new FileInputStream("dataFile" + ".enc")) {
// --- RSA using PKCS#1 v1.5 padding
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// --- read encrypted AES key
byte[] encryptedSKey = in.readNBytes(determineEncryptionSizeInBytes(privateKey));
byte[] decryptedSKey = cipher.doFinal(encryptedSKey);
SecretKey skey = new SecretKeySpec(decryptedSKey, "AES");
// --- Initialization vector 16 byte
byte[] iv = in.readNBytes(128 / Byte.SIZE);
IvParameterSpec ivspec = new IvParameterSpec(iv);
// --- initialize AES cipher
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.DECRYPT_MODE, skey, ivspec);
// --- convert file by copying to memory
File outputDataFile = new File("dataFile.xml2");
try (FileOutputStream out = new FileOutputStream(outputDataFile)) {
byte[] buffer = in.readAllBytes();
byte[] decryptedData = ci.doFinal(buffer);
out.write(decryptedData);
}
}
}
private static int determineEncryptionSizeInBytes(RSAPrivateKey privateKey) {
return (privateKey.getModulus().bitLength() + Byte.SIZE - 1) / Byte.SIZE;
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(384 * Byte.SIZE);
KeyPair pair = kpg.generateKeyPair();
encrypt((RSAPublicKey) pair.getPublic());
decrypt((RSAPrivateKey) pair.getPrivate());
}
As you can see the code is now very much more like a mirror image. I have simply copied the encryption code and then made changes to it. As you can see it now uses fewer classes by relying on InputStream#readAllBytes() (since Java 9) and InputStream#readNBytes() (since Java 11).
Note that you generally want to stream the file using a smaller buffer. As the entire plaintext and ciphertext are currently buffered your application uses much more memory than required. To encrypt data using streams you can rely on CipherInputStream and CipherOutputStream.
Needless to say the exception handling needs improvement, I've simply looked at the best way to solve your current issue. Please take another look at it when you've got things working (get things working, get things right, get things optimized).
I am working on a program that I will use to encrypt and decrypt files using the JCE. My encryption and decryption is working correctly in default mode (ECB/PKCS5PADDING) however when I try to use CBC and I decrypt my file I am getting some of the text being junk (or when I try an image it gets corrupted.
Can anyone see what I am doing wrong? (I have not included my imports, can add if necessary)
public class encwork {
private static String keyString = "ykHySDZCWr16TVku"; //Encryption key
private static void bulkWork(int cipherMode, File inputFile, File outputFile) throws Exception{
//Let the user enter the key they wish to use
Key secretKey = new SecretKeySpec(keyString.getBytes(), "AES"); //Generates a key based on the default keysize for the specified algorithm
//Generate an Initialization Vector (IV)
final int ALG_KEYLENGTH = 128; //Change this as desired for the security level you want
byte[] iv = new byte[ALG_KEYLENGTH / 8]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
}
public static void main(String[] args) {
File inputFile = new File("C:/Users/admin/Desktop/Crypto/In/test.txt");
File encryptedFile = new File("C:/Users/admin/Desktop/Crypto/Enc/test.encrypted");
File decryptedFile = new File("C:/Users/admin/Desktop/Crypto/Dec/testdec.txt");
//Encryption
try {
encwork.encrypt(inputFile, encryptedFile); //Encrypt method
} catch (Exception e) {
e.printStackTrace(); //Will show what caused the error in the console if an error occurs
}
//Decryption
try {
encwork.decrypt(encryptedFile, decryptedFile); //Decrypt method
} catch (Exception e) {
e.printStackTrace(); //Will show what caused the error in the console if an error occurs
}
}
public static void encrypt(File inputFile, File outputFile) throws Exception {
bulkWork(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void decrypt(File inputFile, File outputFile) throws Exception {
bulkWork(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}}
You are not using the same IV for both encryption and decryption.
From the comment that the decryption starts: "the first line is "çQßs}# L¤qMä]this is a test" that would mean the IV for encryption and decryption is not the same.
This comment says it all:
//Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
Either:
save the IV by returning it from the encryption and passing it in on decryption
or
prefix the encrypted data with the IV and on decryption split it off to use for decryption. (The IV does not need to be secret)
More on the IV and CBC mode, see Cipher Block Chaining (CBC):
While the IV influences the entire encrypted data CBC mode is self correcting and this means only the first block is incorrect when the wrong IV is used on decryption.
I am working on a file encryption/decryption app. I am using a simple .txt file for testing. When I select the file from within the app and choose to encrypt, the entire file data is encrypted. However, when I decrypt only part of the file data gets decrypted. For some reason the first 16 bytes/characters doesn't get decrypted.
test_file.txt contents: "This sentence is used to check file encryption/decryption results."
encryption result: "¾mÁSTÐÿT:Y„"O¤]ÞPÕµß~ëqrÈb×ßq²¨†ldµJ,O|56\e^-’#þûÝû"
decryption result: "£ÿÒÜÑàh]VÄþ„- used to check file encryption/decryption results."
There aren't any errors in the logcat.
What am I doing wrong?
Method to encrypt file:
public void encryptFile(String password, String filePath) {
byte[] encryptedFileData = null;
byte[] fileData = null;
try {
fileData = readFile(filePath);//method provided below
// 64 bit salt for testing only
byte[] salt = "goodsalt".getBytes("UTF-8");
SecretKey key = generateKey(password.toCharArray(), salt);//method provided below
byte[] keyData = key.getEncoded();
SecretKeySpec sKeySpec = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
encryptedFileData = cipher.doFinal(fileData);
saveData(encryptedFileData, filePath);//method provided below
}
catch (Exception e) {
e.printStackTrace();
}
}
Method to read file content:
public byte[] readFile(String filePath) {
byte[] fileData;
File file = new File(filePath);
int size = (int) file.length();
fileData = new byte[size];
try {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
inputStream.read(fileData);
inputStream.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return fileData;
}
Method to generate secret key:
private SecretKey generateKey(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase computation time. You
// should select a value that causes computation to take >100ms.
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 256;
SecretKeyFactory secretKeyFactory;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Use compatibility key factory -- only uses lower 8-bits of passphrase chars
secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1And8bit");
}
else {
// Traditional key factory. Will use lower 8-bits of passphrase chars on
// older Android versions (API level 18 and lower) and all available bits
// on KitKat and newer (API level 19 and higher).
secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
}
KeySpec keySpec = new PBEKeySpec(password, salt, iterations, outputKeyLength);
return secretKeyFactory.generateSecret(keySpec);
}
Method to save encrypted/decrypted data to the file:
private void saveData(byte[] newFileData, String filePath) {
File file = new File(filePath);
try {
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
outputStream.write(newFileData);
outputStream.flush();
outputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
Method to decrypt file:
public void decryptFile(String password, String filePath) {
byte[] decryptedFileData = null;
byte[] fileData = null;
try {
fileData = readFile(filePath);
byte[] salt = "goodsalt".getBytes("UTF-8");//generateSalt();
SecretKey key = generateKey(password.toCharArray(), salt);
byte[] keyData = key.getEncoded();
SecretKeySpec sKeySpec = new SecretKeySpec(keyData, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
decryptedFileData = cipher.doFinal(fileData);
saveData(decryptedFileData, filePath);
}
catch (Exception e) {
e.printStackTrace();
}
}
This line of code encrypts the file:
//simple password for testing only
encryptor.encryptFile("password", "storage/emulated/0/Download/test_file.txt");
This line decrypts the file:
encryptor.decryptFile("password", "storage/emulated/0/Download/test_file.txt");
Edit: Thanks to DarkSquirrel42 and Oncaphillis. You guys are awesome!
Adding this line of code to both encrypt and decrypt functions solved my problem.
//note: the initialization vector (IV) must be 16 bytes in this case
//so, if a user password is being used to create it, measures must
//be taken to ensure proper IV length; random iv is best and should be
//stored, possibly alongside the encrypted data
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes("UTF-8"));
and then,
cipher.init(Cipher.XXXXXXX_MODE, sKeySpec, ivSpec);
your problem has something to do with the cipher's mode of operation ... cbc, or cipher block chaining mode
in general CBC is simple ... take whatever the output of your previous encryiption block was, and xor that onto the current input before encrypting it
for the first block we obviously have a problem... there is no previous block ... therefore we introduce something called IV ... an initialisation vector ... a block ength of random bytes ...
now ... as you can imagine, you will need the same IV when you want to decrypt ...
since you don't save that, the AES implementation will give you a random IV every time ...
therefore you don't have all information to decrypt block 1 ... which is the first 16 bytes in case of AES ...
when handling CBC mode data, it's allways a good choice to simply prepend the used IV in your cypertext output ... the IV shall just be random ... it is no secret ...
Like #ÐarkSquirrel42 already points out the en/decrytion routine for CBC seems to interpret the first 16 bytes as an initialisation vector. This worked for me:
// got to be random
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(Cipher.XXXXX_MODE, sKeySpec,ivspec);
I have a problem when decrypting XML type my file my returns incomplete data algorithm and rare symbols.
public File decryptFile(File fileInput, X509Certificate certificate) throws BadPaddingException, Exception {
try (DataInputStream dis = new DataInputStream(new FileInputStream(fileInput))) {
byte[] encryptedKeyBytes = new byte[dis.readInt()];
dis.readFully(encryptedKeyBytes);
PublicKey publicKey = certificate.getPublicKey();
rsaCipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] rijndaelKeyBytes = rsaCipher.doFinal(encryptedKeyBytes);
SecretKey rijndaelKey = new SecretKeySpec(rijndaelKeyBytes, "Rijndael");
byte[] iv = new byte[16];
dis.read(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, rijndaelKey, spec);
try (CipherInputStream cis = new CipherInputStream(dis, cipher)) {
try (FileOutputStream fos = new FileOutputStream(fileInput.getAbsolutePath() + ".xml")) {
byte[] data = new byte[16];
int theByte;
while ((theByte = cis.read(data)) != -1) {
System.out.print(new String(data));
fos.write(data, 0, theByte);
}
System.out.println("\n\n");
}
}
}
return new File(fileInput.getAbsolutePath() + ".xml");
}
this code returns me the data
</ctaAbonBenef><distPago>00000</distPago><item>00000</item><pagoPoder>N</p�|���[�[W�Z�5��Q�
I think this has to do with UTF-8, but I can not solve.
Now I can also believe that it is the encryption algorithm to use, I leave just in case.
public static void generateFileEncrypt(File fileInput, PrivateKey privateKey, String folderSave) throws Exception {
String fileOutput = folderSave + "\" + fileInput.getName() + ENCRYPTED_FILENAME_SUFFIX;
DataOutputStream output = new DataOutputStream(new FileOutputStream(fileOutput));
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, privateKey);
KeyGenerator rijndaelKeyGenerator = KeyGenerator.getInstance("Rijndael");
rijndaelKeyGenerator.init(128);
Key rijndaelKey = rijndaelKeyGenerator.generateKey();
byte[] encodedKeyBytes = rsaCipher.doFinal(rijndaelKey.getEncoded());
output.writeInt(encodedKeyBytes.length);
output.write(encodedKeyBytes);
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
output.write(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
Cipher symmetricCipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding");
symmetricCipher.init(Cipher.ENCRYPT_MODE, rijndaelKey, spec);
try (
CipherOutputStream cos = new CipherOutputStream(output, symmetricCipher);
FileInputStream fis = new FileInputStream(fileInput)) {
int theByte;
byte[] data = new byte[16];
while ((theByte = fis.read(data)) != -1) {
System.out.print(new String(data));
cos.write(data, 0, theByte);
}
System.out.println("\n\n");
cos.flush();
}
}
Thanks in advance.
I haven't digested all your code; I stopped when I saw you trying to decrypt with the public key, and encrypting with the private key. That's sort of like a digital signature, but your padding will be all wrong and you should use the Signature class if that is what your really want to do.
The public key is used to encrypt, or to verify a digital signature. Use the private key to decrypt, and see if that resolves your problem.
You are still doing it wrong. Don't call it "encryption" if the key isn't private.
But anyway, I think the printing to stdout looks wrong because you are converting the entire buffer to text. The last block is likely to be padded, so it won't decode to valid text—it's padding; it wasn't part of the input file, and you aren't writing it to the decrypted file, but you are printing it.
Change to encrypt with the public key, decrypt with the private key, and then change your printing to this:
System.out.print(new String(data, 0, theByte));
Even better would be to specify the character set of the data (probably UTF-8, since it's the default for XML).
I think u should do the opposite. encrypt with the public key and decrypt with the private key..
I wrote two functions as follows which encrypt and decrypt the data.
public static void encrypt() throws Exception {
// Add the BouncyCastle Provider
//Security.addProvider(new BouncyCastleProvider());
// Generate the key
byte[] keyBytes = "AAAAAAAAAAAAAAAA".getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
// Generate the IV
byte[] ivBytes = "AAAAAAAAAAAAAAAA".getBytes();
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Create the cipher object and initialize it
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
// Read all bytes from a file into a bytes array
byte[] inputBytes = GCM.readFile("input");
byte[] cipherBytes = cipher.doFinal(inputBytes);
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("output.enc"));
outputStream.write(cipherBytes);
outputStream.close();
}
public static void decrypt() throws Exception {
// Add the BouncyCastle Provider
//Security.addProvider(new BouncyCastleProvider());
// Generate the key
byte[] keyBytes = "AAAAAAAAAAAAAAAA".getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
// Generate the IV
byte[] ivBytes = "AAAAAAAAAAAAAAAA".getBytes();
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// Create the cipher object and initialize it
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
// Read all bytes from a file into a bytes array
byte[] cipherBytes = GCM.readFile("ouput.enc");
byte[] decBytes = cipher.doFinal(cipherBytes);
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("regen.plain"));
outputStream.write(decBytes);
outputStream.close();
}
I realize that the code has the key set as "AAAAAAAAAAAAAAAA".getBytes() ; however please bear with me as this is just an example.
When I run the program I get the following stack trace :-
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at GCM.decrypt(GCM.java:80)
at GCM.main(GCM.java:90)
I'm having trouble figuring out why I'm encountering this error. Any hints on how I could solve the issue?
[EDIT]
It seems that when I write out data there are 16 bytes in all but only 15 bytes when I read it back in.
On your update: Well, that's easy then, fix the part that reads the file because the ciphertext needs to be N * blocksize, thus 16 bytes. I don't see any other blatant errors.
Possible problem (unless it is a typo) you write to output.enc but read from ouput.enc.