I have the following Decryption code from a C# App written by a colleague:
private static string UrlEncryptionKey = "x0iiR!RG#753!"; // not real values here
private static byte[] salt = new byte[] { 0x41, 0x71, 0x61, 0x6e, 0x21, 0x4d, 0x65, 0x64, 0x76, 0x64, 0x63, 0x62, 0x72 }
public static string Decrypt(String cipherText)
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(UrlEncryptionKey, salt);
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
Piecing together the equivalent cryptography, I currently have the Java equivalent as:
public static String Decrypt(String cipherText) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(UrlEncryptionKey.toCharArray(), salt, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKey secretKeyAES = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeyAES, ivSpec);
byte[] decoded = Base64.getDecoder().decode(cipherText.getBytes("UTF-8"));
byte[] original = cipher.doFinal(decoded);
String originalString = new String(original, "UTF-8");
return originalString;
}
But it's throwing an exception:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 48 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:93)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.implInit(Cipher.java:809)
at javax.crypto.Cipher.chooseProvider(Cipher.java:867)
at javax.crypto.Cipher.init(Cipher.java:1399)
at javax.crypto.Cipher.init(Cipher.java:1330)
at scratch.AESUtil.Decrypt(AESUtil.java:55)
at scratch.AESUtil.main(AESUtil.java:93)
I don't understand why the key length is invalid
Related
I am trying to implement encryption in Flutter, I'm using java as a backend and dart for the mobile application. I have taken code from this Encryption in Java and Decryption in Flutter (AES-256)
But it only supplies decryption in Flutter, and I want to implement encryption in Flutter so the Java code can decrypt it.
Can you help me by providing the code for encryption in Flutter?
This is the java code for encryption and decryption.
public class EncryptionService {
public String encrypt(String item) throws Exception {
byte[] ivBytes;
String password = "Hello";
/* you can give whatever you want for password. This is for testing purpose */
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
byte[] saltBytes = bytes;
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 65556, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
System.out.println("saltBytes : " + saltBytes);
// encrypting the word
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
System.out.println("ivBytes : " + ivBytes);
byte[] encryptedTextBytes = cipher.doFinal(item.getBytes("UTF-8"));
// prepend salt and vi
byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
return new Base64().encodeToString(buffer);
}
public String decrypt(String encryptedText) throws Exception {
String password = "Hello";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// strip off the salt and iv
ByteBuffer buffer = ByteBuffer.wrap(new Base64().decode(encryptedText));
byte[] saltBytes = new byte[20];
buffer.get(saltBytes, 0, saltBytes.length);
byte[] ivBytes1 = new byte[cipher.getBlockSize()];
buffer.get(ivBytes1, 0, ivBytes1.length);
byte[] encryptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes1.length];
buffer.get(encryptedTextBytes);
// Deriving the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, 65556, 256);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes1));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
}
Dart implementation for decryption is something like this
class EncryptionHelper {
static String decrypt(
String ciphertext,
) {
Uint8List ciphertextlist = base64.decode(ciphertext);
var salt = ciphertextlist.sublist(0, 20);
var iv = ciphertextlist.sublist(20, 20 + 16);
var encrypted = ciphertextlist.sublist(20 + 16);
Uint8List key = generateKey("Hello", salt);
CBCBlockCipher cipher = new CBCBlockCipher(new AESFastEngine());
ParametersWithIV<KeyParameter> params =
new ParametersWithIV<KeyParameter>(new KeyParameter(key), iv);
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>
paddingParams =
new PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(
params, null);
PaddedBlockCipherImpl paddingCipher =
new PaddedBlockCipherImpl(new PKCS7Padding(), cipher);
paddingCipher.init(false, paddingParams);
var val = paddingCipher.process(encrypted);
return new String.fromCharCodes(val);
}
static Uint8List generateKey(String passphrase, Uint8List salt) {
Uint8List passphraseInt8List = Uint8List.fromList(passphrase.codeUnits);
KeyDerivator derivator =
PBKDF2KeyDerivator(HMac(SHA1Digest(), 64)); // 64 byte block size
Pbkdf2Parameters params =
Pbkdf2Parameters(salt, 65556, 32); // 32 byte key size
derivator.init(params);
return derivator.process(passphraseInt8List);
}
}
Why don't you use pointcastle ?
it is a dart porting of Bouncy castle and, as I can read in their page, there is yet the implementation of the AES algorithm for decrypt/encrpyt.
Moreover use :
https://pub.dev/packages/encrypt
that is a convenient wrapper on top of pointcastle.
I have found the code of AES encryption and decryption on git hub as bellow:
public static String encrypt(String plainText, String key) throws Exception {
byte[] clean = plainText.getBytes();
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return new String(Base64.getEncoder().encode(encryptedIVAndText));
}
// decrypt method
public static String decrypt(String encrypted, String key) throws Exception {
int ivSize = 16;
int keySize = 16;
byte[] encryptedIvTextBytes = Base64.getDecoder().decode(encrypted);
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
return new String(decrypted);
}
I've found the information that java determine aes algorithm 128 or 256 by key size, so I have modify the code above by changing fixed value of "keySize" to key.length() as bellow:
public static String encryptAes(String plainText, String key){
byte[] clean = plainText.getBytes();
String str = null;
int keyLen = key.length();
try{
// Generating IV.
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[keyLen];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Encrypt.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
// Combine IV and encrypted part.
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
str = new String(Base64.getEncoder().encode(encryptedIVAndText));
}
catch (Exception e) {
// TODO: handle exception
System.out.println("Error");
}
return str;
}
public static String decryptAes(String encrypted, String key){
int ivSize = 16;
int keySize = key.length();
String str = null;
try{
byte[] encryptedIvTextBytes = Base64.getDecoder().decode(encrypted);
// Extract IV.
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Extract encrypted part.
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
// Hash key.
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
// Decrypt.
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
str = new String(decrypted);
}
catch (Exception e) {
// TODO: handle exception
System.out.println("Error");
}
return str;
}
And here is Main with and input
public static void main (String[] args){
String txtInput = "11700000108";
String key256 = "SxKFF2Kunz7tjyUZ4sb7TzzZBR83e8FL"; //length of key will be fixed as 32
String cipher256 = encryptAes(txtInput, key256);
System.out.println("CipherAes256: " + cipher256);
String key128 = "RfZVnF76FshjN46e"; //length of key will be fixed as 16
String cipher128 = encryptAes(txtInput, key128);
System.out.println("CipherAes128: " + cipher128);
}
But the result's length of cipher256 and cipher128 are same (44 characters in this example) I'm not sure if I need to make any further changes.
Please give me advice, Thanks.
I have encrypted the string in php. Decrypted successfully from php and node.js. In addition, it must be decrypted by java.
Help me an example of decrypting from java?
PHP Encrypt code
/* encrypt */
$encryption_method = 'aes-256-cbc';
$secretHash = "d95acd54c6a821ff32c52825b931c194";
$iv_size = openssl_cipher_iv_length($encryption_method);
$iv = openssl_random_pseudo_bytes($iv_size);
//encrypt
$encryptedMessage = openssl_encrypt($new_token, $encryption_method, $secretHash, 0, $iv);
//Concatenate iv with data
$ciphertext = bin2hex($iv).$encryptedMessage;
/* decrypt the cipher */
$iv_size = openssl_cipher_iv_length($encryptionMethod);
$iv = hex2bin(substr($encryptedMessageWithIv, 0, $iv_size * 2));
$decryptedMessage = openssl_decrypt(substr($encryptedMessageWithIv, $iv_size * 2), $encryptionMethod, $secretHash, 0, $iv);
Below is the encryption and decryption process for a string using AES algorithm.
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
In case init vector is not known, try using below code segment.
public byte[] decrypt(String encryptedString) throws DataLengthException, InvalidCipherTextException {
byte[] input = encryptedString.getBytes("UTF-8");
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(new AESEngine());
SecureRandom random = new SecureRandom();;
KeyParameter key = new KeyParameter("$secretHash".getBytes());// your key string
BlockCipherPadding blockCipherPadding = new PKCS7Padding();;
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(cbcBlockCipher, blockCipherPadding);
int blockSize = cbcBlockCipher.getBlockSize(); // Make sure this block size is same as that used while encrypting the string.
int inputOffset = 0;
int inputLength = input.length;
int outputOffset = 0;
byte[] initializationVector = new byte[blockSize];
System.arraycopy(input, 0, initializationVector, 0, blockSize);
inputOffset += blockSize;
inputLength -= blockSize;
pbbc.init(encrypt, new ParametersWithIV(key, initializationVector));
byte[] output = new byte[pbbc.getOutputSize(inputLength) + outputOffset];
int outputLength = outputOffset + pbbc.processBytes(input, inputOffset, inputLength, output, outputOffset);
outputLength += pbbc.doFinal(output, outputLength);
return Arrays.copyOf(output, outputLength);
}
Just in case it helps someone in the future: encryption with AES/CBC/PKCS5PADDING along with the generation of a dynamic IV that is appended to the final ciphertext in Java can be done through the following code:
Encryption (JAVA)
public String encryptPlainText(String plainText) {
String cipherText = "";
try {
String keyString = "examplesecretkeyexamplesecretkey";
//Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] v = new byte[16];
new SecureRandom().nextBytes(v);
IvParameterSpec iv = new IvParameterSpec(v);
cipher.init(Cipher.ENCRYPT_MODE, keyspec, iv);
byte[] cipherTextByteArray = cipher.doFinal(plainText.getBytes());
//appending iv to ciphertext without any additional libraries to handle the concatenation of the two byte arrays
byte[] ivWithCipherTextByteArray = new byte[v.length + cipherTextByteArray.length];
System.arraycopy(v, 0, ivWithCipherTextByteArray, 0, v.length);
System.arraycopy(cipherTextByteArray, 0, ivWithCipherTextByteArray, v.length, cipherTextByteArray.length);
cipherText = new String(Base64.getEncoder().encode(ivWithCipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return cipherText;
}
Decryption of the cipherText obtained with the code above can be implemented in the following way:
Decryption (JAVA)
public static String decryptCipherText(String cipherText) {
String plainText="";
try {
String keyString = "examplesecretkeyexamplesecretkey";
Security.setProperty("crypto.policy", "unlimited");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec keyspec = new SecretKeySpec(keyString.getBytes(), "AES");
byte[] cipherTextByteArray = Base64.getDecoder().decode(cipherText);
//initialize the IvParameterSpec with the first 16 bytes of the cipherText
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(cipherTextByteArray, 0, 16));
//cipherText to decrypt is now the original one with the first 16 bytes removed (the IV used above)
cipherTextByteArray = Arrays.copyOfRange(cipherTextByteArray, 16, cipherTextByteArray.length);
cipher.init(Cipher.DECRYPT_MODE, keyspec, iv);
plainText = new String(cipher.doFinal(cipherTextByteArray));
} catch (Exception e) {
LOG.info("Exception", e);
}
return plainText;
}
Here is the full working example of something encoded in .net and decoded in Java and vice-versa
Private Function Decrypt(cipherText As String) As String
dim _encryptionkey as string = "kmjfds(#1231SDSA()#rt32geswfkjFJDSKFJDSFd"
Dim cipherBytes As Byte() = Convert.FromBase64String(cipherText)
Using encryptor As Aes = Aes.Create()
Dim pdb As New Rfc2898DeriveBytes(_EncryptionKey, New Byte() {&H49, &H76, &H61, &H6E, &H20, &H4D, &H65, &H64, &H76, &H65, &H64, &H65, _
&H76})
encryptor.Key = pdb.GetBytes(32)
encryptor.IV = pdb.GetBytes(16)
Using ms As New MemoryStream()
Using cs As New CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write)
cs.Write(cipherBytes, 0, cipherBytes.Length)
cs.Close()
End Using
cipherText = Encoding.Unicode.GetString(ms.ToArray())
End Using
End Using
Return cipherText
End Function
Here is the Java equivalent. Thanks for everyone's help! Make sure to install the JCE policy in the security folder of your Java as well.
String myData = "kgxCSfBSw5BRxmjgc4qYhwN12dxG0dyf=";
byte[] salt = new byte[] {0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
String pw = "kmjfds(#1231SDSA()#rt32geswfkjFJDSKFJDSFd";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(pw.toCharArray(), salt, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
SecretKeySpec secretSpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Cipher cipher1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
try {
cipher.init(Cipher.DECRYPT_MODE,secretSpec,ivSpec);
cipher1.init(Cipher.ENCRYPT_MODE,secretSpec,ivSpec);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//byte[] decordedValue;
//decordedValue = new BASE64Decoder().decodeBuffer(myData);
//decordedValue = myData.getBytes("ISO-8859-1");
//byte[] decValue = cipher.doFinal(myData.getBytes());
//Base64.getMimeEncoder().encodeToString(cipher.doFinal(myData.getBytes()));
//String decryptedValue = new String(decValue);
byte[] decodedValue = new Base64().decode(myData.getBytes());
String clearText = "ljfva09876FK";
//String encodedValue = new Base64().encodeAsString(clearText.getBytes("UTF-16"));
byte[] cipherBytes = cipher1.doFinal(clearText.getBytes("UTF-16LE"));
//String cipherText = new String(cipherBytes, "UTF8");
String encoded = Base64.encodeBase64String(cipherBytes);
System.out.println(encoded);
byte[] decValue = cipher.doFinal(decodedValue);
System.out.println(new String(decValue, StandardCharsets.UTF_16LE));
Your iteration count should be 1000 (instead of 1), which is the recommended minimum in the RFC and the (unspecified) default of Rfc2898DeriveBytes.
For the methods in this document, a minimum
of 1000 iterations is recommended
So that would translate into:
PBEKeySpec pbeKeySpec = new PBEKeySpec(pw.toCharArray(), salt, 1000, 384);
within the Java code. Note that a higher iteration count is highly recommended, especially if weaker passwords are allowed. 40K-100K is about the minimum now.
The incorrectly named Unicode actually means UTF-16 in .NET, so you should use:
new String(decValue, StandardCharsets.UTF_16LE)
within the last println statement of the Java code.
Here is the answer thanks to Maarten Bodewes
String myData = "kgxCSfBSw5BRxmjgc4qYhwN12dxG0=";
byte[] salt = new byte[] {0x49, 0x76, 0x61, 0x6E, 0x20, 0x4D, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76};
String pw = "kmjfds(#1231SDSA()#rt32geswfkjFJDSKFJDSFd";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(pw.toCharArray(), salt, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
System.arraycopy(secretKey.getEncoded(), 32, iv, 0, 16);
SecretKeySpec secretSpec = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try {
cipher.init(Cipher.DECRYPT_MODE,secretSpec,ivSpec);
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//byte[] decordedValue;
//decordedValue = new BASE64Decoder().decodeBuffer(myData);
//decordedValue = myData.getBytes("ISO-8859-1");
//byte[] decValue = cipher.doFinal(myData.getBytes());
//Base64.getMimeEncoder().encodeToString(cipher.doFinal(myData.getBytes()));
//String decryptedValue = new String(decValue);
byte[] decodedValue = new Base64().decode(myData.getBytes());
byte[] decValue = cipher.doFinal(decodedValue);
System.out.println(new String(decValue, StandardCharsets.UTF_16LE));
I want to use the PBE to generate other encryption keys.
public SecretKey generateKey(String Ags) throws Exception {
// make password
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(),this.salt,20,56);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBE");
SecretKey key = keyFactory.generateSecret(keySpec);
System.out.println();
/*
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(k);
//
SecretKey FINAL_key = new SecretKeySpec(key.getEncoded(), "AES");
*/
return null;
}
My basic idea is use PBEKeySpec and SecretKeyFactory to generate the PBE key first, and then get the first few bytes, let's say 10 bytes, to generate the AES key. However, after searching the Internet, I still don't know how to get the final key as a byte[]. key.getEncoded() will just give me the input password. How do I get the final key as a byte[]?
As far as i understand by reading the the documentation, i understand that if you want to create a AES secret key you need to feed the algorithm with an at least 128 bit of a key.
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
so to generate key why do you insist getting the 128 bits from the PBE key, instead you can use
byte[] key = (Password+Username).getBytes("UTF-8"); // depends on your implementation
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // AES uses 16 byte of key as a parameter (?)
you can also use the PBE key to feed SHA and get the bytes in that way. Okey let's turn to your problem, here is a full working code from my security folder, i remember this worked for me, please feel free to ask any questions. In the code below, if you check you'll see that the key is generated using the pbeKeySpec however when i review the code, i cannot see what's your fault though.
public void testPBEWithSHA1AndAES() throws Exception {
String password = "test";
String message = "Hello World!";
byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
(byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
int count = 1024;
// int keyLength = 256;
int keyLength = 128;
String cipherAlgorithm = "AES/CBC/PKCS5Padding";
String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
SecretKeyFactory keyFac = SecretKeyFactory
.getInstance(secretKeyAlgorithm);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
count, keyLength);
SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
// decrypt
keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
keyLength);
tmp = keyFac.generateSecret(pbeKeySpec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// AlgorithmParameters params = ecipher.getParameters();
// byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] encrypted = ecipher.doFinal(message.getBytes());
byte[] decrypted = dcipher.doFinal(encrypted);
assertEquals(message, new String(decrypted));
ByteArrayOutputStream out = new ByteArrayOutputStream();
CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
cipherOut.write(message.getBytes());
StreamUtils.closeQuietly(cipherOut);
byte[] enc = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(enc);
CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
ByteArrayOutputStream dec = new ByteArrayOutputStream();
StreamUtils.copy(cipherIn, dec);
assertEquals(message, new String(dec.toByteArray()));
}