我正在制作一个需要基于Java的AES加密和基于JavaScript的解密的应用程序。 我使用以下代码进行加密作为基本表单。
public class AESencrp {
private static final String ALGO = "AES";
private static final byte[] keyValue =
new byte[] { 'A', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k','l', 'm', 'n', 'o', 'p'};
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGO);
return key;
}
}
我尝试用来解密的JavaScript是
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"> </script>
var decrypted = CryptoJS.AES.decrypt(encrypted,"Abcdefghijklmnop").toString(CryptoJS.enc.Utf8);
但是JavaScript解密不起作用。我是新手,有人可以告诉我一种解决方法,而无需更改Java代码块吗?
我尝试使用Base-64解码我的文字:
var words = CryptoJS.enc.Base64.parse(encrKey);
var base64 = CryptoJS.enc.Base64.stringify(words);
var decrypted = CryptoJS.AES.decrypt(base64, "Abcdefghijklmnop");
alert("dec :" +decrypted);
但仍然没有好处。
我尝试了下面建议的解决方案来解决可能的填充问题,但它没有给出任何解决方案。
var key = CryptoJS.enc.Base64.parse("QWJjZGVmZ2hpamtsbW5vcA==");
var decrypt = CryptoJS.AES.decrypt( encrKey, key, { mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7 } );
alert("dec :" +decrypt);
答案 0 :(得分:11)
当您的JavaScript代码使用256位AES密钥时,您的Java代码使用128位AES密钥。
您的Java代码使用&#34; Abcdefghijklmnop&#34; .getBytes()作为实际键值,而您的JavaScript代码使用&#34; Abcdefghijklmnop&#34;作为从中派生实际密钥的密码。
Java AES的默认转换是AES / ECB / PKCS5Padding,而CryptoJS的默认转换是AES / CBC / PKCS7Padding。
修复示例的一种方法是修复JavaScript方面:
// this is Base64 representation of the Java counterpart
// byte[] keyValue = new byte[] { 'A', 'b', 'c', 'd', 'e', 'f', 'g',
// 'h', 'i', 'j', 'k','l', 'm', 'n', 'o', 'p'};
// String keyForJS = new BASE64Encoder().encode(keyValue);
var base64Key = "QWJjZGVmZ2hpamtsbW5vcA==";
console.log( "base64Key = " + base64Key );
// this is the actual key as a sequence of bytes
var key = CryptoJS.enc.Base64.parse(base64Key);
console.log( "key = " + key );
// this is the plain text
var plaintText = "Hello, World!";
console.log( "plaintText = " + plaintText );
// this is Base64-encoded encrypted data
var encryptedData = CryptoJS.AES.encrypt(plaintText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
console.log( "encryptedData = " + encryptedData );
// this is the decrypted data as a sequence of bytes
var decryptedData = CryptoJS.AES.decrypt( encryptedData, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
} );
console.log( "decryptedData = " + decryptedData );
// this is the decrypted data as a string
var decryptedText = decryptedData.toString( CryptoJS.enc.Utf8 );
console.log( "decryptedText = " + decryptedText );
答案 1 :(得分:3)
为了使Java和JavaScript能够互操作,在创建Key或Cipher时不必使用默认值。迭代计数,密钥长度,填充,盐和IV应该都是相同的。
参考:https://github.com/mpetersen/aes-example
以下示例代码:
在Java中加密字符串:
String keyValue = "Abcdefghijklmnop";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(keyValue.toCharArray(), hex("dc0da04af8fee58593442bf834b30739"),
1000, 128);
Key key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Cipher c = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(hex("dc0da04af8fee58593442bf834b30739")));
byte[] encVal = c.doFinal("The Quick Brown Fox Jumped over the moon".getBytes());
String base64EncodedEncryptedData = new String(Base64.encodeBase64(encVal));
System.out.println(base64EncodedEncryptedData);
}
在JavaScript中解密相同的字符串:
var iterationCount = 1000;
var keySize = 128;
var encryptionKey ="Abcdefghijklmnop";
var dataToDecrypt = "2DZqzpXzmCsKj4lfQY4d/exg9GAyyj0hVK97kPw5ZxMFs3jQiEQ6LLvUsBLdkA80" //The base64 encoded string output from Java;
var iv = "dc0da04af8fee58593442bf834b30739"
var salt = "dc0da04af8fee58593442bf834b30739"
var aesUtil = new AesUtil(keySize, iterationCount);
var plaintext = aesUtil.decrypt(salt, iv, encryptionKey, dataToDecrypt);
console.log(plaintext);
**//AESUtil - Utility class for CryptoJS**
var AesUtil = function(keySize, iterationCount) {
this.keySize = keySize / 32;
this.iterationCount = iterationCount;
};
AesUtil.prototype.generateKey = function(salt, passPhrase) {
var key = CryptoJS.PBKDF2(passPhrase, CryptoJS.enc.Hex.parse(salt),
{ keySize: this.keySize, iterations: this.iterationCount });
return key;
}
AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) {
var key = this.generateKey(salt, passPhrase);
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(cipherText)
});
var decrypted = CryptoJS.AES.decrypt(cipherParams,key,
{ iv: CryptoJS.enc.Hex.parse(iv) });
return decrypted.toString(CryptoJS.enc.Utf8);
}
}
答案 2 :(得分:0)
在使用 Java 10 分钟和使用 JS 10 小时后,我找到了适用于 AES/CBC/PKCS5Padding 的解决方案
Java/Kotlin Cypher 端:
companion object {
private const val ENCRYPTION_ALGORITHM = "AES"
private const val BLOCK_OPERATION_MODE = "CBC"
private const val PADDING_TYPE = "PKCS5Padding"
private const val ENCRYPTION_MODE =
"$ENCRYPTION_ALGORITHM/$BLOCK_OPERATION_MODE/$PADDING_TYPE"
private const val SPLITTER = "\\."
private const val AES_IV_SIZE = 16
private const val DEFAULT_SALT_SIZE = 32
private const val DEFAULT_ITERATIONS = 128
private const val DEFAULT_AES_KEY_SIZE = 128
private const val INDEX_SALT = 0
private const val INDEX_IV = 1
private const val INDEX_ENCRYPTED_DATA = 2
}
private fun encrypt(password: String, data: String): String {
val random = SecureRandom()
val iv = ByteArray(AES_IV_SIZE)
random.nextBytes(iv)
val salt = ByteArray(DEFAULT_SALT_SIZE)
random.nextBytes(salt)
val key = pbkdf2(password, salt)
val cipher = Cipher.getInstance(ENCRYPTION_MODE)
cipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(key, ENCRYPTION_ALGORITHM),
IvParameterSpec(iv)
)
val encrypted = cipher.doFinal(data.toByteArray(StandardCharsets.UTF_8))
return toBase64(salt) + "." + toBase64(iv) + "." + toBase64(encrypted)
}
/**
* Generates PBKDF2 hash for the configured password using the provided salt.
* If you notice very slow performance this can be related to Android Studio Instant Run
*
* @param salt The salt to use.
* @return The password hash as byte array
*/
private fun pbkdf2(password: String, salt: ByteArray): ByteArray {
val keySpec: KeySpec =
PBEKeySpec(password.toCharArray(), salt, DEFAULT_ITERATIONS, DEFAULT_AES_KEY_SIZE)
val keyFactory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val secretKey: SecretKey = keyFactory.generateSecret(keySpec)
return secretKey.encoded
}
private fun toBase64(data: ByteArray): String {
// use NO_WRAP because https://code.google.com/p/android/issues/detail?id=159799
return Base64.encodeToString(data, Base64.NO_WRAP)
}
JS 加密方面:
function decrypt(password, encrypted) {
const digest = encrypted.split('.');
const salt = CryptoJS.enc.Base64.parse(digest[0])
const iv = CryptoJS.enc.Base64.parse(digest[1])
const encryptedData = CryptoJS.enc.Base64.parse(digest[2])
const key = CryptoJS.PBKDF2(password, salt, {
iterations: 128,
keySize: 128/32,
})
const decrypted = CryptoJS.AES.decrypt(
{
ciphertext: encryptedData
},
key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
).toString(CryptoJS.enc.Utf8);
return JSON.parse(decrypted);
}