I'm writing a code that send encrypted file from client to server
but first the client send the encrypted message digest of the file to the server and then send the name of the file and at the end it will send the bytes of encrypted file,
but in the server side it read all these variables as one variable which is the digest ,
and when the server trying to decrypt the digest it throws Illegal Block Size Exception
My question here is how can the server read and save them in different variables ??
Client
// set mode to encrypt
AesCipher.init(Cipher.ENCRYPT_MODE, key);
DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());
// get the digest of the file
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(bytes);
// encrypt digest and write it to file
byte [] encryptedHash = AesCipher.doFinal(hash);
toServer.write(encryptedHash);
// write file name to server
toServer.writeUTF(fileName);
//encrypt file
byte[] encryptedByte = AesCipher.doFinal(bytes);
// write file to server
toServer.write(encryptedByte);
toServer.flush();
socket.close();
Server
// read digest of the file
byte [] digest =IOUtils.toByteArray(fromClient);
// decrypt it
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedDigest = AesCipher.doFinal(digest);
// read file name to be received
String fileName = fromClient.readUTF();
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// read file bytes from client
byte[] fileBytes = IOUtils.toByteArray(fromClient);
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = AesCipher.doFinal(fileBytes);
bos.write(decryptedByte, 0, decryptedByte.length);
bos.close();
also I tried this code but it didn't works too
// read digest of the file
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = fromClient.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] digest = buffer.toByteArray();
// decrypt it
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedDigest = AesCipher.doFinal(digest);
// read file name to be received
String fileName = fromClient.readUTF();
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// read file bytes from client
byte[] fileBytes = IOUtils.toByteArray(fromClient);
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = AesCipher.doFinal(fileBytes);
bos.write(decryptedByte, 0, decryptedByte.length);
bos.close();
IOUtils.toByteArray(InputStream) reads the entire stream. So instead of just getting the hash bytes, you got the whole stream, and there was nothing left for the filename or the ciphertext, and the hash didn't check.
You don't need external libraries for this. You can do it all with DataInputStream and DataOutputStream. But you do need to send the length of the hash ahead of the hash.
Client:
// set mode to encrypt
AesCipher.init(Cipher.ENCRYPT_MODE, key);
DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());
// get the digest of the file
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hash = md.digest(bytes);
// encrypt digest and write it to file
byte [] encryptedHash = AesCipher.doFinal(hash);
toServer.writeInt(encryptedHash.length);
toServer.write(encryptedHash);
// write file name to server
toServer.writeUTF(fileName);
//encrypt file
byte[] encryptedByte = AesCipher.doFinal(bytes);
// write file to server
toServer.writeInt(encryptedByte.length);
toServer.write(encryptedByte);
socket.close();
Server:
// read digest of the file
int digestLength = fromClient.readInt();
byte[] digest = new byte[digestLength];
fromClient.readFully(digest);
// decrypt it
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedDigest = AesCipher.doFinal(digest);
// read file name to be received
String fileName = fromClient.readUTF();
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// read file bytes from client
int fileLength = fromClient.readInt();
byte[] fileBytes = new byte[fileLength];
fromClient.readFully(fileBytes);
AesCipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = AesCipher.doFinal(fileBytes);
bos.write(decryptedByte, 0, decryptedByte.length);
bos.close();
However the encryption and decryption parts of this would be much better done with CipherInputStream and CipherOutputStream. You shouldn't load entire files into memory.
Note that the file.createNewFile() call was redundant before new FileOutputStream(...).
Why you're encrypting a message digest is another mystery. You should be using it as a final step to compare with a locally-generated digest after decryption.
Related
I'm currently doing an assignment for a college course using Java's JCA.
The application takes in a file and encrypts it (or decrypts it) using DES-ECB. I am fully aware that it's not a secure encryption algorithm.
It encrypts fine, I believe, however when decrypting it blows up with a "Input length must be multiple of 8" even though the original message is being padded with PKCS5.
I have read all literature and quetions regarding this problem here on StackOverflow, but none of the answers seem to resolve this issue, which leads me to believe I am somehow corrupting the message/file...
For the encryption:
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey);
File file = new File(filePath);
FileOutputStream outputStream = new FileOutputStream("encrypted_"+file.getName());
CipherInputStream cipherStream = new CipherInputStream( new FileInputStream(file), cipher);
byte[] buffer = new byte[MAX_BUFFER]; //buffer para leitura
int bytes; //bytes a ler
//Encoder base64 - Apache Commons Codec
Base64 encoder = new Base64();
while ( (bytes = cipherStream.read(buffer)) != -1 ) {
byte[] encodedBuffer = encoder.encode(buffer);
outputStream.write(encodedBuffer, 0, bytes);
}
cipherStream.close();
outputStream.flush();
return outputStream;
For the decryption:
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, symmetricKey);
File file = new File(filePath);
FileInputStream cipheredStream = new FileInputStream(file);
FileOutputStream outputStream = new FileOutputStream("decrypted_"+file.getName());
CipherOutputStream cipherOutStream = new CipherOutputStream(outputStream, cipher);
byte[] buffer = new byte[MAX_BUFFER];
int bytes;
//Decoder base 64 - Apache Commons Codec
Base64 decoder = new Base64();
cipheredStream.read(buffer);
byte[] decodedBuffer = decoder.decode(buffer);
byte[] output = cipher.doFinal(decodedBuffer);
cipherOutStream.write(output);
//TODO bug here -> use this for big files
/*while ( (bytes = cipheredStream.read(buffer)) != -1 ) {
byte[] decodedBuffer = decoder.decode(buffer);
cipherOutStream.write(decodedBuffer, 0, bytes);
}*/
cipherOutStream.close();
cipheredStream.close();
return outputStream;
I've tried using AES to no avail; I've tried no padding, obviously it didn't work.
I'm just lost and would appreciate knowing what I'm doing wrong.
Thanks to #Topaco, the solution was found using Base64InputStream.
Because the deciphering was being done BEFORE decoding, it was generating that error. It was fixed by doing this encryption side:
Base64OutputStream encoder = new Base64OutputStream(outputStream);
while ( (nBytes = cipherStream.read(buffer, 0, MAX_BUFFER)) != -1 )
encoder.write(buffer, 0, nBytes);
And decryption side the exact opposite:
Base64InputStream decoder = new Base64InputStream(fileInputStream);
while ( (nBytes = decoder.read(buffer, 0, MAX_BUFFER)) != -1 )
cipherOutStream.write(buffer, 0, nBytes);
I'm new to encryption/compression in Java and I'm working on a test project where the goal is to compress and then encrypt files via a buffered input in Java. At no point should the file be stored on disk in a non-encrypted format, therefore I want to do the compression and encryption solely on a buffer until the file is fully written.
So the progression would be: read part of file into memory (buffer, 1024 bytes) -> compress (~32 bytes)-> encrypt -> output to disk -> repeat until entire file is written
The issue I'm facing is that once I perform the reverse operations to read the compressed/encrypted file back, only part of the data is there.
To accomplish my goal, I've been combining the Inflater/Deflater classes and a block cipher with AES 256 encryption.
Encryption setup:
byte[] randomSalt = new byte[8];
SecureRandom secRand = new SecureRandom();
secRand.nextBytes(randomSalt);
String randomPassword = new BigInteger(130, secRand).toString(32);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(randomPassword.toCharArray(), randomSalt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
Getting input / writing output:
BufferedInputStream bufferedInput = new BufferedInputStream(
new FileInputStream("file.txt"));
BufferedOutputStream bufferedOutput = new BufferedOutputStream(
new FileOutputStream("encrypted file"));
byte[] buffer = new byte[1024];
try {
while (bufferedInput.read(buffer) != -1) {
byte[] encryptedBuffer = cipher.doFinal(compress(buffer));
bufferedOutput.write(encryptedBuffer);
bufferedOutput.flush();
}
} catch (Exception e) {
//snip
} finally {
bufferedInput.close();
bufferedOutput.close();
}
Compress method:
public static byte[] compress(byte[] data) throws IOException{
Deflater deflater = new Deflater();
deflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
deflater.finish();
byte[] buffer = new byte[1024];
while(!deflater.finished()){
int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
return outputStream.toByteArray();
}
What can I do to be able to compress and encrypt a file 1KB at a time and get the file back in its entirety when I perform the reverse operations?
I'm trying to write an encrypted file that will be decrypted using gpg and will be writing lines incrementally instead of in one chunk. I've generated the keys in GnuPG and am using the public key to encrypt. Here's the method I'm using to encrypt:
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey,
String fileName,boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
if (fileName == null) {
fileName = PGPLiteralData.CONSOLE;
}
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, fileName, // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_192).setSecureRandom(new SecureRandom()));
cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
And I have a small prototype test class to use that method like this:
PGPPublicKey pubKey = PGPEncryptionTools.readPublicKeyFromCol(new FileInputStream(appProp.getKeyFileName()));
byte[] encryptbytes = PGPEncryptionTools.encrypt("\nthis is some test text".getBytes(), pubKey, null, true, false);
byte[] encryptbytes2 = PGPEncryptionTools.encrypt("\nmore test text".getBytes(), pubKey, null, true, false);
FileOutputStream fos = new FileOutputStream("C:/Users/me/workspace/workspace/spring-batch-project/resources/encryptedfile.gpg");
fos.write(encryptbytes);
fos.write(encryptbytes2);
fos.flush();
fos.close();
So this creates encryptedfile.gpg, but when I go to GnuPG to decrypt the file, it works but it only outputs the first line "this is some test text".
How can I modify this code to be able to encrypt both lines and have GnuPG decrypt them?
You're producing multiple, independent OpenPGP messages each time calling your PGPEncryptionTools.encrypt(...) method. To only output a single OpenPGP message (which GnuPG also decrypt in a single run), write all plain text to a single stream (called pOut in your code) and do not close this before finally writing the last byte into the stream.
I'm trying to to apply digital signature to a .txt file. The digital signature is applied successfully but whenever I try to verify it shows me Verified: false.
Here is my code for signing:
public void signData(){
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(privateKey);
File f= new File(path);
//read from file
FileInputStream fis = new FileInputStream(f);
byte[] buffer = new byte[(int) f.length()];
fis.read(buffer);
fis.close();
rsa.update(buffer);
//write to file
byte[] toWrite=rsa.sign();
String signPath;
signPath="Signed-"+f.getName();
File output=new File(signPath);
FileOutputStream fos = new FileOutputStream(output);
fos.write(toWrite);
fos.flush();
fos.close();
System.out.printf("File: %s is now signed in: %s\n\n",path,signPath);
}
Reading and verifying:
public void verify(){
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(publicKey);
File f= new File(path);
FileInputStream fis = new FileInputStream(f);
byte[] buffer = new byte[(int) f.length()];
fis.read(buffer);
fis.close();
sig.update(buffer);
System.out.println("Verified: "+sig.verify(buffer));
}
No Errors are shown. KeyPair algorithm used is "RSA".
Thank you in advance.
For signing, you have the inputs private key and data to sign. The output is the signature.
For verifying, you have the inputs public key and signed data (actually it's the data to sign from the first step). You're missing the signature as an input. It should look like this:
sig.update(signedData);
System.out.println("Verified: "+sig.verify(signature));
Don't forget to read the signature from the file.
I am using AES symmetric encryption algorithm in java and after i encrypt file i want to put something on it to tell me it is encrypted so i can check for it is exist or not before decrypt it ?
Here is a sample of the code
` cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
jTextField3.setText(""+secretKeySpec);
String cleartextFile = "E:\\"+dir;
FileInputStream fis = new FileInputStream(cleartextFile);
CipherInputStream cis = new CipherInputStream(fis, cipher);
FileOutputStream fos = new FileOutputStream("E:\\d"+dir);
FileChannel out = fos.getChannel();
out.transferFrom(Channels.newChannel(cis), 0, Long.MAX_VALUE); `