I have a web application written using Grails 6.1.2. I have to encrypt a couple of fields in the database so I decide to use the grails-jasypt based on the famous jasypt library. It works like a charm.
Now I have to write a small C# application and I need to access the DB to read some of those encrypted fields. Of course I know my password and algorithm used by the web app, from the properties I have:
jasypt {
algorithm = "PBEWITHSHA256AND256BITAES-CBC-BC"
providerName = "BC"
password = "my-strong-pwd"
keyObtentionIterations = 1000 }
Unfortunately the plugin in Grails hides all the code that has been used to encrypt the string I just add in the domain this mapping to be sure to encrypt and decrypt what I need:
static mapping = { dbPassword type: GormEncryptedStringType }
I wrote this static function in C# to decrypt the string that I got from the DB:
public static string Decrypt(string toDecrypt)
{
byte[] decoded = Convert.FromBase64String(toDecrypt);
if (decoded == null || decoded.Length <= 16)
{
throw new ArgumentException("Bad input data", "toDecrypt");
}
byte[] salt = new byte[16];
byte[] wrappedKey = new byte[decoded.Length - 16];
Array.Copy(decoded, 0, salt, 0, 16);
Array.Copy(decoded, 16, wrappedKey, 0, decoded.Length - 16);
int iterationCount = 1000;
string algName = "PBEWITHSHA256AND256BITAES-CBC-BC";
PbeParametersGenerator generator = new Pkcs5S2ParametersGenerator(new Org.BouncyCastle.Crypto.Digests.Sha256Digest());
generator.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(key.ToCharArray()), salt, iterationCount);
ICipherParameters parameters = generator.GenerateDerivedParameters("AES", 256);
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/NoPadding");
cipher.Init(false, new ParametersWithIV(parameters, new byte[16])); // Assuming 16-byte IV
byte[] keyText = cipher.DoFinal(wrappedKey);
return Convert.ToBase64String(keyText);
}
This works but the string is not been decrypted correctly, I don't know much about BC, I probably have to change the GetCipher string but I don't know with what. I try to add the padding with AES/CBC/PKCS5Padding but I got "pad block corrupted"
The decryption with PBEWITHSHA256AND256BITAES-CBC-BC
is implemented incorrectly, the following implementation works:
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
...
// input data
string algorithm = "PBEWITHSHA256AND256BITAES-CBC-BC";
int iterations = 1000;
string password = "dfglhkgfhljktypoio34537567--,dfd435";
string encryptedDataB64 = "Xw4VoIGs7FtD71gVwrDlCweSK7S/Z8ikg39rTxdiOcg=";
// separate salt and ciphertext
byte[] encryptedData = Convert.FromBase64String(encryptedDataB64);
byte[] salt = encryptedData[0..16];
byte[] ciphertext = encryptedData[16..];
// decrypt with PBEWITHSHA256AND256BITAES-CBC-BC
IBufferedCipher cipher = CipherUtilities.GetCipher(algorithm);
Asn1Encodable algParams = PbeUtilities.GenerateAlgorithmParameters(algorithm, salt, iterations);
ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(algorithm, password.ToCharArray(), algParams);
cipher.Init(false, cipherParams);
byte[] decrypted = cipher.DoFinal(ciphertext);
// UTF-8 decode and output
Console.WriteLine(Encoding.UTF8.GetString(decrypted)); // Test1234$$
Edit:
The above implementation uses the identifier PBEWITHSHA256AND256BITAES-CBC-BC
and does not require any further detailed knowledge of the algorithm.
An alternative (more low-level) way, in which the algorithm (AES/CBC/PKCS7Padding
), the key derivation (PKCS#12) and parameters such as key size and IV size must be explicitly specified, is:
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
...
// apply PKCS#12 key derivation and decrypt with AES/CBC/PKCS7Padding
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding");
PbeParametersGenerator generator = new Pkcs12ParametersGenerator(new Sha256Digest());
generator.Init(PbeParametersGenerator.Pkcs12PasswordToBytes(password.ToCharArray()), salt, iterations);
ICipherParameters cipherParams = generator.GenerateDerivedParameters("AES", 256, 128);
cipher.Init(false, cipherParams);
byte[] decrypted = cipher.DoFinal(ciphertext);
A comparison of the second approach with your code shows that in the first place you used the wrong key derivation and the IV size was not specified.