问题描述
首先,我会告诉我的主要目标是什么。我将使用AES加密客户端中的某些内容,然后使用RSA公钥加密重要的AES规范,并将AES加密数据和RSA加密AES规范发送到服务器。所以在服务器上,我将使用RSA私钥解密AES密钥规范,然后使用这些AES规范,我将解密AES加密数据。我通过测试加密和解密成功地使RSA部分工作。在实现RSa之前,我要使这个AES艺术工作。
First, I'll tell what is my primary goal. I'm going to use AES to encrypt some content in the client side, then use RSA public key to encrypt the important AES specs and sending both AES encrypted data and RSA encrypted AES specs to server. So at server, I'll decrypt the AES key specs using RSA private key, then using those AES specs, I'll decrypt the AES encrypted data. I've successfully made the RSA part working by test encrypting and decrypting. I've to made this AES art working before implementing RSa in this.
对于客户端,我使用的是crypto-js
For client side, I'm using 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>
这是我在服务器端使用的代码
And this is the code I'm using in server side
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和密码进行了测试。它收到了相同的数据。但我在行
First I've to make sure that the server is receiving the same data which I'm sending. So I tested it through sysout for encrypted,salt,iv and password. It's received the same data. But I got the exception at line
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)
You can see that in Javascript side, it's CryptoJS.pad.Pkcs7 and at server side it's AES/CBC/PKCS5Padding , I've done some searches on that and found that both are identical. Neither I can change it to CryptoJS.pad.Pkcs5 nor AES/CBC/PKCS7Padding which is not defined for both Crypto-js libraries and Java inbuild libraries.
还有我我有以下想法。在javascript中,我使用随机盐并传递生成128位密钥。使用相同的salt和pass,我通过定义适当的迭代计数和密钥大小在Java中生成相同的密钥。为什么我必须通过再次生成相同的密钥来延长Java中的进程?我可以简单地将密钥(encrypted.key),encrytedData(encrypted.toString())和Iv(encrypted.iv)发送到服务器并立即解密数据,而无需再次生成密钥。我是对的吗?我也试过这个,我得到无效的AES密钥长度异常。为了保持安全性,我将使用RSA公钥对客户端的密钥和Iv进行加密。实现Syymmetric with Asymmetric的原因之一是由于RSA的加密数据量有限。但是如果我不能清除这个BadPaddingException,我就无法实现它。
And also I'm having the following thought. In javascript, I'm using random salt and pass to generate a 128bits key. And using the same salt and pass, I'm generating the same key in Java by defining appropriate Iteration count and key size. Why should I have to lengthen the process in Java by generating the same key again? I can simply send the key (encrypted.key), encrytedData(encrypted.toString()) and Iv (encrypted.iv) to server and decrypt the data instantly without have to go through the process of generating the key again. Am I right about this..? I've tried this too, where I'm getting "Invalid AES key length exception". For maintaining security, I'll encrypt the key and Iv in client side using RSA public key. One of the reasons for Implementing Symmetric with Asymmetric is due to RSA's limited size of data to be encrypted. But I cant Implement it if I can't clear this BadPaddingException.
推荐答案
因为你想使用RSA并且已经实现了它,不需要使用密码派生。创建随机密钥并随机iv:
Since you want to use RSA and did already implement it, there is no need to use a password derivation. Create a random key and random 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密钥,并将所有这些密钥组合起来解密密文。
Then you send iv.toString(Crypto.enc.Base64)
, encrypted.ciphertext.toString(Crypto.enc.Base64)
and "RSAencrypt(key)" to the server, decode the base64 encoded iv and ciphertext, decrypt the RSA ciphertext to get the AES key and combine all of them to decrypt the ciphertext.
您的原始问题可能在于您使用的尺寸。 CryptoJS具有内部表示,每个字包含4个字节。这就是你需要除以32以获得128位散列的原因:
Your original problem probably lies in the sizes that you use. CryptoJS has an internal representation which consists of 4 bytes per word. That is why you need to divide by 32 for example to get a 128-bit hash:
var key128Bits = CryptoJS.PBKDF2(pass, salt, { keySize: 128/32 });
另一方面,WordArray仅适用于字节,这就是除以8的原因:
The WordArray on the other hand works only on bytes which is why you divide by 8:
var key = CryptoJS.lib.WordArray.random(128/8);
这篇关于在AES解密时,给定Final Block未正确填充的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!