在AES解密时,给定Final Block未正确填充

首先,我会告诉我的主要目标是什么。 我将使用AES加密客户端中的某些内容,然后使用RSA公钥加密重要的AES规范,并将AES加密数据和RSA加密AES规范发送到服务器。 所以在服务器上,我将使用RSA私钥解密AES密钥规范,然后使用这些AES规范,我将解密AES加密数据。 我通过测试加密和解密成功地使RSA部分工作。 在实现RSa之前,我要使这个AES艺术工作。

对于客户端,我使用的是crypto-js

     $("#submit").click(function() { var salt = CryptoJS.lib.WordArray.random(16); var iv = CryptoJS.lib.WordArray.random(16); var pass = CryptoJS.lib.WordArray.random(16); var message = "Test Message for encryption"; var key128Bits = CryptoJS.PBKDF2(pass, salt, { keySize: 128 }); var key128Bits10Iterations = CryptoJS.PBKDF2(pass, salt, { keySize: 128, iterations: 10 }); var encrypted = CryptoJS.AES.encrypt(message, key128Bits10Iterations, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); var cipherData = encrypted.toString()+":"+salt.toString()+":"+iv.toString()+":"+pass.toString(); console.log(cipherData); $.ajax({ url: 'encryption', type: 'POST', data: { cipherData: cipherData }, success: function(data) { console.log(data); }, failure: function(data) { } }); });  

这是我在服务器端使用的代码

 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String encryptedData = request.getParameter("cipherData"); String data[] = encryptedData.split(":"); String encrypted = data[0]; String salt = data[1]; String iv = data[2]; String password = data[3]; byte[] saltBytes = hexStringToByteArray(salt); byte[] ivBytes = hexStringToByteArray(iv); IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes); SecretKeySpec sKey = null; try { sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes); } catch (GeneralSecurityException e) { e.printStackTrace(); } try { System.out.println( decrypt( encrypted , sKey ,ivParameterSpec)); } catch (Exception e) { e.printStackTrace(); } } public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException { KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 10, 128); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); SecretKey secretKey = keyFactory.generateSecret(keySpec); return new SecretKeySpec(secretKey.getEncoded(), "AES"); } public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } public static String decrypt(String encryptedData, SecretKeySpec sKey, IvParameterSpec ivParameterSpec) throws Exception { Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); c.init(Cipher.DECRYPT_MODE, sKey, ivParameterSpec); byte[] decordedValue = Base64.decodeBase64(encryptedData); byte[] decValue = c.doFinal(decordedValue); String decryptedValue = new String(decValue); return decryptedValue; } 

首先,我要确保服务器正在接收我正在发送的相同数据。 所以我通过sysout对加密,盐,iv和密码进行了测试。 它收到了相同的数据。 但我得到了例外

 byte[] decValue = c.doFinal(decordedValue); javax.crypto.BadPaddingException: Given final block not properly padded at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966) 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:2121) at com.Encryption.decrypt(Encryption.java:95) at com.Encryption.doPost(Encryption.java:60) at javax.servlet.http.HttpServlet.service(HttpServlet.java:644) at javax.servlet.http.HttpServlet.service(HttpServlet.java:725) 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:503) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:526) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:655) at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523) 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) 

你可以看到,在Javascript方面,它是CryptoJS.pad.Pkcs7,在服务器端它是AES / CBC / PKCS5Padding,我已经对它做了一些搜索,发现它们都是相同的。 我既不能将其更改为CryptoJS.pad.Pkcs5,也不能将AES / CBC / PKCS7Padding更改为Crypto-js库和Java inbuild库。

我也有以下想法。 在javascript中,我使用随机盐并传递生成128位密钥。 使用相同的salt和pass,我通过定义适当的迭代计数和密钥大小在Java中生成相同的密钥。 为什么我必须通过再次生成相同的密钥来延长Java中的进程? 我可以简单地将密钥(encrypted.key),encrytedData(encrypted.toString())和Iv(encrypted.iv)发送到服务器并立即解密数据,而无需再次生成密钥。 我是对的吗? 我也试过这个,我得到“无效的AES密钥长度exception”。 为了保持安全性,我将使用RSA公钥对客户端的密钥和Iv进行加密。 实现Syymmetric with Asymmetric的原因之一是由于RSA的加密数据量有限。 但是如果我不能清除这个BadPaddingException,我就无法实现它。

由于您希望使用RSA并且已经实现了它,因此无需使用密码派生。 创建随机密钥并随机iv:

 var key = CryptoJS.lib.WordArray.random(16); // 128bit var iv = CryptoJS.lib.WordArray.random(16); // 128bit var encrypted = CryptoJS.AES.encrypt(message, key, { iv: iv }); // CBC/PKCS#7 is default 

然后将iv.toString(Crypto.enc.Base64)encrypted.ciphertext.toString(Crypto.enc.Base64)和“RSAencrypt(key)”发送到服务器,解码base64编码的iv和密文,解密RSA密文获取AES密钥并将所有这些密钥组合在一起以解密密文。


您最初的问题可能在于您使用的尺寸。 CryptoJS具有内部表示,每个字包含4个字节。 这就是你需要除以32以获得128位散列的原因:

 var key128Bits = CryptoJS.PBKDF2(pass, salt, { keySize: 128/32 }); 

另一方面,WordArray仅适用于字节,这就是除以8的原因:

 var key = CryptoJS.lib.WordArray.random(128/8); 

感谢Artjom B.

解决方案:工作代码

Javascript /客户端代码

      

服务器/ Java代码

 import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; // You have to download and add it into your project, others are Java Inbuilt Libraries // Do not use sun.misc for Base64 functions import org.apache.commons.codec.binary.Base64; public class BasicDecryption { //I've done it in post method of servlet protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String encryptedData = request.getParameter("cipherData"); String data[] = encryptedData.split(":"); String iv = data[0]; byte[] encryptedByteData = hexStringToByteArray(data[1]); String keyString = data[2]; IvParameterSpec iv = new IvParameterSpec(Base64.decodeBase64(iv); Key k = new SecretKeySpec(Base64.decodeBase64(keyString),"AES"); try { System.out.println("Decrypted String:"+BasicDecryption.decrypt(Base64.encodeBase64String(encryptedByteData),k,iv)); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { e.printStackTrace(); } } public static final String decrypt(final String encrypted,final Key key, final IvParameterSpec iv) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, IOException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key,iv); byte[] raw = Base64.decodeBase64(encrypted); byte[] stringBytes = cipher.doFinal(raw); String clearText = new String(stringBytes, "UTF8"); return clearText; } public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } }