2017-03-13 95 views
0

我在使用加密的生產環境中遇到問題。生產環境中的加密異常

下面的代碼是問題的根源:

public static void standardExceptionHandling(Exception exc, Logger alog) { 
    StringWriter sw = new StringWriter(); 
    PrintWriter pw = new PrintWriter(sw); 
    exc.printStackTrace(pw); 
    alog.info(sw.toString()); /* Line 292 */ 
} 

/** 
* Method that takes a key/value set, converts it into a standard web parameter string 
* and then encrypts the string. 
* 
* @param values the key value set 
* @return the encrypted string 
* 
*/ 
public static String encrypt(Map<String, String> values) { 
    StringBuilder unencrypted = new StringBuilder(); 
    boolean first = true; 
    for (Map.Entry<String, String> value : values.entrySet()) { 
     if (first) { 
      first = false; 
     } else { 
      unencrypted.append("&"); 
     } 
     unencrypted.append(value.getKey()) 
       .append("=") 
       .append(value.getValue()); 
    } 

    try { 
     Cipher cipher = Cipher.getInstance("AES"); 
     Key aesKey = new SecretKeySpec(AES_KEY.getBytes(), "AES"); 
     cipher.init(Cipher.ENCRYPT_MODE, aesKey); 
     byte[] encrypted = cipher.doFinal(unencrypted.toString().getBytes("UTF8")); 

     String enc = new sun.misc.BASE64Encoder().encode(encrypted); 
     return enc; 
    } catch (Exception e) { 
     standardExceptionHandling(e, log); 
     return ""; 
    } 
} 

/** 
* Method that takes an encrypted string containing a standard web parameter string 
* and converts it to a key/value set 
* 
* @param encrypted the encrypted string 
* @return the key value set 
*/ 
public static Map<String, String> decrypt(String encrypted) { 
    String decrypted = ""; 
    try { 
     Cipher cipher = Cipher.getInstance("AES"); 
     Key aesKey = new SecretKeySpec(AES_KEY.getBytes(), "AES"); 
     cipher.init(Cipher.DECRYPT_MODE, aesKey); 
     byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(encrypted); 
     decrypted = new String(cipher.doFinal(dec), "UTF8"); 
    } catch (Exception e) { 
     standardExceptionHandling(e, log); 
    } 

    Map<String, String> values = new HashMap<String, String>(); 
    for (String pair : decrypted.split("&")) { 
     String[] split_pair = pair.split("="); 
     String key, value; 
     if (split_pair.length == 1) { 
      key = split_pair[0]; 
      value = ""; 
     } else if (split_pair.length == 2) { 
      key = split_pair[0]; 
      value = split_pair[1]; 
     } else if (split_pair.length > 2) { 
      log.debug("Error when decrypting string, parameter found with more than 2 parts (" + pair + ")"); 
      continue; 
     } else { 
      // We should never reach this, as it is impossible to split a string into a 0 length array. 
      log.debug("The impossible happened, we split a String into a 0 length array (" + pair + ")"); 
      continue; 
     } 
     // This is only reach when key and value have been initialised thank to the continue statements when we hit 
     // an error state. 
     values.put(key, value); 
    } 
    return values; 
} 

沒有例外加密文本時拋出。 加密後,它是通過一個URL通過servlet傳回

http://URL/servlet?hash=TVgYDScPqQ3eaJfEBmwuSCZUN0GCEshOBZ9H0YKH%2BS2b96BYdLRPBa6Dl8Z0mMmpPM1r2uxdv0sq%0A5BNbWTMcww%3D%3D

在我們的生產服務器,我們得到以下異常:

INFO 10/mar/2017 06:02:37 [http-nio-80-exec-104] (HelperMethods.java:292) - javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher 
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913) 
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824) 
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436) 
at javax.crypto.Cipher.doFinal(Cipher.java:2165) 
at com.gg.gomoenterprise.utils.HelperMethods.decrypt(HelperMethods.java:349) 
at com.gg.gomomessenger.servlets.EmailServlet.doPost(EmailServlet.java:60) 
at com.gg.gomomessenger.servlets.EmailServlet.doGet(EmailServlet.java:46) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
at java.lang.Thread.run(Thread.java:745) 
DEBUG 10/mar/2017 06:02:37 [http-nio-80-exec-104] (EmailServlet.java:88) - com.gg.gomomessenger.commons.exceptions.EmailSeverletException: <h1>We do not see your email address in this program. You may have already unsubscribed or be subscribed with a different email address. If you need assistance, email [email protected]</h1> 
at com.gg.gomomessenger.commons.exceptions.EmailSeverletException.dataMisingException(EmailSeverletException.java:39) 
at com.gg.gomomessenger.servlets.EmailOptInServlet.handleOpt(EmailOptInServlet.java:91) 
at com.gg.gomomessenger.servlets.EmailServlet.doPost(EmailServlet.java:82) 
at com.gg.gomomessenger.servlets.EmailServlet.doGet(EmailServlet.java:46) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
at java.lang.Thread.run(Thread.java:745) 

