Aleksandr Kravets
Aleksandr Kravets

Reputation: 5947

Piping PrintWriter->CipherOutputStream->FileOutputStream

Here is my code:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class EncryptedLogger {

private static Date lastLogTime = null;
private static EncryptedLogger instance = null;
private static FileOutputStream fos = null;
private static CipherOutputStream cos = null;
private static PrintWriter writer = null;
private Cipher cipher;
byte[] Key ={(byte) 0x12,(byte) 0x34,0x55,(byte) 0x66,0x67,(byte)0x88,(byte)0x90,0x12,(byte) 0x23,0x45,0x67,(byte)0x89,0x12,0x33,(byte) 0x55,0x74};

public static EncryptedLogger getInstance(){
    if (instance==null) {
        instance = new EncryptedLogger();
    }
    return instance;
}

private EncryptedLogger(){

    class SQLShutdownHook extends Thread{
        @Override
        public void run() {
            EncryptedLogger.close();
            super.run();
        }
    }

    SecretKeySpec sks = new SecretKeySpec(Key,"AES");
    try {
        cipher = Cipher.getInstance("AES/ECB/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE,sks);

        fos = new FileOutputStream(new File("log.txt"),true);
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    cos = new CipherOutputStream(fos, cipher);
    writer = new PrintWriter(cos);

    SQLShutdownHook hook = new SQLShutdownHook();
    Runtime.getRuntime().addShutdownHook(hook);
}

public synchronized void logSQL(String s){
    if ((lastLogTime==null)||((new Date().getTime() -lastLogTime.getTime())>1000)){
        lastLogTime = new Date();
        writer.printf("-- %1$tm-%1$te-%1$tY %1$tH-%1$tM-%1$tS\n%2$s\n",new Date(),s);   
    }
    else{
        writer.println(s);
    }
}

public synchronized void logComment(String s){
    writer.printf("-- %1$tm-%1$te-%1$tY %1$tH-%1$tM-%1$tS: %2$s\n",new Date(),s);
}

public static void close(){
    writer.flush();
    writer.close();
}

public static void main(String[] args) throws InterruptedException {
    EncryptedLogger.getInstance().logSQL("1");
    EncryptedLogger.getInstance().logSQL("22");
    EncryptedLogger.getInstance().logSQL("33333");
    EncryptedLogger.getInstance().logSQL("4900");
    EncryptedLogger.getInstance().logSQL("5");
    EncryptedLogger.getInstance().logSQL("66666");
    EncryptedLogger.getInstance().logSQL("Some test logging statement");
    EncryptedLogger.getInstance().logSQL("AAAAAAAAAAAAAAAAAAAAAAAAAA");
    EncryptedLogger.getInstance().logComment("here is test commentary");
}

}

As you see i'm trying to encrypt text entries piping them through PrintWriter->CipherOutputStream->FileOutputStream chain. But when I decrypt result file there are missing bytes. I tried to flush cos and fos in EncryptedLogger.close() method - same result. Obviously i'm missing something. What is wrong?

EDIT: here is decryption code i use. It's not mine, taken from tutorial or something... And it works fine when using simmilar encryption. But when using my code...

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class AESDecrypter
{
        Cipher dcipher;

        public AESDecrypter(SecretKey key)
        {

                try
                {
                        dcipher = Cipher.getInstance("AES");
                        dcipher.init(Cipher.DECRYPT_MODE, key);
                }
                catch (Exception e)
                {
                        e.printStackTrace();
                }
        }

        byte[] buf = new byte[1024];

        public void decrypt(InputStream in, OutputStream out)
        {
            System.out.println("decrypting");
            try
                {
                        in = new CipherInputStream(in, dcipher);
                        int numRead = 0;
                        while ((numRead = in.read(buf)) >= 0)
                        {
                                out.write(buf, 0, numRead);
                        }
                        out.close();
                }
                catch (java.io.IOException e)
                {
                }
        }

        public static void main(String args[])
        {
                try
                {
                        byte[] keystr ={(byte) 0x12,(byte) 0x34,0x55,(byte) 0x66,0x67,(byte)0x88,(byte)0x90,0x12,(byte) 0x23,0x45,0x67,(byte)0x89,0x12,0x33,(byte) 0x55,0x74};
                        SecretKeySpec sks = new SecretKeySpec(keystr,"AES");                        
                        AESDecrypter encrypter = new AESDecrypter(sks);
                        encrypter.decrypt(new FileInputStream("sqllogenc.log"),new FileOutputStream("sqllogdec.log"));
                }
                catch (Exception e)
                {
                        e.printStackTrace();
                }
        }
}

EDIT2: when i write directly to fos i get this output:

-- 04-19-2012 16-17-56
1
22
33333
4900
5
66666 + delay starting 1100
Some test logging statement
AAAAAAAAAAAAAAAAAAAAAAAAAA
-- 04-19-2012 16-17-56: here is test commentary

and when writing using cos and decrypting:

-- 04-19-2012 16-22-13
1
22
33333
4900
5
66666 + delay starting 1100
Some test logging statement
AAAAAAAAAAAAAAAAAAAAAAAAAA
-- 04-19-2012 16-22-13: here 

As you see part of the last line is missing including linebreak.

Upvotes: 0

Views: 1040

Answers (2)

Sorin
Sorin

Reputation: 1985

Well, AES has a fixed block size of 128 bits. When you use AES/ECB/NoPadding, you take the responsability of making sure the size of your message is a multiple of the block size.

It probably isn't, so you get less text when you decrypt.

You should use AES/ECB/NoPadding for arbitrary length of text.

Upvotes: 1

axtavt
axtavt

Reputation: 242686

You should use the same cryptographic transformation (such as AES/ECB/NoPadding) at both sides. Also, note that NoPadding mode doesn't allow you to pass data of arbitrary size, therefore you need to specify some other kind of padding.

So, you need to construct Ciphers as Cipher.getInstance("AES/ECB/PKCS5Padding") at both sides.

Also, note the suggestion of rossum about use of CBC or CTR instead of ECB.

Upvotes: 1

Related Questions