問題描述
我正在 Android 應(yīng)用程序中制作調(diào)試登錄功能.我有一個簡單的類,它使用 128 位 AES 加密記錄到 .txt 文件.
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.
記錄完成后,我用一個簡單的 JAVA 程序解密記錄的文件.
問題是當(dāng)我解密加密日志時我得到了一些奇怪的內(nèi)容,我也得到了加密的內(nèi)容,但是有一些額外的字符,見下文.
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 應(yīng)用日志部分:
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 + "
";
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();
}
}
}
這是解密JAVA程序:
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);
}
}
}
輸出解密后的日志(用notepad++打開):
T這是有效內(nèi)容,但您也可以看到額外的 thrash 字符.如果我使用默認的 windows 文本編輯器打開,我也會得到 thrash 字符,但不同.
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.
這是我第一次嘗試使用 encrypt -decrypt,我做錯了什么?有什么想法嗎?
This is my first try with encrypt -decrypt, what m i doing wrong? Any ideas?
推薦答案
AES 是一種僅適用于塊的分組密碼.您要加密的明文可以是任意長度,因此密碼必須始終填充明文以將其填充到塊大小的倍數(shù)(或者當(dāng)它已經(jīng)是塊大小的倍數(shù)時添加一個完整的塊).在此 PKCS#5/PKCS#7 填充中,每個填充字節(jié)表示填充字節(jié)數(shù).
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.
簡單的解決方法是在解密期間迭代 outputBytes
并刪除始終在下一行的那些填充字節(jié).一旦您使用多行日志消息或使用語義安全模式(稍后會詳細介紹),這將中斷.
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).
更好的解決方法是在消息之前寫入每個日志消息的字節(jié)數(shù),讀取它并僅解密那么多字節(jié).這也可能更容易通過文件流實現(xiàn).
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.
您當(dāng)前使用 Cipher.getInstance("AES");
這是 Cipher.getInstance("AES/ECB/PKCS5Padding"); 的非完全限定版本;
.ECB 模式在語義上并不安全.它只是用 AES 和密鑰加密每個塊(16 個字節(jié)).因此,相同的塊在密文中將是相同的.這尤其糟糕,因為一些日志消息以相同的開頭,攻擊者可能能夠區(qū)分它們.這也是為什么整個文件的解密工作盡管被分塊加密的原因.您應(yīng)該使用隨機 IV 的 CBC 模式.
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.
以下是一些示例代碼,用于在 CBC 模式下正確使用 AES,并使用流進行隨機 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;
}
這篇關(guān)于AES 加密,解密文件中有多余的垃圾字符的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!