這僅發生在生產,而不是本地,dev或staging。 所有的服務器都運行Tomcat 8.0.26

本地JRE Java版本 「1.8.0_121」 的Java(TM)SE運行時環境(建1.8.0_121-B13) 的HotSpot的Java(TM)64位服務器VM(建立25.121-B13,混合模式)

分期JRE Java版本 「1.8.0_66」 的Java(TM)SE運行時環境(建立1.8.0_66-B17) 的HotSpot的Java(TM)64位服務器VM(版本25.66-b17,混合模式)

Stagin JRE Java版本 「1.8.0_66」 的Java(TM)SE運行時環境(建立1.8.0_66-B17) 爪哇熱點(TM)64位服務器VM(建立25.66-B17,混合模式)

生產JRE Java版本 「1.8.0_60」 的Java(TM)SE運行時環境(建立1.8.0_60-B27) 爪哇熱點(TM)64位服務器VM(建立25.60-B23,混合模式)

這可能是一個代碼問題?

+0

在調試什麼是數據長度(dec的長度)時,它必須是塊大小的倍數,AES的16字節?錯誤消息:「當使用填充密碼解密時,輸入長度必須是16的倍數」說明一切。 – zaph

+1

您在生產環境中使用該代碼?它使用ECB模式......你應該修復這個問題。 –

+0

@LukePark,不是很有幫助....你能告訴我什麼使用。而不是僅僅說「修復」 此外,這並不意味着是一個主要的加密算法,它只是簡單地隱藏用戶的4個參數。 如果我們沒有加密,我們會傳遞4個參數,他們都需要匹配(所有不同但鏈接)。如果他們不正確鏈接,頁面將不會加載! –

回答

2

我注意到例如:

TVgYDScPqQ3eaJfEBmwuSCZUN0GCEshOBZ9H0YKH%2BS2b96BYdLRPBa6Dl8Z0mMmpPM1r2uxdv0sq%0A5BNbWTMcww%3D%3D

是URL編碼,幷包含一個換行字符(%0A)。 Base64需要刪除URL編碼。

此外,Base64編碼不應該添加換行字符,應該有一個選項。無論如何,他們都需要在Base64解碼上剝離。

正確解碼它是十六進制:

4D58180D270FA90DDE6897C4066C2E48265437418212C84E059F47D18287F92D9BF7A05874B44F05AE8397C67498C9A93CCD6BDAEC5DBF4B2AE4135B59331CC3

+0

採取的措施...我錯過了URLDecode,即使加密做URLEncode,謝謝! 但是,您是否有任何解釋說明爲什麼它在其他環境中使用完全相同的設置。 –

+1

好運。根據加密的數據,Base64編碼中可能不會有「+」和「/」,並且根據長度,UIRL編碼沒有尾隨「=」字符。換行符0x0A通常由Base64解碼功能刪除。 – zaph

+0

雖然,我真的很喜歡你的答案,並且肯定會把它看作是一個問題....我真的很驚訝這只是運氣,它只發生在PROD中。 但是,在另一方面,我們沒有測試太多,....我們做了一個緊急修復程序,回去刪除加密。也許一些散列wouyd已經在生產中工作。 –

0

正如@zaph指出的那樣,有可能解析所述密文中的問題,這會導致不同長度的密文從的倍數塊大小(16 bytes)。在正確編碼和解碼密文後,您應該能夠驗證密文長度是n * 16字節。

此外,您獲得Cipher類的實例Cipher.getInstance("AES");的實例並不理想。默認情況下,Java中的AESAES/ECB/PKCS5Padding(但這也可能因供應商而異)。 ECB是一個不推薦/不推薦/破碎的block cipher mode。除了不知不覺地選擇了一種糟糕的操作模式之外,這裏的含糊不清是沒有必要的。評估可用模式並明確選擇合適的模式(可能的GCM或可能的CBCCTR與強大的HMAC功能一起使用)。

對於每個消息加密,您還應該使用唯一且不可預測的初始化向量(IV)。

+1

請顯示ECB模式在哪裏是不推薦/破碎的分組密碼模式。 – zaph

+0

@ zaph:歐洲央行不安全是常識:http://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption – Robert

+0

也許這些條款很強大,但取決於您的威脅模型,我站在他們旁邊。一個明顯的例子是[「企鵝形象」](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29),但有[許多](http://crypto.stackexchange.com/ a/20946/12569)[其他](http://crypto.stackexchange.com/a/968/12569)[示例](https://security.stackexchange.com/a/62084/16485)'ECB'模式對現代密碼分析不安全。 – Andy