AES加密,在解密文件中获得了额外的垃圾字符 [英] AES encryption, got extra trash characters in decrypted file

查看:1457
本文介绍了AES加密,在解密文件中获得了额外的垃圾字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Android应用程式中使用调试登录功能。
我有一个简单的类,它是使用128位AES加密记录到.txt文件。

记录完成后, 我用简单的JAVA程序解密记录的文件。



问题是当我解密加密日志



Android应用程式记录部分:

  public class FileLogger {

//文件和文件夹名称
public static String LOG_FILE_NAME =my_log.txt;
public static String LOG_FOLDER_NAME =my_log_folder;

static SimpleDateFormat formatter = new SimpleDateFormat(yyyy-MM-dd_HH-mm-ss_SSS);

//我的秘密密钥,16字节= 128位
静态字节[] key = {1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6};

//使用加密附加到日志文件
public static void appendToLog(Context context,Object msg){

String msgStr;
String timestamp =t:+ formatter.format(new java.util.Date());

msgStr = msg +| + timestamp +\\\
;

文件sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath()+/+ LOG_FOLDER_NAME);
if(!dir.exists()){
dir.mkdir();
}

文件encryptedFile =新文件(dir,LOG_FILE_NAME);

try {

//使用上面定义的密钥加密
密钥secretKey = new SecretKeySpec(key,AES);
Cipher cipher = Cipher.getInstance(AES);
cipher.init(Cipher.ENCRYPT_MODE,secretKey);

byte [] outputBytes = cipher.doFinal(msgStr.getBytes());

//使用追加模式写入文件
FileOutputStream outputStream = new FileOutputStream(encryptedFile,true);
outputStream.write(outputBytes);
outputStream.close();


} catch(FileNotFoundException e){
e.printStackTrace();
} catch(IOException e){
e.printStackTrace();
} catch(NoSuchAlgorithmException e){
e.printStackTrace();
} catch(NoSuchPaddingException e){
e.printStackTrace();
} catch(IllegalBlockSizeException e){
e.printStackTrace();
} catch(BadpaddingException e){
e.printStackTrace();
} catch(InvalidKeyException e){
e.printStackTrace();
}

}

}

这是解密的JAVA程序:

  public class Main {



//解密后输出文件名
private static String decryptptedFileName;
//输入加密文件
private static String fileSource;
//输出文件名的前缀标签
private static String outputFilePrefix =decryptpted_;
//我的密钥用于解密,它与encrypter程序中的相同。
static byte [] key = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};

//解密函数
public static void decrypt(byte [] key,File inputFile,File outputFile)throws Exception {
try {

secretKey = new SecretKeySpec(key,AES);
Cipher cipher = Cipher.getInstance(AES);
cipher.init(Cipher.DECRYPT_MODE,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,true);
outputStream.write(outputBytes);

inputStream.close();
outputStream.close();

} catch(Exception ex){
ex.printStackTrace();
}
}

//第一个参数是intput文件源
public static void main(String [] args){

if(args.length!= 1){
System.out.println(Add log file name as a parameter。);

} else {
fileSource = args [0];

try {
文件sourceFile =新文件(fileSource);
if(sourceFile.exists()){

//解密
decryptptedFileName = outputFilePrefix + sourceFile.getName();
文件decryptedFile =新文件(decryptedFileName);
decrypt(key,sourceFile,decryptedFile);
} else {
System.out.println(Log file not found:+ fileSource);
}

} catch(Exception e){
e.printStackTrace();
}

System.out.println(Decryption done,output file:+ decryptptedFileName);
}

}

}

输出解密日志(使用notepad ++打开)





T 这里是有效的内容,但你也可以看到额外的thrash字符。 strong>如果我用默认的窗口文本编辑器打开我也有thrash charaters,但不同的。



这是我第一次尝试与encrypt -decrypt,什么mi做错了?
任何想法?

解决方案

AES是一种只在块上工作的块密码。要加密的明文可以是任何长度,因此密码必须始终填充明文,以将其填充到块大小的倍数(或者当它已经是块大小的倍数时添加完整块)。在这个PKCS#5 / PKCS#7填充中,每个填充字节表示填充字节的数目。



