Hi I have a client using Triple DES. I know I know if AES I would not have a problem it is a older solution integration. I have code below that Takes in a file and writes to another. On the decrypt method I don't understand the 2nd parameter for length of the file. Please help. The error I get is the following:
Exception in thread "main" org.bouncycastle.crypto.DataLengthException: last block incomplete in decryption
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(Unknown Source)
at DESede_BC.decrypt(DESede_BC.java:102)
at DESede_BC.main(DESede_BC.java:120)
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
public class DESede_BC {
PaddedBufferedBlockCipher encryptCipher;
PaddedBufferedBlockCipher decryptCipher;
// Buffers used to transport the bytes from one stream to another
byte[] buf = new byte[8]; //input buffer - block size length
byte[] obuf = new byte[557]; //output buffer
byte[] key = null; //the key
public DESede_BC(){
//use a default 192 bit key
key = "thekey".getBytes();
InitCiphers();
}
public DESede_BC(byte[] keyBytes){
key = new byte[keyBytes.length];
System.arraycopy(keyBytes, 0 , key, 0, keyBytes.length);
InitCiphers();
}
private void InitCiphers(){
encryptCipher = new PaddedBufferedBlockCipher(new DESedeEngine());
encryptCipher.init(true, new KeyParameter(key));
decryptCipher = new PaddedBufferedBlockCipher(new DESedeEngine());
decryptCipher.init(false, new KeyParameter(key));
}
public void ResetCiphers() {
if(encryptCipher!=null)
encryptCipher.reset();
if(decryptCipher!=null)
decryptCipher.reset();
}
public void encrypt(InputStream in, long length, OutputStream out)
throws ShortBufferException,
IllegalBlockSizeException,
BadPaddingException,
DataLengthException,
IllegalStateException,
InvalidCipherTextException
{
try {
// Bytes written to out will be encrypted
// Read in the cleartext bytes from in InputStream and
// write them encrypted to out OutputStream
int noBytesRead = 0; //number of bytes read from input
int noBytesProcessed = 0; //number of bytes processed
while ((noBytesRead = in.read(buf)) >= 0) {
noBytesProcessed =
encryptCipher.processBytes(buf, 0, noBytesRead, obuf, 0);
out.write(obuf, 0, noBytesProcessed);
}
noBytesProcessed =
encryptCipher.doFinal(obuf, 0);
out.write(obuf, 0, noBytesProcessed);
out.flush();
}
catch (java.io.IOException e) {
System.out.println(e.getMessage());
}
}
public void decrypt(InputStream in,long length, OutputStream out)
throws ShortBufferException, IllegalBlockSizeException, BadPaddingException,
DataLengthException, IllegalStateException, InvalidCipherTextException
{
try {
// Bytes read from in will be decrypted
// Read in the decrypted bytes from in InputStream and and
// write them in cleartext to out OutputStream
int noBytesRead = 0; //number of bytes read from input
int noBytesProcessed = 0; //number of bytes processed
while ((noBytesRead = in.read(buf)) >= 0) {
noBytesProcessed = decryptCipher.processBytes(buf, 0, noBytesRead, obuf, 0);
out.write(obuf, 0, noBytesProcessed);
}
noBytesProcessed = decryptCipher.doFinal(obuf, 0);
out.write(obuf, 0, noBytesProcessed);
out.flush();
}
catch (java.io.IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String... args)
throws Exception
{
DESede_BC d = new DESede_BC();
FileInputStream fis2 = new FileInputStream("c:\\2.in");
FileOutputStream fos2 = new FileOutputStream("c:\\decrypted.txt");
d.decrypt(fis2, new Long("128"), fos2);
}
}
I think you need to decode your input into Base64 before doFinal:
byte[] obuf = Base64.decode(obuf, Base64.NO_OPTIONS);
byte[] decValue = c.doFinal(obuf);
Related
I'm working on a file encryption benchmark for large files and tested ChaCha20-Poly1305, but received an error on decryption part:
java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
at ChaCha20.ChaCha20Poly1305Jre.main(ChaCha20Poly1305Jre.java:73)
Caused by: javax.crypto.ShortBufferException: Output buffer too small
at java.base/com.sun.crypto.provider.ChaCha20Cipher$EngineAEADDec.doFinal(ChaCha20Cipher.java:1360)
at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:701)
This is not an error in my program but in OpenJava 11 I'm using and should get fixed (known since 2019, see https://bugs.openjdk.java.net/browse/JDK-8224997).
Even with the newest "Early adopter"-version (OpenJDK11U-jdk_x64_windows_11.0.7_9_ea) the error still occurs. This test is run with java version: 11.0.6+8-b520.43.
My question is: is there any other way to perform file encryption with ChaCha20-Poly1305 with native JCE for large files ?
I do not want to use BouncyCastle (as I'm using BC allready for the counterpart benchmark) or reading the plainfile completly into memory (in my source the
testfile is only 1.024 bytes large but the benchmark will test up to 1 GB files). As well I do not want to use ChaCha20 as it does not provide any authentication.
You can find the sources for ChaCha20Poly1305Jce.java, ChaCha20Poly1305JceNoStream.java and ChaCha20Jce.java in my Github-Repo https://github.com/java-crypto/Stackoverflow/tree/master/ChaCha20Poly1305.
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
public class ChaCha20Poly1305Jce {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
// Get Cipher Instance
Cipher cipherE = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// Create parameterSpec
AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + keySpec.getAlgorithm() + " " + keySpec.getFormat());
System.out.println("cipher algorithm: " + cipherE.getAlgorithm());
// initialize the cipher for encryption
cipherE.init(Cipher.ENCRYPT_MODE, keySpec, algorithmParameterSpec);
// encryption
System.out.println("start encryption");
byte inE[] = new byte[8192];
byte outE[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenamePlain));
OutputStream os = new FileOutputStream(new File(filenameEnc))) {
int len = 0;
while (-1 != (len = is.read(inE))) {
cipherE.update(inE, 0, len, outE, 0);
os.write(outE, 0, len);
}
byte[] outEf = cipherE.doFinal();
os.write(outEf, 0, outEf.length);
} catch (Exception e) {
e.printStackTrace();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
// initialize the cipher for decryption
cipherD.init(Cipher.DECRYPT_MODE, keySpec, algorithmParameterSpec);
byte inD[] = new byte[8192];
byte outD[] = new byte[8192];
try (InputStream is = new FileInputStream(new File(filenameEnc));
OutputStream os = new FileOutputStream(new File(filenameDec))) {
int len = 0;
while (-1 != (len = is.read(inD))) {
cipherD.update(inD, 0, len, outD, 0);
os.write(outD, 0, len);
}
byte[] outDf = cipherD.doFinal();
os.write(outDf, 0, outDf.length);
} catch (Exception e) {
e.printStackTrace();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
Using the Early Access version of OpenJDK 11.0.8 (get it here: https://adoptopenjdk.net/upstream.html?variant=openjdk11&ga=ea)
I was able to run the (edited) testprogram (now I'm using CipherInput/OutputStreams).
File En-/Decryption with ChaCha20-Poly1305 JCE
see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small
java version: 11.0.8-ea+8
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
Edit: I missed the final version (GA) for one hour but it's working as well:
java version: 11.0.8+10
start encryption
keySpec: ChaCha20 RAW
cipher algorithm: ChaCha20-Poly1305/None/NoPadding
start decryption
compare plain <-> dec: true
new code:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.*;
import java.util.Arrays;
public class ChaCha20Poly1305JceCis {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
System.out.println("File En-/Decryption with ChaCha20-Poly1305 JCE");
System.out.println("see: https://stackoverflow.com/questions/61520639/chacha20-poly1305-fails-with-shortbufferexception-output-buffer-too-small");
System.out.println("\njava version: " + Runtime.version());
String filenamePlain = "test1024.txt";
String filenameEnc = "test1024enc.txt";
String filenameDec = "test1024dec.txt";
Files.deleteIfExists(new File(filenamePlain).toPath());
generateRandomFile(filenamePlain, 1024);
// setup chacha20-poly1305-cipher
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] key = new byte[32]; // 32 for 256 bit key or 16 for 128 bit
byte[] nonce = new byte[12]; // nonce = 96 bit
sr.nextBytes(key);
sr.nextBytes(nonce);
System.out.println("start encryption");
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenamePlain);
FileOutputStream out = new FileOutputStream(filenameEnc);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
System.out.println("keySpec: " + secretKeySpec.getAlgorithm() + " " + secretKeySpec.getFormat());
System.out.println("cipher algorithm: " + cipher.getAlgorithm());
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
// decryption
System.out.println("start decryption");
Cipher cipherD = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
try (FileInputStream in = new FileInputStream(filenameEnc); // i don't care about the path as all is lokal
CipherInputStream cipherInputStream = new CipherInputStream(in, cipherD);
FileOutputStream out = new FileOutputStream(filenameDec)) // i don't care about the path as all is lokal
{
byte[] buffer = new byte[8192];
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "ChaCha20");
//AlgorithmParameterSpec algorithmParameterSpec = new IvParameterSpec(nonce);
cipherD.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(nonce));
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
// file compare
System.out.println("compare plain <-> dec: " + Arrays.equals(sha256(filenamePlain), sha256(filenameDec)));
}
public static void generateRandomFile(String filename, int size) throws IOException, NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] data = new byte[size];
sr.nextBytes(data);
Files.write(Paths.get(filename), data, StandardOpenOption.CREATE);
}
public static byte[] sha256(String filenameString) throws IOException, NoSuchAlgorithmException {
byte[] buffer = new byte[8192];
int count;
MessageDigest md = MessageDigest.getInstance("SHA-256");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filenameString));
while ((count = bis.read(buffer)) > 0) {
md.update(buffer, 0, count);
}
bis.close();
return md.digest();
}
}
Edit July 23 2020: Seems to be that not all Java versions with version nr 11.0.8 got fixed. I tested the "original" Oracle Java 1.0.8 for Windows x64 and the error still persists. I reported this to Oracle Bug tracker and it was assigned as a Bug ID: JDK-8249844 (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8249844).
I would like to encrypt and decrypt files. While decrypting I wish to skip first few bytes of the file and decrypt only the rest.
Here contains three files
input_file.txt - input text file for encryption
output_file.txt - encrypted input_file.txt file
decrypted_file.txt - decrypted output_file.txt file
Sample code:
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileReadWrite {
public static byte[] getAESKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* Method used to generate a random new iv
*
* #return Randomly generated iv
*/
public static byte[] getAESIV() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return bytes;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");
SecretKeySpec keySpec = null;
IvParameterSpec ivSpec = null;
Cipher ecipher = null;
Cipher dcipher = null;
byte[] keyBytes = getAESKey();
byte[] ivBytes = getAESIV();
// Creating keyspec and ivspec for generating cipher
keySpec = new SecretKeySpec(keyBytes,"AES");
ivSpec = new IvParameterSpec(ivBytes);
try {
ecipher = Cipher.getInstance("AES/CTR/NoPadding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
cout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
fout.close();
cout.close();
}
fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");
try {
dcipher = Cipher.getInstance("AES/CTR/NoPadding");
dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
//fin.skip(1024);
CipherInputStream cin = new CipherInputStream(fin, dcipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
**//cin.read(bytearray, 0, 30);**
while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
fout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
cin.close();
fout.close();
}
System.out.println("File read write completed successfully !!! ");
}
}
Tried:
Cipher input stream skip - cin.skip(no_of_bytes) - this doesn't work - this decrypts the entire file
File input stream skip
fin.skip(no_of_bytes); CipherInputStream cin = new
CipherInputStream(fin, cipher);
This does not decrypt the file. The output file looks like encrypted.
Dummy read - Reading and ignoring from cipher input stream - this works
//cin.read(bytearray, 0, 30);
Please clarify me the following:
What skip and seek input stream does internally?
Why my cipher input stream seek doesn't work?
Why cipher input stream requires all the bytes to be read from the stream (I assume so) for decrypting? How does it work?
With reference to the article,
Random access InputStream using AES CTR mode in android
After updating the iv with offset value the decryption works fine.
Here is the updated code
package Streams;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class FileReadWrite {
private static final int AES_BLOCK_SIZE = 16;
public static final void jumpToOffset(final Cipher c, final SecretKeySpec aesKey, final IvParameterSpec iv, final long offset) {
if (!c.getAlgorithm().toUpperCase().startsWith("AES/CTR")) {
throw new IllegalArgumentException("Invalid algorithm, only AES/CTR mode supported");
}
if (offset < 0) {
throw new IllegalArgumentException("Invalid offset");
}
final int skip = (int) (offset % AES_BLOCK_SIZE);
final IvParameterSpec calculatedIVForOffset = calculateIVForOffset(iv, offset - skip);
try {
c.init(Cipher.ENCRYPT_MODE, aesKey, calculatedIVForOffset);
final byte[] skipBuffer = new byte[skip];
c.update(skipBuffer, 0, skip, skipBuffer);
Arrays.fill(skipBuffer, (byte) 0);
} catch (ShortBufferException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalStateException(e);
}
}
private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv, final long blockOffset) {
final BigInteger ivBI = new BigInteger(1, iv.getIV());
final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset / AES_BLOCK_SIZE));
final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
final IvParameterSpec ivForOffset;
if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
} else {
final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE - ivForOffsetBA.length, ivForOffsetBA.length);
ivForOffset = new IvParameterSpec(ivForOffsetBASized);
}
return ivForOffset;
}
public static byte[] getAESKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[32];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* Method used to generate a random new iv
*
* #return Randomly generated iv
*/
public static byte[] getAESIV() {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[16];
secureRandom.nextBytes(bytes);
return bytes;
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");
SecretKeySpec keySpec = null;
IvParameterSpec ivSpec = null;
Cipher ecipher = null;
Cipher dcipher = null;
byte[] keyBytes = getAESKey();
byte[] ivBytes = getAESIV();
// Creating keyspec and ivspec for generating cipher
keySpec = new SecretKeySpec(keyBytes,"AES");
ivSpec = new IvParameterSpec(ivBytes);
try {
ecipher = Cipher.getInstance("AES/CTR/NoPadding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
cout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
fout.close();
cout.close();
}
fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");
try {
dcipher = Cipher.getInstance("AES/CTR/NoPadding");
dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
} catch (Exception e) {
System.out.println("Thus the exception occured during cipher generation is ::: "+e);
}
fin.skip(1024);
jumpToOffset(dcipher, keySpec, ivSpec, 1024);
CipherInputStream cin = new CipherInputStream(fin, dcipher);
//cin.skip(1024);
try {
int count = 0;
int BUFFER_SIZE = 1024;
byte[] bytearray = new byte[BUFFER_SIZE];
//cin.read(bytearray, 0, 30);
while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
//fout.write(bytearray, 0, count);
fout.write(bytearray, 0, count);
}
} catch(Exception ex) {
System.out.println("Thus the exception occured is ::: "+ex);
} finally {
fin.close();
cin.close();
fout.close();
}
System.out.println("File read write completed successfully !!! ");
}
}
I am running into an issue where the code that works against a InputStream backed by a FileInputStream does not work if a CipherInputStream is used.
Example is below:
// skipCount is same as n in a FileInputStream
FileInputStream fis;
...
skipCount = fis.skip(n)
Get a different behavior if a CipherInputStream is used
// skipCount is always 0
CipherInputStream cis;
...
skipCount = cis.skip(n)
After further debugging it looks like, skip will only work (i.e., return values > 0) if used in conjunction with the read() call.
Is there a better way of getting skip to work with CipherInputStream, than rolling my own "skip" method that relies on calling read?
Also, is there a way of telling the CipherInputStream to automatically do a "read" as part of invoking the skip call? Otherwise it looks like the skip API is flaky in CipherInputStream.
MCVE
public class TestSkip {
public static final String ALGO = "AES/CBC/PKCS5Padding";
public static final String CONTENT = "Strive not to be a success, but rather to be of value";
private static int BlockSizeBytes = 16;
private static SecureRandom random = null;
static {
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Could not initialize AES encryption", e);
}
}
static byte[] getKeyBytes() throws NoSuchAlgorithmException, UnsupportedEncodingException
{
byte[] key = "Not a secure string!".getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
return key;
}
static KeySpec getKeySpec() throws GeneralSecurityException, UnsupportedEncodingException
{
return new SecretKeySpec(getKeyBytes(), "AES");
}
static byte[] getIv ()
{
byte[] iv = new byte[BlockSizeBytes];
random.nextBytes(iv);
return iv;
}
static Cipher initCipher (int mode, byte[] iv) throws GeneralSecurityException, UnsupportedEncodingException
{
KeySpec spec = getKeySpec();
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(mode, (SecretKey) spec, new IvParameterSpec(iv));
return cipher;
}
static void encrypt(String fileName) throws
GeneralSecurityException,
IOException
{
FileOutputStream fos = new FileOutputStream(fileName);
byte[] iv = getIv();
fos.write(iv);
Cipher cipher = initCipher(Cipher.ENCRYPT_MODE, iv);
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
PrintWriter pw = new PrintWriter(cos);
pw.println(CONTENT);
pw.close();
}
static void skipAndCheck(String fileName) throws
GeneralSecurityException,
IOException
{
FileInputStream fis = new FileInputStream(fileName);
byte[] iv = new byte[BlockSizeBytes];
if (fis.read(iv) != BlockSizeBytes) {
throw new GeneralSecurityException("Could not retrieve IV from AES encrypted stream");
}
Cipher cipher = initCipher(Cipher.DECRYPT_MODE, iv);
CipherInputStream cis = new CipherInputStream(fis, cipher);
// This does not skip
long count = cis.skip(32);
System.out.println("Bytes skipped: " + count);
// Read a line
InputStreamReader is = new InputStreamReader(cis);
BufferedReader br = new BufferedReader(is);
String read = br.readLine();
System.out.println("Content after skipping 32 bytes is: " + read);
br.close();
}
static InputStream getWrapper(CipherInputStream cis) {
return new SkipInputStream(cis);
}
public static void main(String[] args) throws
IOException,
GeneralSecurityException
{
String fileName = "EncryptedSample.txt";
encrypt(fileName);
skipAndCheck(fileName);
}
}
Found a solution that worked for me.
Created a wrapper class that extended FilterInputStream and implemented the skip method using the same code found in InputStream.java
Wrapper class
public class SkipInputStream extends FilterInputStream
{
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* #param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected SkipInputStream (InputStream in)
{
super(in);
}
/**
* Same implementation as InputStream#skip
*
* #param n
* #return
* #throws IOException
*/
public long skip(long n)
throws IOException
{
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = in.read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
}
With this class one just needs to change the following line in the MCVE above
long count = cis.skip(32);
to
long count = getWrapper(cis).skip(32);
Output
Old
Bytes skipped: 0
Content after skipping 32 bytes is: Strive not to be a success, but rather to be of value
New
Bytes skipped: 32
Content after skipping 32 bytes is: rather to be of value
I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox.
As i have research online and only manage to found this code (pasted at the bottom) which only encrypt the password, but I wanted to know how to encrypt a file or a photo which will eventually uploaded into the dropbox instead.
So is there any reference or help or guide on how to write a java programming ( will be using on Eclipse software ) on encrypting a file using triple DES? Thank you so much.
package com.kushal.utils;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESEncryption {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DES_ENCRYPTION_SCHEME = "DES";
private KeySpec myKeySpec;
private SecretKeyFactory mySecretKeyFactory;
private Cipher cipher;
byte[] keyAsBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public DESEncryption() throws Exception
{
myEncryptionKey = "ThisIsSecretEncryptionKey";
myEncryptionScheme = DES_ENCRYPTION_SCHEME;
keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
myKeySpec = new DESKeySpec(keyAsBytes);
mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = mySecretKeyFactory.generateSecret(myKeySpec);
}
/**
* Method To Encrypt The String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
BASE64Encoder base64encoder = new BASE64Encoder();
encryptedString = base64encoder.encode(encryptedText);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
/**
* Returns String From An Array Of Bytes
*/
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
/**
* Testing the DES Encryption And Decryption Technique
*/
public static void main(String args []) throws Exception
{
DESEncryption myEncryptor= new DESEncryption();
String stringToEncrypt="Sanjaal.com";
String encrypted=myEncryptor.encrypt(stringToEncrypt);
String decrypted=myEncryptor.decrypt(encrypted);
System.out.println("String To Encrypt: "+stringToEncrypt);
System.out.println("Encrypted Value :" + encrypted);
System.out.println("Decrypted Value :"+decrypted);
}
}
File reading is taken from here
public static byte[] encryptFile(String pFilePath, byte[] pKey) throws GeneralSecurityException, IOException
{
File file = new File(pFilePath);
long length = file.length();
InputStream is = new FileInputStream(file);
// You cannot create an array using a long type.
// It needs to be an int type.
// Before converting to an int type, check
// to ensure that file is not larger than Integer.MAX_VALUE.
if (length > Integer.MAX_VALUE) {
// File is too large
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int)length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}
// Close the input stream and return bytes
is.close();
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file.getName());
}
SecretKeyFactory lDESedeKeyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey kA = lDESedeKeyFactory.generateSecret(new DESedeKeySpec(pKey));
IvParameterSpec lIVSpec = new IvParameterSpec(new byte[8]);
Cipher desedeCBCCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
desedeCBCCipher.init(Cipher.ENCRYPT_MODE, kA, lIVSpec);
byte[] encrypted = desedeCBCCipher.doFinal(bytes);
return encrypted;
}
A file can probably be read as a string of binary digits (lots of 1's and 0's). You could read the file, then encrypt the string using DES the same way you would a password, outputting the result to a new file.
Decryption would work the same way, reading the encrypted file, decrypting, and outputting to an unencrypted file.
all files in ~/Cipher/nsdl/crypto can be found here
java files compiled with gcj, see compile.sh
nmint#nqmk-mint ~/Cipher/nsdl/crypto $ echo test | ./cryptTest encrypt deadbeefdeadbeefdeadbeefdeadbeef deadbeef Blowfish CBC > test
null
Exception in thread "main" java.lang.IllegalStateException: cipher is not for encrypting or decrypting
at javax.crypto.Cipher.update(libgcj.so.81)
at javax.crypto.CipherOutputStream.write(libgcj.so.81)
at nsdl.crypto.BlockCrypt.encrypt(cryptTest)
at nsdl.crypto.cryptTest.main(cryptTest)
BlockCrypt.java:
package nsdl.crypto;
import java.io.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class BlockCrypt {
Cipher ecipher;
Cipher dcipher;
byte[] keyBytes;
byte[] ivBytes;
SecretKey key;
AlgorithmParameterSpec iv;
byte[] buf = new byte[1024];
BlockCrypt(String keyStr, String ivStr, String algorithm, String mode) {
try {
ecipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
dcipher = Cipher.getInstance(algorithm + "/" + mode + "/PKCS5Padding");
keyBytes = hexStringToByteArray(keyStr);
ivBytes = hexStringToByteArray(ivStr);
key = new SecretKeySpec(keyBytes, algorithm);
iv = new IvParameterSpec(ivBytes);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public void encrypt(InputStream in, OutputStream out) {
try {
// out: where the plaintext goes to become encrypted
out = new CipherOutputStream(out, ecipher);
// in: where the plaintext comes from
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public void decrypt(InputStream in, OutputStream out) {
try {
// in: where the plaintext come from, decrypted on-the-fly
in = new CipherInputStream(in, dcipher);
// out: where the plaintext goes
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.flush();
out.close();
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
cryptTest.java:
package nsdl.crypto;
import nsdl.crypto.BlockCrypt;
public class cryptTest {
public static void main (String args[]) {
if (args.length != 5) {
System.err.println("Usage: cryptTest (encrypt|decrypt) key iv algorithm mode");
System.err.println("Takes input from STDIN. Output goes to STDOUT.");
} else {
String operation = args[0];
String key = args[1];
String iv = args[2];
String algorithm = args[3];
String mode = args[4];
BlockCrypt blockCrypt = new BlockCrypt(key, iv, algorithm, mode);
if (operation.equalsIgnoreCase("encrypt")) {
blockCrypt.encrypt(System.in, System.out);
} else if (operation.equalsIgnoreCase("decrypt")) {
blockCrypt.decrypt(System.in, System.out);
} else {
System.err.println("Invalid operation. Use (encrypt|decrypt).");
}
}
}
}
The Cipher, ecipher, is not initialized, and it throws an IllegalStateException when you try to use it as if it were initialized in ENCRYPT_MODE.
Note your catch block in the constructor of BlockCrypt. It is catching an exception with no message, and printing "null" to System.err. Rather than aborting execution—perhaps by throwing an exception from the constructor—you keep sailing.
Replacing System.err.println(e.getMessage()) with e.printStackTrace() or at least System.err.println(e) should give you more detail. My guess is that ecipher.init() is throwing an exception because you're providing a 32-bit IV instead of 64 bits.
Perhaps looking at the source for javax.crypto.Cipher helps this make sense? I couldn't really figure it out, even finding the error message in the source. Good luck!