Open csandels opened 6 years ago
For anyone who might find this in a search wondering about the same sort of issues, though I haven't yet solved the issues above. Mainly, I don't know where the iv is or how it's derived in the BouncyCastle version, nor do I know how to use pkcs12 if in fact that is even the problem in #2 above.
However, I AM able to migrate the java data to a different encryption algorithm that can be decrypted with forge or crypto-js. This will solve my problem if nothing else, though with a bit more work.
There is a great example of using java and crypto-js together to share encrypted data at https://github.com/mpetersen/aes-example.
Following is roughly the same code, modified to use forge and a 256 bit key. Just change 256 to 1 for sha1 like the original crypto-js/java example. And to get java to do 256, just change SHA1 to SHA256 in the SecretKeyFactory.getInstance and pass 256 to PBEKeySpec instead of 128.
var AesUtil = function(keySize, iterationCount) {
this.keySize = keySize / 8;
this.iterationCount = iterationCount;
};
AesUtil.prototype.generateKey = function(salt, passPhrase) {
var key = forge.pkcs5.pbkdf2(passPhrase, forge.util.hexToBytes(salt), this.iterationCount, this.keySize, 'sha256');
return key;
}
AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) {
var key = this.generateKey(salt, passPhrase);
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: forge.util.createBuffer(forge.util.hexToBytes(iv))});
cipher.update(forge.util.createBuffer(plainText));
cipher.finish();
return forge.util.encode64(cipher.output.getBytes());
}
AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) {
var key = this.generateKey(salt, passPhrase);
var cipher = forge.cipher.createDecipher('AES-CBC', key);
cipher.start({iv: forge.util.createBuffer(forge.util.hexToBytes(iv))});
cipher.update(forge.util.createBuffer(forge.util.decode64(cipherText)));
cipher.finish();
return cipher.output.data;
}
@csandels - Regarding your first comment: PBEWITHSHA256AND128BITAES-CBC-BC follows PKCS#12. node-forge provides the function forge.pki.pbe.generatePkcs12Key()
that can be used to derive key and IV according to PKCS#12:
var forge = require('node-forge');
var pki = forge.pki;
const password = "Password";
const cryptSrc = "AabOla0M5kc1NglPKNKVVNb0XSKD6IsWa0n84gfO0AM=";
// separate salt/message
let fullBytes = forge.util.decode64(cryptSrc);
let salt = forge.util.createBuffer(fullBytes.substring(0, 16))
let msg = forge.util.createBuffer(fullBytes.substring(16));
// key/IV derivation
let sha256 = forge.md.sha256.create()
var key = pki.pbe.generatePkcs12Key(password, salt, 1, 1002, 16, sha256)
var iv = pki.pbe.generatePkcs12Key(password, salt, 2, 1002, 16, sha256)
// decryption
let decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(msg);
decipher.finish();
// output
let decDat = decipher.output.data;
console.log("Decrypted: " + decDat); // Decrypted: My Message
// key/IV -------------
console.log("Key: ", forge.util.bytesToHex(key.data)); // Key: 733fa98d304f7135647bee191a112cd4
console.log("IV: ", forge.util.bytesToHex(iv.data)); // IV: bb8ba3bff3a42c7e662fd13727aca024
Regarding the Java code: This is misleading, as it pretends to set an IV, which is not the case (the IV is derived like the key). A more transparent implementation in this respect is:
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
...
Security.addProvider(new BouncyCastleProvider());
String password = "Password";
String src = "AabOla0M5kc1NglPKNKVVNb0XSKD6IsWa0n84gfO0AM=";
byte[] srcBytes = Base64.decodeBase64(src);
byte[] saltBytes = new byte[16];
byte[] msgBytes = new byte[srcBytes.length - saltBytes.length];
System.arraycopy(srcBytes, 0, saltBytes, 0, 16);
System.arraycopy(srcBytes, saltBytes.length, msgBytes, 0, msgBytes.length);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 1002);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC");
SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(msgBytes);
String decryptedText = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Decrypted: " + decryptedText); // Decrypted: My Message
...
Key and IV can be determined as follows:
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
...
ParametersWithIV parametersWithIV = (ParametersWithIV)((BCPBEKey)secretKey).getParam();
KeyParameter keyParameter = (KeyParameter)parametersWithIV.getParameters();
System.out.println("Key: " + HexFormat.of().formatHex(keyParameter.getKey())); // Key: 733fa98d304f7135647bee191a112cd4
System.out.println("IV: " + HexFormat.of().formatHex(parametersWithIV.getIV())); // IV: bb8ba3bff3a42c7e662fd13727aca024
...
Note that what you call key
is just the password encoded according to PKCS#12, i.e. to which a NULL terminator has been appended and which has been encoded with UCS-2 BE (Unicode code points of the BMP encoded in Big Endian), s. RFC 7292, B.1 Password Formatting. This can be easily seen if key.getEncoded()
is not Base64 but hex encoded:
00500061007300730077006f007200640000
The actual key and IV are derived from this according to the key derivation function defined in PKCS#12.
i created my implementation of PBEWITHSHA256AND256BITAES-CBC-BC you can use jaspyt-js-pbe its open to use. i hope this helps you
Hello.
I'm trying to use forge to decrypt a message that was encrypted using the Java BouncyCastle PBEWITHSHA256AND128BITAES-CBC-BC algorithm.
The Java code looks like the following:
This prints out:
The javascript looks like the following:
Which print the following:
As we see, the javascript version has the same salt, message and password. I seem to have the following 2 problems:
I'm not sure what to use for an iv in the javascript side. The iv on the Java side appears to have no effect at all. No matter what I use for an iv on the Java side, I get the correct decrypted message. I'm thinking BouncyCastle is disregarding the iv for this algorithm. If that's so, how would I correclty use the AES-CBC cipher in forge when it requires an iv?
It appears the key is wrong. I'm assumed it should be 32 bytes since it's sha256. Iterations are the same, salt is the same. BUT... the BouncyCastle source for the PBEWithSHA256And128BitAES-CBC-BC SecretKeyFactory shows it uses SHA256 for the digest, a 128 bit key and iv size (implying I should use 16 bytes above) and a PKCS12 scheme. With some hints from the accepted SO answer at https://stackoverflow.com/questions/8674018/pbkdf2-with-bouncycastle-in-java/10569975, I figure BouncyCastle is using PKCS12 for iterations instead of PKCS5, which is used by forge. So, is there any way to use forge to generate the same key as Java has done in this case?