I have the following code :
public static string Encrypt3Des(string cipherString)
{
string result = "";
byte[] keyArray;
byte[] ivArray;
byte[] toEncryptArray = Enc3DesPerChar(cipherString);
//string toEncryptString = ByteArrayToString(toEncryptArray);
// Get the key from config file
System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
string iv = (string)settingsReader.GetValue("InitializationVector", typeof(String));
keyArray = StringToByteArray(key);
ivArray = StringToByteArray(iv);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
//set the secret key for the tripleDES algorithm
tdes.Key = keyArray;
tdes.IV = ivArray;
//ChiperMode
tdes.Mode = CipherMode.CBC;
//PaddingMode(if any extra byte added)
tdes.Padding = PaddingMode.None;
ICryptoTransform cTransform = tdes.CreateEncryptor();
//transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
//Release resources held by TripleDes Encryptor
tdes.Clear();
result = ByteArrayToString(resultArray);
return result;
}
And this is my method :
protected static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
protected static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
protected static byte[] Enc3DesPerChar(String toEncrypt)
{
string toAsciiString = ByteArrayToString(Encoding.ASCII.GetBytes(toEncrypt));
string toRoll = toAsciiString;
int NumberChars = toRoll.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(toRoll.Substring(i, 2), 16);
}
return bytes;
}
Everything works fine with the above method until I found that the method cannot accept less than 8 character.
The block code that raise an error :
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
Error message :
Length of the data to encrypt is invalid.
Example input :
Encrypt3Des("14022000"); // return encrypt because 8 character or more
Encrypt3Des("1402200"); // return error because 7 character
Does anybody know why this is or how I can fix it? (I don't know if it comes from my encrypting method, but I know a web app which uses the exact same thing to encrypt strings and that one does work.)
EDIT :
The tool that I used for manual encrypt : 3des
The option must :
Text input type
Plaintext input text
3DES function
CBC mode
Fixed Key Hex
Fixed Init Vector
You are using padding as none. Set the padding mode to PKCS7.
Ok, I think just found the solution (my client told me how), I need to fill up the character with null before the loop. null can be converted to ascii with "00". so I decide to PadRight to the ascii result with '0' to 16 character, so one of my method become :
protected static byte[] Enc3DesPerChar(String toEncrypt)
{
string toAsciiString = ByteArrayToString(Encoding.ASCII.GetBytes(toEncrypt));
string toRoll = toAsciiString.PadRight(16,'0');
int NumberChars = toRoll.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(toRoll.Substring(i, 2), 16);
}
return bytes;
}
Related
I am trying to decrypt a ciphersaber encrypted hexadecimal message using an IV mixing round of 20 with the key MyKey.
The messages is:
bad85d9e7f5aff959b6b332b44af2cc554d8a6eb
I am doing this in pure C# and it should return the message: Hola Mundo
using System;
using System.Text;
public class Program
{
public static void Main(string[] args)
{
// Hexadecimal text
string hexText = "bad85d9e7f5aff959b6b332b44af2cc554d8a6eb";
// Convert hexadecimal text to byte array
byte[] encryptedData = new byte[hexText.Length / 2];
for (int i = 0; i < encryptedData.Length; i++)
{
encryptedData[i] = Convert.ToByte(hexText.Substring(i * 2, 2), 16);
}
// IV length
int ivLength = 1;
// Key loop iterations
int keyIterations = 20;
// Encryption key
string encryptionKey = "MyKey";
// Convert encryption key to byte array
byte[] keyData = Encoding.UTF8.GetBytes(encryptionKey);
// Create an array to store the IV
byte[] ivData = new byte[ivLength];
// Copy the first `ivLength` bytes of the encrypted data to the IV array
Array.Copy(encryptedData, 0, ivData, 0, ivLength);
// Create an array to store the encrypted message
byte[] messageData = new byte[encryptedData.Length - ivLength];
// Copy the remaining bytes of the encrypted data to the message data array
Array.Copy(encryptedData, ivLength, messageData, 0, messageData.Length);
// Create an array to store the decrypted message
byte[] decryptedData = new byte[messageData.Length];
// Perform the decryption
for (int i = 0; i < messageData.Length; i++)
{
decryptedData[i] = (byte)(messageData[i] ^ keyData[i % keyData.Length]);
for (int j = 0; j < keyIterations; j++)
{
decryptedData[i] = (byte)(decryptedData[i] ^ ivData[j % ivData.Length]);
}
}
// Convert the decrypted data to a string and print it
string decryptedMessage = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine("Decrypted message: " + decryptedMessage);
}
}
Now when I try it returns: �$�#���Jf=�I���
What mistake am I making in the code or am I implementing it wrong?
I tested the text with the following site to see if it was ok: https://ruletheweb.co.uk/cgi-bin/saber.cgi
CipherSaber uses as IV the first 10 bytes of the encrypted message. The rest is the actual ciphertext. The IV is appended to the key (giving the key setup input), which is used as input to the CipherSaber key setup, see CipherSaber, Technical description, 1st section.
In the posted code, an IV length of 1 is applied instead of 10, which incorrectly determines IV (and thus key setup input) and actual ciphertext. The correct determination of IV and actual ciphertext is:
private static (byte[], byte[]) SeparateIvCiphertext(byte[] ivCiphertext)
{
int ivLen = 10;
byte[] iv = new byte[ivLen];
Buffer.BlockCopy(ivCiphertext, 0, iv, 0, iv.Length);
byte[] ciphertext = new byte[ivCiphertext.Length - iv.Length];
Buffer.BlockCopy(ivCiphertext, iv.Length, ciphertext, 0, ciphertext.Length);
return (iv, ciphertext);
}
and of the key setup input:
private static byte[] GetKeySetupInput(byte[] key, byte[] iv)
{
byte[] keySetupInput = new byte[key.Length + iv.Length];
Buffer.BlockCopy(key, 0, keySetupInput, 0, key.Length);
Buffer.BlockCopy(iv, 0, keySetupInput, key.Length, iv.Length);
return keySetupInput;
}
Furthermore, the decryption itself seems to be implemented incorrectly or at least incompletely. CipherSaber uses RC4 as its encryption/decryption algorithm, which can be divided into a key setup and the actual encryption/decryption:
The referenced website performs decryption using CipherSaber-2. Compared to the original CipherSaber (referred to as CipherSaber-1), a modified key setup is used in which the CipherSaber-1/RC4 key setup is repeated multiple times, 20 times in the case of the posted data.
A description of the CipherSaber-1/RC4 key setup can be found here, Key-scheduling algorithm (KSA), a possible implementation for CipherSaber-2 is:
private static byte[] sBox = new byte[256];
private static void KeySetup(byte[] input, int iterations)
{
for (int i = 0; i < 256; i++)
{
sBox[i] = (byte)i;
}
int j = 0;
for (int cs2loop = 0; cs2loop < iterations; cs2loop++) // CipherSaber-2 modification
{
for (int i = 0; i < 256; i++)
{
j = (j + sBox[i] + input[i % input.Length]) % 256;
Swap(ref sBox[i], ref sBox[j]);
}
}
}
private static void Swap(ref byte val1, ref byte val2)
{
if (val1 == val2) return;
val1 = (byte)(val1 ^ val2);
val2 = (byte)(val2 ^ val1);
val1 = (byte)(val1 ^ val2);
}
The loop marked CipherSaber-2 modification in the code snippet is the modification compared to CipherSaber-1/RC4!
The actual encryption/decryption is described here, Pseudo-random generation algorithm (PRGA), a possible implememtation is:
private static byte[] Process(byte[] input)
{
int i = 0, j = 0;
byte[] result = new byte[input.Length];
for (int k = 0; k < input.Length; k++)
{
i = (i + 1) % 256;
j = (j + sBox[i]) % 256;
Swap(ref sBox[i], ref sBox[j]);
result[k] = (byte)(sBox[(sBox[i] + sBox[j]) % 256] ^ input[k]);
}
return result;
}
Note that this algorithm is used for both encryption and decryption.
With this, the posted encrypted message can be decrypted as follows:
using System;
using System.Text;
...
byte[] key = Encoding.UTF8.GetBytes("MyKey");
byte[] encryptedData = Convert.FromHexString("bad85d9e7f5aff959b6b332b44af2cc554d8a6eb");
(byte[] iv, byte[] ciphertext) = SeparateIvCiphertext(encryptedData);
byte[] keySetupInput = GetKeySetupInput(key, iv);
int iterations = 20;
KeySetup(keySetupInput, iterations);
byte[] plaintext = Process(ciphertext);
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Hola Mundo
which gives Hola Mundo as plaintext.
I want calculate the MAC with S-MAC to sign the plain with single DES plus final triple DES in Secure Channel. I tried as follows but is not worked.
Can anyone help me? Thanks.
byte[] mac_iv = ToHexBytes("0000000000000000");
byte[] mac_key = ToHexBytes("C6713F31B8DC1F8905DFECB4065CB81E"); // S-MAC
byte[] mac_plain = BytesAppend(ToHexBytes("8482000010"), ToHexBytes("1122334455667788"));
byte[] mac_cipher = DES_MAC8_ISO9797_M2_ALG3_Encrypt(mac_iv, mac_key, mac_plain);
Debug.Print("\nmac_cipher: " + ToHexString(mac_cipher));
//
private byte[] DES_MAC8_ISO9797_M2_ALG3_Encrypt(byte[] iv, byte[] key, byte[] plain)
{
try
{
// split the 16 byte key into key A and key B
var key1 = new byte[8];
Buffer.BlockCopy(key, 0, key1, 0, key1.Length);
var key2 = new byte[8];
Buffer.BlockCopy(key, 8, key2, 0, key2.Length);
// init DES CBC encryption with key A and an all-zero IV of 8 bytes
DES des = new DESCryptoServiceProvider();
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.None;
MemoryStream streamOut = new MemoryStream();
CryptoStream streamCrypto = new CryptoStream(streamOut, des.CreateEncryptor(key1, iv), CryptoStreamMode.Write);
// iterate over all full blocks within the message & for each block perform CBC encryption,
// throwing away the result (using the same cipher instance, you need to keep the state after all)
int fullBlocks = plain.Length / 8;
for (int i = 0; i < fullBlocks; i++) {
int off = i * 8;
byte[] block = new byte[off + 8];
Buffer.BlockCopy(plain, off, block, off, block.Length);
streamCrypto.Write(block, 0, block.Length);
streamCrypto.FlushFinalBlock();
}
// create a final block and copy the left over bytes from the message into it
byte[] final_block = new byte[8];
int left = plain.Length % 8;
Buffer.BlockCopy(plain, left, final_block, left, final_block.Length);
// at the next position add the initial padding indicator byte
// ???
// finalize the CBC encryption by encrypting the final block, and keep the result
streamCrypto.Write(final_block, 0, final_block.Length);
streamCrypto.FlushFinalBlock();
byte[] res = streamOut.ToArray();
// perform DES ECB decryption over the result with key B, replacing the result
des.Mode = CipherMode.ECB;
streamCrypto = new CryptoStream(streamOut, des.CreateDecryptor(key2, iv), CryptoStreamMode.Write);
streamCrypto.Write(res, 0, res.Length);
res = streamOut.ToArray();
// peform DES ECB encryption over the result with key A, replacing the result
des.Mode = CipherMode.ECB;
streamCrypto = new CryptoStream(streamOut, des.CreateDecryptor(key1, iv), CryptoStreamMode.Write);
streamCrypto.Write(res, 0, res.Length);
res = streamOut.ToArray();
return res;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return new byte[0];
}
I resolved my problem with answer from #Jeff2022 in C# Implementation of Retail MAC Calculation (ISOIEC 9797-1 MAC algorithm 3) Thanks.
private static byte[] getCC_MACNbytes(string Key_MAC, byte[] eIFD, string Init_Vec)
{
byte[] Kmac = StringToByteArray(Key_MAC);
// Split the 16 byte MAC key into two keys
byte[] key1 = new byte[8];
Array.Copy(Kmac, 0, key1, 0, 8);
byte[] key2 = new byte[8];
Array.Copy(Kmac, 8, key2, 0, 8);
DES des1 = DES.Create();
des1.BlockSize = 64;
des1.Key = key1;
des1.Mode = CipherMode.CBC;
des1.Padding = PaddingMode.None;
des1.IV = new byte[8];
DES des2 = DES.Create();
des2.BlockSize = 64;
des2.Key = key2;
des2.Mode = CipherMode.CBC;
des2.Padding = PaddingMode.None;
des2.IV = new byte[8];
// Padd the data with Padding Method 2 (Bit Padding)
System.IO.MemoryStream out_Renamed = new System.IO.MemoryStream();
out_Renamed.Write(eIFD, 0, eIFD.Length);
out_Renamed.WriteByte((byte)(0x80));
while (out_Renamed.Length % 8 != 0)
{
out_Renamed.WriteByte((byte)0x00);
}
byte[] eIfd_padded = out_Renamed.ToArray();
int N_bytes = eIfd_padded.Length/8; // Number of Bytes
byte[] d1 = new byte[8];
byte[] dN = new byte[8];
byte[] hN = new byte[8];
byte[] intN = new byte[8];
// MAC Algorithm 3
// Initial Transformation 1
Array.Copy(eIfd_padded, 0, d1, 0, 8);
hN = des1.CreateEncryptor().TransformFinalBlock(d1, 0, 8);
// Split the blocks
// Iteration on the rest of blocks
for (int j = 1; j<N_bytes; j++)
{
Array.Copy(eIfd_padded, (8*j), dN, 0, 8);
// XOR
for (int i = 0; i < 8; i++)
intN[i] = (byte)(hN[i] ^ dN[i]);
// Encrypt
hN = des1.CreateEncryptor().TransformFinalBlock(intN, 0, 8);
}
// Output Transformation 3
byte[] hNdecrypt = des2.CreateDecryptor().TransformFinalBlock(hN, 0, 8);
byte[] mIfd = des1.CreateEncryptor().TransformFinalBlock(hNdecrypt, 0, 8);
// Get check Sum CC
return mIfd;
}
I spent a whole day investigating this and search all related questions on Stack Overflow for this question so please don't mention about possible duplicates.
The code below gives me a System.Security.Cryptography.CryptographicException: 'Specified padding mode is not valid for this algorithm.'
While using the very same parameters on this website : http://aes.online-domain-tools.com it decrypts perfectly into "Hello world" then filled with five 'x05' bytes for padding (PKCS#7 padding).
However the code below will always yield an exception when calling the TransformFinalBlock()
Context:
Console application running on Win8.1 with .NET Core 2.0 / Algorithm is AES / CBC / padding PKCS#7
I also tried the proposed solution here: Specified padding mode is not valid for this algorithm - c# - System.Security.Cryptography but no success (I also don't understand why if IV is already set in the SymmetricAlgorithm instance, it should be used later on when deciphering?
static void Main(string[] args)
{
string encryptedStr = "e469acd421dd71ade4937736c06fdc9d";
string passphraseStr = "1e089e3c5323ad80a90767bdd5907297b4138163f027097fd3bdbeab528d2d68";
string ivStr = "07dfd3f0b90e25e83fd05ba338d0be68";
// Convert hex strings to their ASCII representation
ivStr = HexStringToString(ivStr);
passphraseStr = HexStringToString(passphraseStr);
encryptedStr = HexStringToString(encryptedStr);
// Convert our ASCII strings to byte arrays
byte[] encryptedBytes = Encoding.ASCII.GetBytes(encryptedStr);
byte[] key = Encoding.ASCII.GetBytes(passphraseStr);
byte[] iv = Encoding.ASCII.GetBytes(ivStr);
// Configure our AES decryptor
SymmetricAlgorithm algorithm = Aes.Create();
algorithm.Mode = CipherMode.CBC;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.KeySize = 256;
//algorithm.BlockSize = 128;
algorithm.Key = key;
algorithm.IV = iv;
Console.WriteLine("IV length " + iv.Length); // 16
Console.WriteLine("Key length " + key.Length); // 32
ICryptoTransform transform = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV);
// Perform decryption
byte[] outputBuffer = transform.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
// Convert it back to a string
string result = Encoding.ASCII.GetString(outputBuffer);
Console.WriteLine(result);
Console.ReadLine();
}
public static string HexStringToString(string hexString)
{
var sb = new StringBuilder();
for (var i = 0; i < hexString.Length; i += 2)
{
var hexChar = hexString.Substring(i, 2);
sb.Append((char)Convert.ToByte(hexChar, 16));
}
return sb.ToString();
}
The problem is in the way how you convert hex string to byte array. Try to debug your code and check the value of array encryptedBytes. You'll see the following array:
{ 0x3f, 0x69, 0x3f, 0x3f, 0x21, 0x3f, 0x71, 0x3f, 0x3f, 0x3f, 0x77, 0x36, 0x3f, 0x6f, 0x3f, 0x3f }
which is far from input e469acd421dd71ade4937736c06fdc9d.
You shouldn't use System.String object as just a holder of binary char codes because .Net strings are UTF16-encoded.
Now when root cause is clear, the fix is pretty straighforward. Change your HexStringToString method so that it converts hex string to bytes array directly:
public static byte[] HexStringToByteArray(string hexString)
{
if (hexString.Length % 2 != 0)
{
throw new InvalidOperationException($"Inalid hex string '{hexString}'");
}
byte[] bytes = new byte[hexString.Length / 2];
for (var i = 0; i < hexString.Length; i += 2)
{
var hexChar = hexString.Substring(i, 2);
bytes[i / 2] = Convert.ToByte(hexChar, 16);
}
return bytes;
}
Then adjust the code in Main():
byte[] encryptedBytes = HexStringToByteArray(encryptedStr);
byte[] key = HexStringToByteArray(passphraseStr);
byte[] iv = HexStringToByteArray(ivStr);
This will give you desired Hello world in result variable.
I would like to be able to perform application-level encryption in ASP.NET, producing an array of bytes that would then be saved to a MySQL blob column. I would then like it to be an option that, if you have the encryption key, you would be able to decrypt it using MySQL's AES_DECRYPT() function. This seems like it should be possible, since AES_DECRYPT is an implementation of AES/Rijndael.
The MySQL AES_ENCRYPT/DECRYPT functions simply take a key and the string to encrypt/decrypt as parameters. The examples i've seen for encryption in ASP.NET/C#, however, involve also specifying values for Key and IV (initialization vector). How do these affect the final, encrypted byte array, and how can they be taken into account when decrypting with AES_DECRYPT)_?
You can do that by setting RijndaelManaged to use ECB mode.
However, ECB mode is not secure and should be avoided.
In general, a database is a very bad place to perform encryption.
If you are able to encrypt your data in the database, that implies that you have both the ciphertext and the key in the same place; this defeats the purpose of encryption.
You should keep the key as far away from ciphertext storage as possible; using any sort of SQL encryption function is usually indicative of a fundamental design flaw in your encryption strategy which can have disastrous consequences.
Encryption
In Mysql use HEX(AES_ENCRYPT('unencryptedString', 'Password'))
Example
UPDATE `secrets` SET `value`=HEX(AES_ENCRYPT('unencryptedString', 'Password')) WHERE `Id` = 2;
you will see in the database there is a value similar to this D4B5E4CAD92FFB73FCAEB5ED3B31E9EDD8FA7440E9E3F582FE5A9237DB8EE013
Now the equivalent code in C# is (Original Source:link)
public static String AES_encrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
byte[] xXml = Encoding.UTF8.GetBytes(Input);
cs.Write(xXml, 0, xXml.Length);
cs.FlushFinalBlock();
}
xBuff = ms.ToArray();
}
return xBuff.ToHexString();
}
Helper methods and extensions that used
Refernce Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static class ByteArrayExtensions
{
public static string ToHexString(this byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
}
Decryption
in Mysql use CAST(AES_DECRYPT(UNHEX(c.value), 'Password') as char)
Example
SELECT c.*,CAST(AES_DECRYPT(UNHEX(c.`value`), 'Password') as char) FROM `secrets` as c where `Id` = 2;
Equivalent code in C# is
public static String AES_decrypt(String Input, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
aes.Key = mkey(key);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
var decrypt = aes.CreateDecryptor();
byte[] encryptedStr = Input.FromHex2ByteArray();
string Plain_Text;
using (var ms = new MemoryStream(encryptedStr))
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cs))
{
Plain_Text = reader.ReadToEnd();
}
}
}
return Plain_Text;
}
Helper methods and extensions that used
Reference Link
private static byte[] mkey(string skey)
{
byte[] key = Encoding.UTF8.GetBytes(skey);
byte[] k = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < key.Length; i++)
{
k[i % 16] = (byte)(k[i % 16] ^ key[i]);
}
return k;
}
Reference Link
public static byte[] FromHex2ByteArray(this string hex)
{
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
byte[] arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}
return arr;
}
private static int GetHexVal(char hex)
{
int val = (int)hex;
//For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
//return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}
I'm trying to decrypt passwords that were stored in a database from a standard SqlMembershipProvider. In order to do this, I hacked together following console app:
static void Main(string[] args)
{
const string encryptedPassword = #"wGZmgyql4prPIr7t1uaxa+RBRJC51qOPBO5ZkSskUtUCY1aBpqNifQGknEfWzky4";
const string iv = #"Jc0RhfDog8SKvtF9aI+Zmw==";
var password = Decrypt(encryptedPassword, iv);
Console.WriteLine(password);
Console.ReadKey();
}
public static string Decrypt(string toDecrypt, string iv)
{
var ivBytes = Convert.FromBase64String(iv);
const string decryptKey = "DECRYPTION_KEY_HERE";
var keyArray = StringToByteArray(decryptKey);
var toEncryptArray = Convert.FromBase64String(toDecrypt);
var rDel = new AesCryptoServiceProvider() { Key = keyArray, IV = ivBytes};
var cTransform = rDel.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Encoding.UTF8.GetString(resultArray);
}
public static byte[] StringToByteArray(String hex)
{
var numberChars = hex.Length;
var bytes = new byte[numberChars / 2];
for (var i = 0; i < numberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
This does indeed decrypt the text, however instead of the resulting text being something like "Password1", it's "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0P\0a\0s\0s\0w\0o\0r\0d\01\0" which writes to the console as a bunch of spaces, then "P a s s w o r d 1". Any ideas what I'm doing wrong?
I suspect that part of the problem might be that the original password was encoded as UTF-16 before encryption, and you're decoding it as UTF-8. Try changing the final line of your Decrypt method:
return Encoding.Unicode.GetString(resultArray);
That doesn't explain all those spurious leading zeros though. Very strange...
EDIT...
Actually, I seem to remember that SqlMembershipProvider prefixes the password bytes with a 16-byte salt before encryption, in which case you'll probably be able to get away with something like this:
return Encoding.Unicode.GetString(resultArray, 16, resultArray.Length - 16);
But that still doesn't explain why those 16 bytes are all zeros rather than a bunch of random values...