Java AES解密时未正确填充给定的最终块

Java AES解密时未正确填充给定的最终块,java,encryption,cryptography,aes,cryptojs,Java,Encryption,Cryptography,Aes,Cryptojs,首先,我会告诉你我的主要目标是什么。我将使用AES加密客户端中的一些内容,然后使用RSA公钥加密重要的AES规范,并将AES加密的数据和RSA加密的AES规范发送到服务器。所以在服务器上,我将使用RSA私钥解密AES密钥规范,然后使用这些AES规范解密AES加密的数据。我已经通过测试加密和解密成功地使RSA部分工作。我必须先让这个AES艺术发挥作用,然后才能在这里实现RSa 对于客户端,我使用crypto js <script src="http://crypto-js.googlecod

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

对于客户端,我使用crypto js

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/pbkdf2.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
<script type="text/javascript" src="jquery-1.7.1.js"></script>
<script type="text/javascript">

    $("#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) {

            }
        });
    });

</script>
您可以看到,在Javascript端,它是CryptoJS.pad.Pkcs7,在服务器端,它是AES/CBC/PKCS5Padding,我对此进行了一些搜索,发现两者都是相同的。我既不能将其更改为CryptoJS.pad.Pkcs5,也不能将其更改为AES/CBC/PKCS7Padding,这两个库都没有定义


我也有以下想法。在javascript中,我使用随机salt和pass生成128位的密钥。使用相同的salt和pass,我通过定义适当的迭代计数和密钥大小在Java中生成相同的密钥。为什么我必须通过再次生成相同的密钥来延长Java中的进程?我可以简单地将密钥(encrypted.key)、encrytedData(encrypted.toString())和Iv(encrypted.Iv)发送到服务器并立即解密数据,而无需再次生成密钥。我说的对吗。。?我也尝试过这个,我得到了“无效AES密钥长度异常”。为了维护安全性,我将使用RSA公钥在客户端加密密钥和Iv。使用非对称实现对称的一个原因是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)
和“rsacyncrypt(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);

因为您想要使用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)
和“rsacyncrypt(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/客户端代码


$(“#提交”)。单击(函数(){
var key=CryptoJS.lib.WordArray.random(16);
var iv=CryptoJS.lib.WordArray.random(16);
var message=“用户”;
var encrypted=CryptoJS.AES.encrypt(消息,密钥,{iv:iv});
//如果希望在客户端进行解密,请使用注释代码
/*var decrypted=CryptoJS.AES.decrypt(加密,密钥,{iv:iv});
函数hex2a(hexx){
var hex=hexx.toString();//强制转换
var-str='';
对于(变量i=0;i
服务器/Java代码

import java.io.IOException;
导入java.io.UnsupportedEncodingException;
导入java.security.invalidalgorithParameterException;
导入java.security.InvalidKeyException;
导入java.security.Key;
导入java.security.NoSuchAlgorithmException;
导入java.security.SecureRandom;
导入javax.crypto.BadPaddingException;
导入javax.crypto.Cipher;
导入javax.crypto.IllegalBlockSizeException;
导入javax.crypto.KeyGenerator;
导入javax.crypto.NoSuchPaddingException;
导入javax.crypto.spec.IvParameterSpec;
导入javax.crypto.spec.SecretKeySpec;
导入javax.servlet.ServletException;
导入javax.servlet.annotation.WebServlet;
导入javax.servlet.http.HttpServlet;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletResponse;
//您必须下载并将其添加到您的项目中,其他是Java内置库
//请勿将sun.misc用于Base64函数
导入org.apache.commons.codec.binary.Base64;
公共类基本加密{
//我已经在servlet的post方法中完成了
受保护的void doPost(HttpServletRequest请求、HttpServletResponse响应)引发ServletException、IOException{
字符串encryptedData=request.getParameter(“cipherData”);
字符串数据[]=encryptedData.spl
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;
}
}