Encrypt file with FileReader - java

In my java program I would like to read a .txt file in and encode it afterwards. I know how to read a File in and tried to learn how to encode an array. The problem I have is that I don't know how to combine it, it doesn't work the way I tried it.
Here's the part I can read in my text file with:
public class ReadFile {
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr);
String zeile = "";
do
{
zeile = br.readLine();
System.out.println(zeile);
}
while (zeile != null);
br.close();
}
}
In this part I can encrypt and decrypt bytes:
public class Crypt {
public static void main(String[] args) {
try{
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("DES");
byte[] text = "test".getBytes("UTF8");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desalgCipher.doFinal(text);
String s = new String(textEncrypted);
System.out.println(s);
desalgCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desalgCipher.doFinal(textEncrypted);
s = new String(textDecrypted);
System.out.println(s);
}
catch(Exception e)
{
System.out.println("Error");
}
}
}
I thought to read the text file in and put it in a string to encode it, but I think it is way too complex. Is there another way to connect them, or is another way for encoding required?

I strongly advise you to use Streams ( see https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html & https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html) rather than directly using a FileReader.
Encryption happens at a lower level (on bytes) than what you're trying to do.
Java ciphers offer the convenient CipherInputStream (and CipherOutputStream ) to encrypt byte streams on the fly. It's much cheaper and more scalable than trying to dump a whole file in a single byte[] (moreso because you're decoding and re-encoding the file content).
If you want an example of use, please look at the following snippet :
public static void encrypt(Path inputFile, OutputStream output) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
// init cipher
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("DES");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
try(InputStream is = Files.newInputStream(inputFile); // get an IS on your file
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher)){ // wraps input Stream with cipher
copyStreams(cipherIS, output); // copyStream is let to the implementer's choice.
}
}
And I'll let you figure out how to decrypt.
EDIT :
A common way to communicate encrypted bytes without fear for encoding issues is to encode the raw bytes with base 64.
You can wrap the outputStream with Base64.getEncoder().wrap(os)

FileReader/FileWriter are the wrong (old utility) classes, as they use the current platform encoding, and a file encrypted on one computer (Greek Windows) would not be decryptable on another computer (Linux server).
Text in java, String, is in Unicode. One cannot (should not) throw arbitrary bytes into a String.
So the following cannot be done
new String(textEncrypted); // Uses the default platform encoding
new String(textEncrypted, "UTF-8"); // Probably the bytes are not valid UTF-8
So do:
Path path = Paths.get("text.txt");
byte[] content = Files.readAllBytes(path);
content = encrypt(content);
Files.write(path, content);

Related

Store 3DES in String instead of CipherOutputStream

I'm trying to 3DES encrypt a string and store it in a properties file using this example. The problem I'm having is I do not want to write the contents of encrypt() and decrypt() to a file directly from the methods. I want to store it in a string for use later.
Below are the methods I'm using.
As you can see this uses CipherOutputStream and CipherInputStream. How would I read the result of both encrypt() and decrypt() into a String instead of writing it out to file?
public static void encrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException,
NoSuchPaddingException, IOException {
// Create and initialize the encryption engine
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
// Create a special output stream to do the work for us
CipherOutputStream cos = new CipherOutputStream(out, cipher);
// Read from the input and write to the encrypting output stream
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
cos.write(buffer, 0, bytesRead);
}
cos.close();
// For extra security, don't leave any plaintext hanging around memory.
java.util.Arrays.fill(buffer, (byte) 0);
}
/**
* Use the specified TripleDES key to decrypt bytes ready from the input
* stream and write them to the output stream. This method uses uses Cipher
* directly to show how it can be done without CipherInputStream and
* CipherOutputStream.
*/
public static void decrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException, IOException,
IllegalBlockSizeException, NoSuchPaddingException,
BadPaddingException {
// Create and initialize the decryption engine
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
// Read bytes, decrypt, and write them out.
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(cipher.update(buffer, 0, bytesRead));
}
// Write out the final bunch of decrypted bytes
out.write(cipher.doFinal());
out.flush();
}
Simple: instead of passing a file output stream to these methods - pass a different kind of stream, for example a ByteArrayOutputStream.
Then you can extract the encrypted data as string from that stream.
And to ensure that the final result is reasonable encoded, you should actually use some kind of Base64OutputStream which in turn writes to that ByteArrayOutputStream.