很容易解决的是迭代 outputBytes ,并删除那些总是在下一行的填充字节。这将在你使用多行日志消息或使用语义安全模式(稍后更多)时中断。



更好的解决方法是写入字节数对于消息之前的每个日志消息,读取和解密只有那么多字节。



您目前使用 Cipher.getInstance(AES); 这是 Cipher.getInstance(AES / ECB / PKCS5Padding); 的非完全限定版本。 ECB模式不是语义安全的。它简单地用AES和密钥加密每个块(16字节)。因此,相同的块在密文中将是相同的。这是特别糟糕的,因为一些日志消息开始相同,攻击者可能能够区分它们。这也是为什么整个文件的解密工作,尽管在块中加密的原因。您应该使用随机IV的CBC模式。



这里是一些示例代码,用于在CBC模式下正确使用AES,随机IV使用流:

  private static SecretKey key = generateAESkey(); 
private static String cipherString =AES / CBC / PKCS5Padding;

public static void main(String [] args)throws Exception {
ByteArrayOutputStream log = new ByteArrayOutputStream();
appendToLog(Test1,log);
appendToLog(Test2 is longer,log);
appendToLog(Test3是块大小的倍数!,log);
appendToLog(Test4 is short。,log);

byte [] encLog = log.toByteArray();

List< String> logs = decryptLog(new ByteArrayInputStream(encLog));

for(String logLine:logs){
System.out.println(logLine);
}
}

private static SecretKey generateAESkey(){
try {
return KeyGenerator.getInstance(AES)。generateKey
} catch(NoSuchAlgorithmException e){
e.printStackTrace();
}
return null;
}

private static byte [] generateIV(){
SecureRandom random = new SecureRandom();
byte [] iv = new byte [16];
random.nextBytes(iv);
return iv;
}

public static void appendToLog(String s,OutputStream os)throws Exception {
Cipher cipher = Cipher.getInstance(cipherString);
byte [] iv = generateIV();
cipher.init(Cipher.ENCRYPT_MODE,key,new IvParameterSpec(iv));
byte [] data = cipher.doFinal(s.getBytes(UTF-8));
os.write(data.length);
os.write(iv);
os.write(data);
}

public static List< String> decryptLog(InputStream is)throws Exception {
ArrayList< String> logs = new ArrayList< String>();
while(is.available()> 0){
int len = is.read();
byte [] encLogLine = new byte [len];
byte [] iv = new byte [16];
is.read(iv);
is.read(encLogLine);

Cipher cipher = Cipher.getInstance(cipherString);
cipher.init(Cipher.DECRYPT_MODE,key,new IvParameterSpec(iv));
byte [] data = cipher.doFinal(encLogLine);
logs.add(new String(data,UTF-8));
}
返回日志;
}


Im making a debug loggin function in an android app. I have a simple class which is logging to .txt file using 128 bit AES encryption.

After the logging is done, i decrypt the logged file with a simple JAVA program.

The problem is when i decrypt the encrypted log i got some weird content in it, i also got the encrypted content, but there are some extra characters, see below.

Android app logging part:

public class FileLogger {

//file and folder name
public static String LOG_FILE_NAME = "my_log.txt";
public static String LOG_FOLDER_NAME = "my_log_folder";

static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS");

//My secret key, 16 bytes = 128 bit
static byte[] key = {1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};

//Appends to a log file, using encryption
public static void appendToLog(Context context, Object msg) {

    String msgStr;
    String timestamp = "t:" + formatter.format(new java.util.Date());

    msgStr = msg + "|" + timestamp + "\n";

    File sdcard = Environment.getExternalStorageDirectory();
    File dir = new File(sdcard.getAbsolutePath() + "/" + LOG_FOLDER_NAME);
    if (!dir.exists()) {
        dir.mkdir();
    }

    File encryptedFile = new File(dir, LOG_FILE_NAME);

    try {

        //Encryption using my key above defined
        Key secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        byte[] outputBytes = cipher.doFinal(msgStr.getBytes());

        //Writing to the file using append mode
        FileOutputStream outputStream = new FileOutputStream(encryptedFile, true);
        outputStream.write(outputBytes);
        outputStream.close();


    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }

}

}

And this is the decrypter JAVA program:

