2015-03-13 22 views
0

在Android應用程序中製作調試登錄函數。 我有一個簡單的類,它是使用128位AES加密記錄到.txt文件。AES加密,在解密的文件中獲得額外的垃圾字符

完成日誌記錄後,我用簡單的JAVA程序解密記錄的文件。

問題是,當我解密加密的日誌我得到了它一些奇怪的內容,我也得到了加密的內容,但也有一些額外的字符,請參閱下文。

Android應用日誌部分:

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

} 

} 

這是解密器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); 
    } 

} 

} 

輸出解密日誌(與記事本中打開++):

enter image description here

T 這裏是有效的內容,但你也可以看到額外的thrash字符。如果我用默認的Windows文本編輯器打開,我也有毆打字符,但不同的。

這是我第一次嘗試加密-decrypt,我做錯了什麼? 任何想法?

回答

3

AES是一種只能在塊上工作的塊密碼。要加密明文可以是任意長度的,所以密碼必須始終墊明文填滿它的塊大小的倍數(或添加一個完整的塊時,它已經是塊大小的倍數)。在這個PKCS#5/PKCS#7填充中,每個填充字節表示填充字節的數量。

的簡單的解決方法是在解密期間遍歷outputBytes和除去它們總是從下一行的那些填充字節。只要您使用多行日誌消息或使用語義安全模式(稍後會詳細介紹),這會中斷。

更好的解決方法是在消息之前寫入每條日誌消息的字節數,讀取並解密僅許多字節。這也可能更容易實現與文件流。

您目前正在使用Cipher.getInstance("AES");這是一個非完全限定版本的Cipher.getInstance("AES/ECB/PKCS5Padding");。 ECB模式在語義上不安全。它只是用AES和密鑰加密每個塊(16個字節)。所以相同的塊在密文中是相同的。這特別糟糕,因爲某些日誌消息的啓動相同,攻擊者可能會區分它們。這也是整個文件解密儘管以塊加密的原因。您應該隨機使用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 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; 
} 
+0

對我來說,我必須指定使用「RSA/ECB/PKCS1Padding」正確的填充,而不是默認的「RSA」字符串,問題就解決了。沒有更多的額外字符 – 2017-01-21 07:31:55

3

您已經使用不同的加密上下文對每條日誌消息進行了加密。當您在密碼對象上調用doFinal方法時,明文將被填充爲16的倍數。有效地,您的日誌文件是許多小型加密消息的序列。但是,在解密時,您忽略了這些消息邊界,並將該文件視爲單個加密消息。結果是填充字符未被正確地去除。你所看到的'垃圾'字符可能是這些填充字節。您需要重新設計日誌文件格式,以保留消息邊界,以便解密器可以發現它們或完全消除它們。

另外,不要在Java加密中使用默認值:它們不可移植。例如,Cipher.getInstance()採用的形式爲alg/mode/padding。總是指定全部三個。我注意到你也使用默認的無參數方法。總是指定一個字符集,幾乎總是「UTF8」是最好的選擇。