java - decrypt a file with base64

In my project a textfile is chosen and become encrypted. The encrypted text is saved seperatly as well as the key. Now I try to create a program which is decrypting the file when the right keyfile is available. I think the decrypting program needs to look pretty like the encrypting program just in DECRYPT_MODE. When I read in the key I don't know how to do the next step at it to decrypt the textfile. Maybe anyone can help me how I use the key from .txt file and use it to decrypt the encoded file.
The encrypting program:
public class encrypt {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
//Key is created and saved in File
KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
SecretKey myDesKey = keygenerator.generateKey();
String encodedKey = Base64.getEncoder().encodeToString(myDesKey.getEncoded());
Path keypath = Paths.get("C:/xxx/key.txt");
Path keyfile = Files.createFile(keypath);
Files.write(keyfile, encodedKey.getBytes(), StandardOpenOption.WRITE);
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("AES");
desalgCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
Path target = Paths.get("C:/xxx/encrypted.txt");
Path file = Files.createFile(target);
Path path = Paths.get("test.txt");
try(InputStream is = Files.newInputStream(path);
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher);
BufferedReader reader = new BufferedReader(new InputStreamReader(cipherIS));){
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
Files.write(file, line.getBytes(), StandardOpenOption.WRITE);
}
}
}
}
Decrypt: read in the key and decrypt it
public class decrypt {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException {
try {
File fileDir = new File("C:/Users/JT/Desktop/key.txt");
BufferedReader in = new BufferedReader(
new InputStreamReader(new FileInputStream(fileDir), "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
System.out.println(str);
}
in.close();
}
catch (UnsupportedEncodingException e)
{
System.out.println(e.getMessage());
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
byte[] decodedKey = Base64.getDecoder().decode(sb.toString());
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
SecretKeySpec key = new SecretKeySpec(sb.toString().getBytes(), "Base64");
Cipher desalgCipher;
desalgCipher = Cipher.getInstance("AES");
desalgCipher.init(Cipher.DECRYPT_MODE, key);
Path path = Paths.get("encrypted.txt"); // path to your file
try(InputStream is = Files.newInputStream(path); // get an IS on your file
CipherInputStream cipherIS = new CipherInputStream(is, desalgCipher); // wraps stream using cipher
BufferedReader reader = new BufferedReader(new InputStreamReader(cipherIS));){ // init reader.
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
}
}
}
}
Your application is not being programmed the right way. Currently you try to encrypt by wrapping the input stream with a CipherInputStream instance. Then this instance again is wrapped with a BufferedReader instance.
So what you are doing is to first convert the bytes of the input file - probably text - into ciphertext. This ciphertext can contain any byte value. Then you try to read those bytes in line-by-line using the default character set and line endings. Obviously after encryption even the notion of lines doesn't exist anymore, so you'll loose data in that final step.
Then you convert back to bytes, which you then (somehow) try to decrypt. This will obviously fail as you lost data during the readLine statement.
What you should do is to read in the file using bytes. You can then write to a CipherOutputStream. If the file with the ciphertext needs to be actual text you can use a Base64 stream which the new java.util.Base64 nicely provides.
Only once you programmed the encryption correctly you can try and reverse the process. As long as data is lost obviously the decryption will fail (with an error or garbage output, depending on the mode and your luck).
If you're unlucky you will end up with code that works 99% of the time. So good luck and heed the comments: don't try and perform encryption without understanding what you're doing. It will end with tears - or a smashed keyboard.

Chunking AES symmetric encryption

Use case 1 (working baseline):
Use case one is straightforward and is implemented / working.
In Java, Write a stream to disk in a single fell swoop.
Wrap output stream with symmetric cipher so that contents on disk are encrypted.
Later, read from disk. Wrap input stream with same symmetric cipher
in a single fell swoop so that contents retrieved from input stream
are plaintext and match original.
Use case 2 (no suitable solution determined):
In Java, Write a stream to disk.
Allow for subsequent bytes ("chunks") to be appended to file.
Wrap output stream with symmetric cipher so that contents on disk are encrypted.
Use same cipher so that all chunks are encrypted in the same manner.
Later, read from disk. Wrap input stream with same symmetric cipher
in a single fell swoop so that contents retrieved from input stream
are plaintext and match original.
Problem statement:
Encrypting and decrypting "abc" does not yield the same result as encrypting and decrypting "a", "b", and "c" separately, and therefore the "chunked" file described in use case 2 will no be successfully decrypted.
// e.g.
decrypt(encrypt("abc")) != decrypt(encrypt("a") + encrypt("b") + encrypt("c"))
The Actual Question:
... so the question is, how might one configure a Java cipher stream that can encrypt one chunk at a time, (a) without having prior knowledge of encrypted chunks, and (b) be decipherable using a single input stream cipher wrapper (without requiring knowledge of indexes where file was appended)...
Unfortunately, in this case you can't have your cake and eat it too.
You must either
write some length bytes at the start of each chunk, or
use an encryption algorithm where decrypt(encrypt("abc")) == decrypt(encrypt("a") + encrypt("b") + encrypt("c")) (aka trivial, and not recommended)
Number 1 is definitely a better choice, and is easier than you might think. Details below.
Number 2, you could use something like a Vigenere cipher, which would allow you to decrypt the whole file in one fell swoop, but would be a compromise in terms of encryption strength.
Details on number 1
The way you would do this is by reserving, for instance, four bytes (a 32-bit integer) at the beginning of each chunk. This integer represents the length of the chunk. To decrypt you would therefore:
Read the first four bytes and convert to integer n.
Read the next n bytes and decrypt.
Read the next four bytes and convert to integer n.
Read the next n bytes, decrypt and append to the first decrypted chunk.
Repeat steps 3 and 4 until end of file is reached.
And obviously this makes the chunk encryption easy because all you have to do is first write how many encrypted bytes you're about to append.
I found a solution close enough to my particular problem (stealing from this post), albeit slightly different from the problem statement (not a single stream).
public static void appendAES(File file, byte[] data, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
RandomAccessFile rfile = new RandomAccessFile(file,"rw");
byte[] iv = new byte[16];
byte[] lastBlock = null;
if (rfile.length() % 16L != 0L) {
throw new IllegalArgumentException("Invalid file length (not a multiple of block size)");
} else if (rfile.length() == 16) {
throw new IllegalArgumentException("Invalid file length (need 2 blocks for iv and data)");
} else if (rfile.length() == 0L) {
// new file: start by appending an IV
new SecureRandom().nextBytes(iv);
rfile.write(iv);
// we have our iv, and there's no prior data to reencrypt
} else {
// file length is at least 2 blocks
rfile.seek(rfile.length()-32); // second to last block
rfile.read(iv); // get iv
byte[] lastBlockEnc = new byte[16];
// last block
// it's padded, so we'll decrypt it and
// save it for the beginning of our data
rfile.read(lastBlockEnc);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
lastBlock = cipher.doFinal(lastBlockEnc);
rfile.seek(rfile.length()-16);
// position ourselves to overwrite the last block
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] out;
if (lastBlock != null) { // lastBlock is null if we're starting a new file
out = cipher.update(lastBlock);
if (out != null) rfile.write(out);
}
out = cipher.doFinal(data);
rfile.write(out);
rfile.close();
}
public static void decryptAES(File file, OutputStream out, byte[] key) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// nothing special here, decrypt as usual
FileInputStream fin = new FileInputStream(file);
byte[] iv = new byte[16];
if (fin.read(iv) < 16) {
throw new IllegalArgumentException("Invalid file length (needs a full block for iv)");
};
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key,"AES"), new IvParameterSpec(iv));
byte[] buff = new byte[1<<13]; //8kiB
while (true) {
int count = fin.read(buff);
if (count == buff.length) {
out.write(cipher.update(buff));
} else {
out.write(cipher.doFinal(buff,0,count));
break;
}
}
fin.close();
}
public static void main(String[] args) throws Exception {
// prep the new encrypted output file reference
File encryptedFileSpec = File.createTempFile("chunked_aes_encrypted.", ".test");
// prep the new decrypted output file reference
File decryptedFileSpec = File.createTempFile("chunked_aes_decrypted.", ".test");
// generate a key spec
byte[] keySpec = new byte[]{0,12,2,8,4,5,6,7, 8, 9, 10, 11, 12, 13, 14, 15};
// for debug/test purposes only, keep track of what's written
StringBuilder plainTextLog = new StringBuilder();
// perform chunked output
for (int i = 0; i<1000; i++) {
// generate random text of variable length
StringBuilder text = new StringBuilder();
Random rand = new Random();
int n = rand.nextInt(5) + 1;
for (int j = 0; j < n; j++) {
text.append(UUID.randomUUID().toString()); // append random string
}
// record it for later comparison
plainTextLog.append(text.toString());
// write it out
byte[] b = text.toString().getBytes("UTF-8");
appendAES(encryptedFileSpec, b, keySpec);
}
System.out.println("Encrypted " + encryptedFileSpec.getAbsolutePath());
// decrypt
decryptAES(encryptedFileSpec, new FileOutputStream(decryptedFileSpec), keySpec);
System.out.println("Decrypted " + decryptedFileSpec.getAbsolutePath());
// compare expected output to actual
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] expectedDigest = md.digest(plainTextLog.toString().getBytes("UTF-8"));
byte[] expectedBytesEncoded = Base64.getEncoder().encode(expectedDigest);
System.out.println("Expected decrypted content: " + new String(expectedBytesEncoded));
byte[] actualBytes = Files.readAllBytes(Paths.get(decryptedFileSpec.toURI()));
byte[] actualDigest = md.digest(actualBytes);
byte[] actualBytesEncoded = Base64.getEncoder().encode(actualDigest);
System.out.println("> Actual decrypted content: " + new String(actualBytesEncoded));
}

