I use the MachineKey.Protect()
method to encrypt the id passed as a query string in my asp.net MVC application.
Here's the code I use to encrypt/decrypt:
public static string Encrypt(this string expression)
{
if (string.IsNullOrEmpty(expression))
return string.Empty;
byte[] stream = Encoding.Unicode.GetBytes(expression);
byte[] encodedValue = MachineKey.Protect(stream);
return HttpServerUtility.UrlTokenEncode(encodedValue);
}
public static string Decrypt(this string expression)
{
if (string.IsNullOrEmpty(expression))
return string.Empty;
byte[] stream = HttpServerUtility.UrlTokenDecode(expression);
byte[] decodedValue = MachineKey.Unprotect(stream);
return Encoding.Unicode.GetString(decodedValue);
}
And, here is the MachineKey
element in my web.config
file:
<system.web>
.
.
.
<machineKey validationKey="xxx" decryptionKey="xxx" validation="SHA1" decryption="AES" />
</system.web>
The problem is the encrypted id is not persistent. Every time I call the method, I get a new encrypted expression. How do I make it persistent?
Summary:
If you want to get the same result every time, you need to use a different method to protect your data. MachineKey.Protect
uses a different IV for each run resulting in a different result every time.
Detail
Microsoft makes the source code for a lot of the dot net framework freely viewable on the internet.
Starting from the top: MachineKey
The protect method uses the AspNetCryptoServiceProvider
If you follow the code through AspNetCryptoServiceProvider.GetCryptoService
into NetFXCryptoService, you will find this:
public byte[] Protect(byte[] clearData) {
// The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures.
checked {
// These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block.
using (SymmetricAlgorithm encryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
// Initialize the algorithm with the specified key and an appropriate IV
encryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial();
if (_predictableIV) {
// The caller wanted the output to be predictable (e.g. for caching), so we'll create an
// appropriate IV directly from the input buffer. The IV length is equal to the block size.
encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize);
}
else {
// If the caller didn't ask for a predictable IV, just let the algorithm itself choose one.
encryptionAlgorithm.GenerateIV();
}
byte[] iv = encryptionAlgorithm.IV;
using (MemoryStream memStream = new MemoryStream()) {
memStream.Write(iv, 0, iv.Length);
// At this point:
// memStream := IV
// Write the encrypted payload to the memory stream.
using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor()) {
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write)) {
cryptoStream.Write(clearData, 0, clearData.Length);
cryptoStream.FlushFinalBlock();
// At this point:
// memStream := IV || Enc(Kenc, IV, clearData)
// These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block.
using (KeyedHashAlgorithm signingAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) {
// Initialize the algorithm with the specified key
signingAlgorithm.Key = _validationKey.GetKeyMaterial();
// Compute the signature
byte[] signature = signingAlgorithm.ComputeHash(memStream.GetBuffer(), 0, (int)memStream.Length);
// At this point:
// memStream := IV || Enc(Kenc, IV, clearData)
// signature := Sign(Kval, IV || Enc(Kenc, IV, clearData))
// Append the signature to the encrypted payload
memStream.Write(signature, 0, signature.Length);
// At this point:
// memStream := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
// Algorithm complete
byte[] protectedData = memStream.ToArray();
return protectedData;
}
}
}
}
}
}
}
The class was initialised with the default options, so _predictableIV
is false.
Therefore, it uses a new IV every time, which means the result will be different everytime, even with the same input.
The IV is included in the result so the Unprotect
method can reverse the encryption.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With