I'm doing this project for a client and the part i'm currently stuck on involves taking a XML String and encrypting it - this doesn't need to be state of the art, it just needs to encrypt it and decrypt it using a password.
So far the user enters a password which i've hashed using SHA-256, I then try and encrypt it by doing this:
public static String encryptString(String password, String source, String fileName, String fileDir) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, FileNotFoundException, IOException {
FileOutputStream fos = null;
CipherInputStream cis;
byte key[] = password.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(key, "DES");
Cipher encrypt = Cipher.getInstance("DES/ECB/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, secretKey);
InputStream fileInputStream = new ByteArrayInputStream(source.getBytes());//Here I am getting file data as byte array. You can convert your file data to InputStream by other way too.
File dataFile = new File(fileDir, fileName); //dataDir is location where my file is stored
if (!dataFile.exists()) {
cis = new CipherInputStream(fileInputStream, encrypt);
try {
fos = new FileOutputStream(dataFile);
byte[] b = new byte[32];
int i;
while ((i = cis.read(b)) != -1) {
fos.write(b, 0, i);
}
return fileName;
} finally {
try {
if (fos != null) {
fos.flush();
fos.close();
}
cis.close();
fileInputStream.close();
} catch (IOException e) {
//IOException
}
}
}
return "";
}
The password being passed in is the hashed password - from here I try and run it but I get a:
java.security.InvalidKeyException: Invalid key length: 64 bytes exception.
Can someone help please?
Or tell me of a better way to encrypt an XML file with a password?
Thanks
From the docs.
If this cipher requires any algorithm parameters that cannot be
derived from the given key, the underlying cipher implementation is
supposed to generate the required parameters itself (using
provider-specific default or random values) if it is being initialized
for encryption or key wrapping, and raise an InvalidKeyException if it
is being initialized for decryption or key unwrapping. The generated
parameters can be retrieved using getParameters or getIV (if the
parameter is an IV).
A very simple way of encrypting is taking your hash and XOR it with the bytes in the file in a loop (256 bits at a time). It's kind of a low-level approach but you won't need to debug the API. You should be able to implement this with very minimal code.
Related
I am trying to decrypt a String with a known key in Java using standard Cipher API.
The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.
The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.
Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.
Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?
Example Strings and Key
String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
String userKey = "123456789";
private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (message.length() >= 48) {
ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
messageFromEncryptedString = message.substring(24, message.length());
System.out.println(ivFromEncryptedString);
System.out.println(messageFromEncryptedString);
byte[] data = decodeBase64(messageFromEncryptedString);
byte[] ivData = decodeBase64(ivFromEncryptedString);
paddedKey = padShortKeys(userKey);
byte[] keyBytes = paddedKey.getBytes(CHARSET);
MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
keyBytes = sha.digest(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
System.out.println(e);
} finally {
return encrypted;
}
}
return null;
}
private static String encodeBase64(byte [] in){
return Base64.getEncoder().encodeToString(in);
}
private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
return DatatypeConverter.parseBase64Binary(str);
}
Also with the current code status I am getting placehoder characters instead of the desired result.
Thanks in advance folks. :)
CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.
Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.
Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.
As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which
encrypts or decrypts data in a single-part operation.
Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.
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.
Hi guys I have to do this and I can encrypt file according to the des algorithm but I can not decyrpt again file ,I recieve error messaje like that :
javax.crypto.BadPaddingException Given final block not properly padded
I can not decrypt file I couldnt find why. Can u help me please
Thank you guys.
JAVA CODE :
public class Sifreleme {
public static void encrypt(){
try {
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
FileOutputStream fosKey = new FileOutputStream("..\\KEY");
SecretKeyFactory keyfac = SecretKeyFactory.getInstance("DES");
DESKeySpec keyspec = (DESKeySpec) keyfac.getKeySpec(key, DESKeySpec.class);
fosKey.write(keyspec.getKey());
fosKey.close();
Cipher crypt = Cipher.getInstance("DES");
crypt.init(Cipher.ENCRYPT_MODE, key);
FileInputStream fis = new FileInputStream("C:\\Users\\akif\\Desktop\\zilsesi.mp3");
FileOutputStream fos = new FileOutputStream("C:\\Users\\akif\\Desktop\\sifrelenenzilsesi.mp3");
byte[] arrayBytes = new byte[8];
int bytesReads;
while ((bytesReads = fis.read(arrayBytes)) != -1) {
fos.write(crypt.doFinal(arrayBytes), 0, bytesReads);
}
fis.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void decrypt(){
try {
FileInputStream fisKey = new FileInputStream("..\\KEY");
byte[] arrayKey = new byte[fisKey.available()];
fisKey.read(arrayKey);
SecretKey key = new SecretKeySpec(arrayKey, "DES");
Cipher decrypt = Cipher.getInstance("DES");
decrypt.init(Cipher.DECRYPT_MODE, key);
FileInputStream fis = new FileInputStream("C:\\Users\\akif\\Desktop\\sifrelenenzilsesi.mp3");
byte[] encText = new byte[16];
int bytesReads;
while ((bytesReads = fis.read(encText)) != -1) {
fis.read(decrypt.doFinal(encText), 0, bytesReads);
}
fis.close();
System.out.println(new String(encText));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String []args) throws IOException{
encrypt();
decrypt();
}
Your code here:
while ((bytesReads = fis.read(encText)) != -1) {
fis.read(decrypt.doFinal(encText), 0, bytesReads);
}
Is rather obviously wrong: you need to write the plaintext generated by calling decrypt.doFinal just like you do during encryption. Currently you are overwriting the generated plaintext by the next block(s) of ciphertext because you call read twice in the loop.
Furthermore, depending on your DES Cipher implementation, you forgot about the IV.
A lot of other things are wrong as well, including:
the stream handling using getAvailable();
the use of the 56 bit DES cipher;
the use of ECB mode;
the repeated calls to doFinal (which results in a very large overhead and insecure code);
not using the CipherInputStream and CipherOutputStream (etcetera);
using a string as the key;
forgetting to close your streams when an exception occurs (use the try with resources);
the printStackTracke() exception handling;
the use of static fields as variables.
Using the platform encoding within new String(encText) is only likely wrong.
Note that using the wrong key / ciphertext combination will likely also result in this error.
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 am having a problem decrypting a file using RSA public key decryption. My process is to receive the xml file, encrypt the content, and write it back to the same file. Another function decrypts the content. My source code is:
public void decryptFile(String fileName,PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
FileInputStream fis = new FileInputStream(fileName);
File file=new File("decryptedfile.xml");
if(file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream("decryptedfile.xml");
CipherInputStream cis = new CipherInputStream(fis, cipher);
int i;
byte[] block = new byte[32];
//System.out.println("Read : "+cis.read(block));
while ((i = cis.read(block)) != -1) {
System.out.println(String.valueOf(i));
fos.write(block, 0, i);
}
fos.close();
}
I just pass in the name of the encrypted file, and the corresponding private key value, into the function. However the cis.read(block) returns -1 on the first attempt. Can anyone suggest how I can decrypt the encrypted file?
Your file is almost certainly not RSA encrypted. Its probably encrypted with AES under a random symmetric key and the key is then encrypted with RSA.
You are assuming someone actually encrypted the entire file using just RSA. Assuming the implementation even lets you do this (i've seen ones that throw exceptions when one tries this), it would be way way way too slow to do anything useful.