【发布时间】:2015-03-31 13:23:32
【问题描述】:
首先,我将说明我的主要目标是什么。我将使用 AES 加密客户端的一些内容,然后使用 RSA 公钥加密重要的 AES 规范并将 AES 加密数据和 RSA 加密 AES 规范发送到服务器。所以在服务器上,我将使用 RSA 私钥解密 AES 密钥规范,然后使用这些 AES 规范,我将解密 AES 加密数据。我已经通过测试加密和解密成功地使 RSA 部分工作。在实现 RSa 之前,我必须先让这个 AES 艺术工作。
对于客户端,我使用的是 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>
这是我在服务器端使用的代码
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 内置库定义。
我也有以下想法。在 javascript 中,我使用随机盐并通过生成 128 位密钥。并且使用相同的 salt 和 pass,我通过定义适当的迭代计数和密钥大小在 Java 中生成相同的密钥。为什么我必须通过再次生成相同的密钥来延长 Java 中的过程?我可以简单地将密钥 (encrypted.key)、encrytedData(encrypted.toString()) 和 Iv (encrypted.iv) 发送到服务器并立即解密数据,而无需再次执行生成密钥的过程。我对这个..是对的吗?我也试过这个,我得到“无效的 AES 密钥长度异常”。为了维护安全,我将在客户端使用 RSA 公钥加密密钥和 Iv。使用非对称实现对称的原因之一是由于 RSA 的待加密数据大小有限。但是如果我不能清除这个 BadPaddingException,我就不能实现它。
【问题讨论】:
-
你可以把这一切都留给 HTTPS+TLS 反正它或多或少做同样的事情。
-
是的,我知道,但这是我客户的需要。而且我还可以学习密码学,这是我第一次在密码学中编码。但我假设我选择了正确的加密方案。
标签: java encryption cryptography aes cryptojs