Simplest way to encrypt a text file in java

For my School project I had to show that I can utilize file handling within a program. For this I made a very simple login process that you can create an account on that writes a username and password to a text file located in the resource folder. Obviously this has no security at all as it wasn't designed to be secure just to showcase file handling however my teacher has said that I should attempt to add some encryption to the file as well to get a better grade.
I have done some research and many people are recommending DES.
The problem I'm having is I don't have much time left for my project and need to finish it asap. Using DES seems like it would take a while to implement all the extra code.
In my program I am using a simple lineNumberReader to read the files line by line. To write to the files I am using a BufferedWriter.
Is there anyway to encrypt this data very simply? It doesn't have to be very secure but I need to show that I have atleast attempted to encrypt the data. The encryption and decryption would all be completed on the same application as data isn't being transferred.
Potentially a way I can create a very simple encryption and decryption algorithm myself?
Try this,... Its pretty simple
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class HelloWorld{
public static void main(String[] args) {
try{
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desCipher;
desCipher = Cipher.getInstance("DES");
byte[] text = "No body can see me.".getBytes("UTF8");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
String s = new String(textEncrypted);
System.out.println(s);
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desCipher.doFinal(textEncrypted);
s = new String(textDecrypted);
System.out.println(s);
}catch(Exception e)
{
System.out.println("Exception");
}
}
}
So basically before writing to file you will encrypt and after reading you will need to decrypt it.
An easy and fun scrambling algorithm would be the Burrows-Wheeler transform. Not really a secure encryption, but seriously, it's a school work and this is awesome.
use simple subtitute encryption algorythm, change every character into number or other character.
get every character of your string.
get the ascii value of the string.
add the ascii value with specific integer (this will be your encryption key)
display the result
A very basic method would be to xor the data with a key. This method is symmetrical, i.e you can use the same key to decode as encode.
If we choose a 1 byte key it's nice and simple, enough to make it unreadable (but not at all secure!):
private void encodeDecode(byte[] bytes, byte key) {
for(int i=0; i<bytes.length; i++)
bytes[i] = (byte) (bytes[i]^key);
}
You could use a simple ceasar cipher (http://en.wikipedia.org/wiki/Caesar_cipher)
public class Cipher {
public static void main(String[] args) {
String str = "The quick brown fox Jumped over the lazy Dog";
System.out.println( Cipher.encode( str, 12 ));
System.out.println( Cipher.decode( Cipher.encode( str, 12), 12 ));
}
public static String decode(String enc, int offset) {
return encode(enc, 26-offset);
}
public static String encode(String enc, int offset) {
offset = offset % 26 + 26;
StringBuilder encoded = new StringBuilder();
for (char i : enc.toCharArray()) {
if (Character.isLetter(i)) {
if (Character.isUpperCase(i)) {
encoded.append((char) ('A' + (i - 'A' + offset) % 26 ));
} else {
encoded.append((char) ('a' + (i - 'a' + offset) % 26 ));
}
} else {
encoded.append(i);
}
}
return encoded.toString();
}
}
Found at http://rosettacode.org/wiki/Caesar_cipher#Java
Note that Java has native solutions for encryption and when it comes to passwords, it is much better to just hash them and compare hashes as there usually is no need to decrypt them.
I don't know who recommends DES to encrypt password.
I suggest you to follow these step if you would to impress your teacher:
cite your reference as theoric support to your cryptographic solution. I sugget you this OWSAP - Password Storage Cheat Sheet
explain where your code meets specification. For a good tutorial with sample code I suggest you this secure password hash
This solution makes your project real and you can reuse it to pass the exam of your future Crypto Module :) . Otherwise I like the solution proposed from StanislavL.
Enjoy!
Bouncy Castle Crypto API is a lightweight cryptography API in Java.
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.modes.*;
import org.bouncycastle.crypto.params.*;
// A simple example that uses the Bouncy Castle
// lightweight cryptography API to perform DES
// encryption of arbitrary data.
public class Encryptor {
private BufferedBlockCipher cipher;
private KeyParameter key;
// Initialize the cryptographic engine.
// The key array should be at least 8 bytes long.
public Encryptor( byte[] key ){
/*
cipher = new PaddedBlockCipher(
new CBCBlockCipher(new DESEngine()));
*/
cipher = new PaddedBlockCipher(
new CBCBlockCipher(new BlowfishEngine()));
this.key = new KeyParameter( key );
}
// Initialize the cryptographic engine.
// The string should be at least 8 chars long.
public Encryptor( String key ){
this( key.getBytes());
}
// Private routine that does the gritty work.
private byte[] callCipher( byte[] data )
throws CryptoException {
int size = cipher.getOutputSize( data.length );
byte[] result = new byte[ size ];
int olen = cipher.processBytes(data,0,data.length result, 0);
olen += cipher.doFinal( result, olen );
if( olen < size ){
byte[] tmp = new byte[ olen ];
System.arraycopy(
result, 0, tmp, 0, olen );
result = tmp;
}
return result;
}
// Encrypt arbitrary byte array, returning the
// encrypted data in a different byte array.
public synchronized byte[] encrypt( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return new byte[0];
}
cipher.init( true, key );
return callCipher( data );
}
// Encrypts a string.
public byte[] encryptString( String data )
throws CryptoException {
if( data == null || data.length() == 0 ){
return new byte[0];
}
return encrypt( data.getBytes() );
}
// Decrypts arbitrary data.
public synchronized byte[] decrypt( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return new byte[0];
}
cipher.init( false, key );
return callCipher( data );
}
// Decrypts a string that was previously encoded
// using encryptString.
public String decryptString( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return "";
}
return new String( decrypt( data ) );
}
}
There are too many ways to encrypted simple string in Java. If it is a school project , I really don't think you can get a higher band by simply using some third-part libs to finish the encrypted work.
If you have some time, you could try to understand how Base64 works, then try to create some encrypted algorithm by yourself.
How ever, if you insist to use some API in Java , I have to say that DES is really old way to encrypted text , 3DEs(DESede) or AES will be better and safer , both of them have already been supported since Java6.
If you have to import the BouncyCastle lib , I prefer IDEA, it's one of the safest algorithm, may have you achieve a good score.
I won't give you any demo code, but you can easily find some by google all the algorithm I have mentioned.
public class CryptoUtils {
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile,
File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file", ex);
}
}
}
package net.codejava.crypto;
import java.io.File;
public class CryptoException extends Exception {
public CryptoException() {
}
public CryptoException(String message, Throwable throwable) {
super(message, throwable);
}
public static void main(String[] args) {
String key = "Mary has one cat1";
File inputFile = new File("document.txt");
File encryptedFile = new File("document.encrypted");
File decryptedFile = new File("document.decrypted");
try {
CryptoUtils.encrypt(key, inputFile, encryptedFile);
CryptoUtils.decrypt(key, encryptedFile, decryptedFile);
} catch (CryptoException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
}
My suggestion: don't use encryption at all.
Here's something better:(I hope)
Scanner sc=new Scanner(System.in);
String name=sc.next();
//for inputting user name
File f= new File("d://"+name+".txt");
if(f.exists())
{
if(f.lastModified()!=0)
{
System.out.println("Account data tampered...cannot be accessed");
}
else{
String data="";
System.out.println(data); //data should contain
//data from file read using BufferedReader
f.setLastModified(0);
}
}
else
{
f.createNewFile();//Write whatever you want to to the file
f.setLastModified(0);
}
So, you can effectively know whether the user has tampered with the text file with the details and display an error message if the tampered account is used.
However, This does not prevent the user from changing the file, it will just prevent a tampered account from being used....I think your computer teacher might like this.
You could also do:
f.setReadOnly();
and when you write to the file,
f.setWritable(true,true),
then after closing the output stream,
f.setReadOnly();
again...
But the file can still be replaced, therefore the 1st and is more
Effective.
Thanks
you can use these function to encrypt and decrypt simple text
//Encrypt simple text
public String EncryptSimpleText (String text2Encrypt) throws Exception {
byte[] encryptArray = Base64.getEncoder().encode(text2Encrypt.getBytes());
return new String(encryptArray,"UTF-8");
}
//Decrypt simple text
public String Decrypt2SimpleText(String textEncrypted) throws Exception {
byte[] dectryptArray = textEncrypted.getBytes();
byte[] decarray = Base64.getDecoder().decode(dectryptArray);
return new String(decarray,"UTF-8");
}

Java - Missing final characters when encrypting using blowfish

I am using some java code that encrypts the contents of a text file using Blowfish. When I convert the encrypted file back (i.e. decrypt it) the string is missing a character from the end. Any ideas why? I am very new to Java and have been fiddling with this for hours with no luck.
The file war_and_peace.txt just contains the string "This is some text". decrypted.txt contains "This is some tex" (with no t on the end). Here is the java code:
public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
}
public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
}
private static byte[] getBytes(String toGet)
{
try
{
byte[] retVal = new byte[toGet.length()];
for (int i = 0; i < toGet.length(); i++)
{
char anychar = toGet.charAt(i);
retVal[i] = (byte)anychar;
}
return retVal;
}catch(Exception e)
{
String errorMsg = "ERROR: getBytes :" + e;
return null;
}
}
public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
String iv = "12345678";
byte[] IVBytes = getBytes(iv);
IvParameterSpec IV = new IvParameterSpec(IVBytes);
byte[] KeyData = key.getBytes();
SecretKeySpec blowKey = new SecretKeySpec(KeyData, "Blowfish");
//Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(Cipher.ENCRYPT_MODE, blowKey, IV);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
} else if (mode == Cipher.DECRYPT_MODE) {
cipher.init(Cipher.DECRYPT_MODE, blowKey, IV);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
}
public static void doCopy(InputStream is, OutputStream os) throws IOException {
byte[] bytes = new byte[4096];
//byte[] bytes = new byte[64];
int numBytes;
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
os.flush();
os.close();
is.close();
}
public static void main(String[] args) {
//Encrypt the reports
try {
String key = "squirrel123";
FileInputStream fis = new FileInputStream("war_and_peace.txt");
FileOutputStream fos = new FileOutputStream("encrypted.txt");
encrypt(key, fis, fos);
FileInputStream fis2 = new FileInputStream("encrypted.txt");
FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
decrypt(key, fis2, fos2);
} catch (Throwable e) {
e.printStackTrace();
}
}
`
There is a couple of things not optimal here.
But let's first solve your problem. The reason why the last portion of your input is somehow missing is the padding you specify: none! Without specifying a padding, the Cipher can just operate on full-length blocks (8 bytes for Blowfish). Excess input that is less than a block long will be silently discarded, and there's your missing text. In detail: "This is some text" is 17 bytes long, so two full blocks will be decrypted, and the final 17th byte, "t", will be discarded.
Always use a padding in combination with symmetric block ciphers, PKCS5Padding is fine.
Next, when operating with Cipher, you don't need to implement your own getBytes() - there's String#getBytes already doing the job for you. Just be sure to operate on the same character encoding when getting the bytes and when reconstructing a String from bytes later on, it's a common source of errors.
You should have a look at the JCE docs, they will help you avoiding some of the common mistakes.
For example, using String keys directly is a no-go for symmetric cryptography, they do not contain enough entropy, which would make it easier to brute-force such a key. The JCE gives you theKeyGenerator class and you should always use it unless you know exactly what you are doing. It generates a securely random key of the appropriate size for you, but in addition, and that is something people tend to forget, it will also ensure that it doesn't create a weak key. For example, there are known weak keys for Blowfish that should be avoided in practical use.
Finally, you shouldn't use a deterministic IV when doing CBC encryption. There are some recent attacks that make it possible to exploit this, resulting in total recovery of the message, and that's obviously not cool. The IV should always be chosen at random (using a SecureRandom) in order to make it unpredictable. Cipher does this for you by default, you can simply obtain the used IV after encryption with Cipher#getIV.
On another note, less security-relevant: you should close streams in a finally block to ensure they're closed at all cost - otherwise you will be left with an open file handle in case of an exception.
Here's an updated version of your code that takes all these aspects into account (had to use Strings instead of files in main, but you can simply replace it with what you had there):
private static final String ALGORITHM = "Blowfish/CBC/PKCS5Padding";
/* now returns the IV that was used */
private static byte[] encrypt(SecretKey key,
InputStream is,
OutputStream os) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
return cipher.getIV();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static void decrypt(SecretKey key,
byte[] iv,
InputStream is,
OutputStream os)
{
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static void doCopy(InputStream is, OutputStream os)
throws IOException {
try {
byte[] bytes = new byte[4096];
int numBytes;
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
} finally {
is.close();
os.close();
}
}
public static void main(String[] args) {
try {
String plain = "I am very secret. Help!";
KeyGenerator keyGen = KeyGenerator.getInstance("Blowfish");
SecretKey key = keyGen.generateKey();
byte[] iv;
InputStream in = new ByteArrayInputStream(plain.getBytes("UTF-8"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
iv = encrypt(key, in, out);
in = new ByteArrayInputStream(out.toByteArray());
out = new ByteArrayOutputStream();
decrypt(key, iv, in, out);
String result = new String(out.toByteArray(), "UTF-8");
System.out.println(result);
System.out.println(plain.equals(result)); // => true
} catch (Exception e) {
e.printStackTrace();
}
}
You have your CipherInputStream and CipherOutputStream mixed up. To encrypt, you read from a plain inputstream and write to a CipherOutputStream. To decrypt ... you get the idea.
EDIT:
What is happening is that you have specified NOPADDING and you are attempting to encrypt using a CipherInputStream. The first 16 bytes form two valid complete blocks and so are encrypted correctly. Then there is only 1 byte left over, and when the CipherInputStream class receives the end-of-file indication it performs a Cipher.doFinal() on the cipher object and receives an IllegalBlockSizeException. This exception is swallowed, and read returns -1 indicating end-of-file. If however you use PKCS5PADDING everything should work.
EDIT 2:
emboss is correct in that the real issue is simply that it is tricky and error-prone to use the CipherStream classes with the NOPADDING option. In fact, these classes explicitly state that they silently swallow every Security exception thrown by the underlying Cipher instance, so they are perhaps not a good choice for beginners.
Keys are binary, and String is not a container for binary data. Use a byte[].
When I had this problem I had to call doFinal on the cipher:
http://docs.oracle.com/javase/1.4.2/docs/api/javax/crypto/Cipher.html#doFinal()

Categories