我正在尝试使用AES256加密/解密,使用Java进行加密,使用CryptoJS进行解密。 加密已经过测试在 正常工作,但JavaScript中的解密方法返回一个空字符串。请注意,为了测试JavaScript,我在tmp文件中打印出数据,IV和salt的值,然后在JS中进行硬编码。 (注意:文件格式为:data(byte [] base64),Iv(string base64)和salt(string base64))。
以下是java中的代码:
public byte[] encrypt(String plainText) throws Exception {
//get salt
salt = generateSalt();
byte[] saltBytes = salt.getBytes("UTF-8");
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return Base64.encode(encryptedTextBytes);
}
下面的JavaScript中的解密代码有什么问题?
// the password that user provides
var userPass = document.getElementById("password").value;
console.log("user pass : " + userPass);
// hash contains 5 bytes
var hashedPass = CryptoJS.SHA1(userPass);
console.log("hashed pass : " + hashedPass.toString(CryptoJS.enc.Base64) + " | array length " + hashedPass.words.length + " | " + typeof(hashedPass));
// use only 4 bytes (128 bits) from the hashed pass
// (same as used in java when encrypting)
/////////////////////////var hashed4bytes = CryptoJS.lib.WordArray.create(hashedPass.words.slice(0,4));
//console.log( "hashed4bytes encoded 64 = " + hashed4bytes.toString(CryptoJS.enc.Base64));
// get the encrypted msg
var encMsg64 = document.getElementById("themessage").innerHTML;
encMsg64 = encMsg64.toString( CryptoJS.enc.Base64);
//var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
var salt =CryptoJS.enc.Base64.parse("EAWnOgxUDuvhWqrSUsugq1umMpI=");
var iv =CryptoJS.enc.Base64.parse("xWpmXNbmbFjmWBUajuWYXQ==");
//var salt = "EAWnOgxUDuvhWqrSUsugq1umMpI=";
//var iv = "xWpmXNbmbFjmWBUajuWYXQ==";
console.log('salt '+ salt );
console.log('iv '+ iv );
var key = CryptoJS.PBKDF2(hashedPass, salt, { keySize: 256/32, iterations: 1000 });
console.log( 'key '+ key);
var decText = '';
var ok = true;
try {
debugger;
var decMsg = CryptoJS.AES.decrypt( encMsg64, key, {
iv:iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
} );
console.log( "decryptedData = " + decMsg );
// convert to UTF8 string
decText = decMsg.toString( CryptoJS.enc.Utf8 );
console.log( "decryptedText = " + decText );
if (decText == '') {
ok = false;
}
}
catch (e) {
//console.log("Error when decrypting: " + e.message)
ok = false;
}
mafe之后改变的问题仍然存在 更改后的完整代码
JAVA
public class AES256EncryptionServiceBean implements EncryptionService {
private static final Logger LOGGER = LoggerFactory
.getLogger(AES256EncryptionServiceBean.class);
private String salt = null; //get bytes out of UTF-8 for decryption
private static final int PSWDITERATIONS = 1000;//65536;
private static final int KEYSIZE = 256;
private static final String AES_ALGO = "AES";
private static final String SHA1_ALGO = "PBKDF2WithHmacSHA1";
private static final String AES_CBC_PKCS5_TRANSFORM = "AES/CBC/PKCS5Padding";
private byte[] Iv;
/**
* Encrypts the data with AES-256 algorithm Encrypted data will be encoded
* with base64 algorithm and the returned. Initial vector is being used
* during encryption along with CBC encryption mode.
*
* output format: [algo indicator(1char)][Initialization vector()][salt()][encoded data(variable size)]
*/
@Override
public byte[] encrypt(String password, byte[] data) throws PibException {
byte[] encodedData = null;
try {
byte[] encryptedData = encryptCBC256Bits(password, data);
encodedData = Base64.encodeBase64(encryptedData);
/*String finalStr=null;
String algo256 = "2";
String datastr = Base64.encodeBase64String(encryptedData);
String ivstr = new String(Iv);
finalStr = algo256 +ivstr+salt+datastr;
encodedData = finalStr.getBytes();
*/
} catch (Exception e) {
throw ExceptionFactory.createPibException(
MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
}
return encodedData;
}
/**
* Encrypts the input data with AES CBC transformation using 256 bits (32
* bytes) Key is generated based on the provided password and random salt.
* Salt is the extra bits added to the password to ensure every key is
* unique SHA1 hashing is also participate in key generation.
*
* @throws PibException
*
*/
private byte[] encryptCBC256Bits(String password, byte[] data)
throws PibException {
salt = generateSalt();
byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);
byte[] encryptedTextBytes = null;
// Derive the key
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(SHA1_ALGO);
// Password based key specification
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
PSWDITERATIONS, KEYSIZE);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(),
AES_ALGO);
// encrypt the data
Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_TRANSFORM);
// SecureRandom random = new SecureRandom();
// byte[] ivTemp = new byte[16];
// random.nextBytes(ivTemp);
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
Iv = params.getParameterSpec(IvParameterSpec.class).getIV();
encryptedTextBytes = cipher.doFinal(data);
} catch (NoSuchAlgorithmException | InvalidKeySpecException
| NoSuchPaddingException | InvalidKeyException
| InvalidParameterSpecException | IllegalBlockSizeException
| BadPaddingException e) {
throw ExceptionFactory.createPibException(
MessageCodes.PIB_ENCRYPTION_FAILED, e, LOGGER);
}
return encryptedTextBytes;
}
private String generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
}
public String getSalt() {
return salt;
}
public byte[] getIv() {
return Iv;
}
}
的Javascript
function decryptMsg256() {
// the password that user provides
var userPass = document.getElementById("password").value;
console.log("user pass : " + userPass);
// get the encrypted msg
var encMsg64 = document.getElementById("themessage").innerHTML;
var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
var salt =CryptoJS.enc.Utf8.parse("?E€O5?…°®I^y??O:n");
var iv =CryptoJS.enc.Utf8.parse("S;Ui?¨=ENzI—$");
console.log('salt '+ salt );
console.log('iv '+ iv );
var key = CryptoJS.PBKDF2("password", salt, { keySize: 256/32, iterations: 1000 });
console.log( 'key '+ key);
var decText = '';
var ok = true;
try {
debugger;
var decMsg = CryptoJS.AES.decrypt( encMsg, key, {
iv:iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
} );
console.log( "decryptedData = " + decMsg );
// convert to UTF8 string
decText = decMsg.toString( CryptoJS.enc.Utf8 );
console.log( "decryptedText = " + decText );
if (decText == '') {
ok = false;
}
}
catch (e) {
//console.log("Error when decrypting: " + e.message)
ok = false;
}
我无法理解有什么问题请帮忙 CipherText,Salt和Iv检索如下:
public void testEncryption_WriteToFile() throws Exception {
byte[] data = IOUtils.toByteArray(this.getClass().getClassLoader()
.getResourceAsStream(SOME_FILE_NAME));
byte[] encryptedData = this.encryptionService.encrypt(PASSWORD, data);
byte[] initial_vector = ((AES256EncryptionServiceBean) encryptionService)
.getIv();
String salt = ((AES256EncryptionServiceBean) encryptionService)
.getSalt();
IOUtils.write(encryptedData, new FileOutputStream(
"C:\\Temp\\data.encrypted"));
/*IOUtils.write(new String(encryptedData), new FileOutputStream(
"C:\\Temp\\data[byte32string].encrypted"));
*/
IOUtils.write(Base64.encodeBase64String(salt.getBytes(StandardCharsets.UTF_8)), new FileOutputStream(
"C:\\Temp\\salt.encrypted"));
/*IOUtils.write(salt.getBytes(StandardCharsets.UTF_8), new FileOutputStream(
"C:\\Temp\\salt.encrypted"));
*/
IOUtils.write(Base64.encodeBase64String(initial_vector), new FileOutputStream(
"C:\\Temp\\iv.encrypted"));
/*IOUtils.write(initial_vector, new FileOutputStream(
"C:\\Temp\\iv.encrypted"));*/
}
答案 0 :(得分:4)
CryptoJS.PBKDF2
默认使用SHA1。因此,只要提供相同的密码,salt,密钥大小和迭代计数,它就会生成相同的密钥。问题是在JavaScript中你还用SHA1散列密码。不要这样做,并将密码直接传递给PBKDF2,就像在Java中一样。
第二个问题是,在尝试解密时,密文应采用CryptoJS的本机格式。由于您从Java获得基本64位编码密文,因此必须对其进行解码。取消注释该行:
var encMsg = CryptoJS.enc.Base64.parse(encMsg64);
并且不执行encMsg64 = encMsg64.toString( CryptoJS.enc.Base64);
,因为这将再次编码已编码的密文。
对于更新后的代码,您无法仅将字符串和salt打印为字符串,并希望它可以在JavaScript中运行。那些是byte[]
是有原因的。它们包含不可打印的字符,当您尝试在JavaScript中解析它时,这些字符将丢失。您必须将要从Java传输到JavaScript的所有byte[]
值编码为Base64,然后在JavaScript中对其进行解码。