public class Main {



//output file name after decryption
private static String decryptedFileName;
//input encrypted file
private static String fileSource;
//a prefix tag for output file name
private static String outputFilePrefix = "decrypted_";
//My key for decryption, its the same as in the encrypter program.
static byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };

//Decrypting function
public static void decrypt(byte[] key, File inputFile, File outputFile) throws Exception {
    try {

        Key secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, 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, true);
        outputStream.write(outputBytes);

        inputStream.close();
        outputStream.close();

    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

//first argument is the intput file source
public static void main(String[] args) {

    if (args.length != 1) {
        System.out.println("Add log file name as a parameter.");

    } else {
        fileSource = args[0];

        try {
            File sourceFile = new File(fileSource);
            if (sourceFile.exists()) {

                //Decrption
                decryptedFileName = outputFilePrefix + sourceFile.getName();
                File decryptedFile = new File(decryptedFileName);
                decrypt(key, sourceFile, decryptedFile);
            } else {
                System.out.println("Log file not found: " + fileSource);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Decryption done, output file: " + decryptedFileName);
    }

}

}

Output decrypted log (Opened with notepad++):

There is the valid content, but you also can see the extra thrash characters. If I open with the default windows text editor i also got thrash charaters, but different ones.

This is my first try with encrypt -decrypt, what m i doing wrong? Any ideas?

解决方案

AES is a block cipher which only works on blocks. The plaintext that you want to encrypt can be of any length, so the cipher must always pad the plaintext to fill it up to a multiple of the block size (or add a complete block when it already is a multiple of the block size). In this PKCS#5/PKCS#7 padding each padding byte denotes the number of padded bytes.

The easy fix would be to iterate over outputBytes during decryption and remove those padding bytes which are always on the next line. This will break as soon as you use multiline log messages or use a semantically secure mode (more on that later).

The better fix would be to write the number of bytes for each log message before the message, read that and decrypt only that many bytes. This also probably easier to implement with file streams.

You currently use Cipher.getInstance("AES"); which is a non-fully qualified version of Cipher.getInstance("AES/ECB/PKCS5Padding");. ECB mode is not semantically secure. It simply encrypts each block (16 bytes) with AES and the key. So blocks that are the same will be the same in ciphertext. This is particularly bad, because some log messages start the same and an attacker might be able to distinguish them. This is also the reason why the decryption of the whole file worked despite being encrypted in chunks. You should use CBC mode with a random IV.

Here is some sample code for proper use of AES in CBC mode with a random IV using streams:

private static SecretKey key = generateAESkey();
private static String cipherString = "AES/CBC/PKCS5Padding";

public static void main(String[] args) throws Exception {
    ByteArrayOutputStream log = new ByteArrayOutputStream();
    appendToLog("Test1", log);
    appendToLog("Test2 is longer", log);
    appendToLog("Test3 is multiple of block size!", log);
    appendToLog("Test4 is shorter.", log);

    byte[] encLog = log.toByteArray();

    List<String> logs = decryptLog(new ByteArrayInputStream(encLog));

    for(String logLine : logs) {
        System.out.println(logLine);
    }
}

private static SecretKey generateAESkey() {
    try {
        return KeyGenerator.getInstance("AES").generateKey();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

private static byte[] generateIV() {
    SecureRandom random = new SecureRandom();
    byte[] iv = new byte[16];
    random.nextBytes(iv);
    return iv;
}

public static void appendToLog(String s, OutputStream os) throws Exception {
    Cipher cipher = Cipher.getInstance(cipherString);
    byte[] iv = generateIV();
    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
    byte[] data = cipher.doFinal(s.getBytes("UTF-8"));
    os.write(data.length);
    os.write(iv);
    os.write(data);
}

public static List<String> decryptLog(InputStream is) throws Exception{
    ArrayList<String> logs = new ArrayList<String>();
    while(is.available() > 0) {
        int len = is.read();
        byte[] encLogLine = new byte[len];
        byte[] iv = new byte[16];
        is.read(iv);
        is.read(encLogLine);

        Cipher cipher = Cipher.getInstance(cipherString);
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
        byte[] data = cipher.doFinal(encLogLine);
        logs.add(new String(data, "UTF-8"));
    }
    return logs;
}

这篇关于AES加密,在解密文件中获得了额外的垃圾字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