Piping PrintWriter-> CipherOutputStream-> FileOutputStream

时间:2012-04-19 12:32:02

标签: java encryption aes

这是我的代码:

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");
}

}

如您所见,我正在尝试通过PrintWriter-> CipherOutputStream-> FileOutputStream链来加密文本条目。但是当我解密结果文件时,会丢失字节。我尝试在EncryptedLogger.close()方法中刷新 cos fos - 结果相同。显然我错过了一些东西。有什么问题?

编辑:这是我使用的解密代码。这不是我的,取自教程或其他东西...... 使用simmilar加密时它可以正常工作。但是在使用我的代码时......

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 :当我直接写 fos 时,我会收到此输出:

-- 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

使用 cos 进行书写并解密时:

-- 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 

如您所见,最后一行的部分内容已丢失,包括换行符。

2 个答案:

答案 0 :(得分:1)

您应该在两侧使用相同的加密转换(例如AES/ECB/NoPadding)。另请注意,NoPadding模式不允许您传递任意大小的数据,因此您需要指定其他类型的填充。

因此,您需要将Cipher s构建为Cipher.getInstance("AES/ECB/PKCS5Padding")两侧。

另外,请注意关于使用CBCCTR代替ECB的rossum建议。

答案 1 :(得分:1)

嗯,AES的固定块大小为128位。 当您使用AES / ECB / NoPadding时,您需要负责确保消息的大小是块大小的倍数。

可能不是,所以当你解密时你会得到更少的文字。

您应该使用AES / ECB / NoPadding来处理任意长度的文本。