I have set up a simple symmetric AES-en/decryption in C#, but I'm having problems with the padding. According to MSDN, the padding bytes for PKCS #7 are supposed to be 0x07, but in my case it's just zero-bytes (0x00).
How is this possible? It almost seems as if this was not correctly implemented in .NET...
Here is my code:
Aes aes = new AesManaged();
aes.Key = new byte[] { /* ... */ };
aes.IV = new byte[] { /* ... */ };
// Debugging shows:
// aes.Padding = PaddingMode.PKCS7
// the data to encrypt (1 byte only, to demonstrate padding)
byte[] plainData = new byte[1] { 0xFF };
byte[] encData;
// (encrypt)
using (MemoryStream encStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(encStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cryptoStream.Write(plainData, 0, plainData.Length);
}
encData = encStream.ToArray();
}
// (new length is 16 bytes (128 bits), incl. padding)
plainData = new byte[16];
// (decrypt)
using (MemoryStream decrStream = new MemoryStream(encData))
{
using (CryptoStream cryptoStream = new CryptoStream(decrStream, aes.CreateDecryptor(), CryptoStreamMode.Read))
{
cryptoStream.Read(plainData, 0, plainData.Length);
}
}
// output:
// 16 bytes,
// 1st byte = 0xFF,
// other 15 bytes = 0x00 (instead of 0x07!)
The decryptor is correctly removing the padding that was applied by the encryptor, thus the zero bytes in your output are simply the un-touched bytes in the original plainData array. The cryptoStream.Read(...) call returns an integer indicating the number of bytes that were read (1 in this case), which you should be using to determine how many bytes in the output array are valid data.
If for whatever reason you are interested in seeing the padding bytes, you can set aes.Padding = PaddingMode.None; after the encryption is performed, but before you create the decryptor. You will then find that cryptoStream.Read(...) returns 16, and plainData has 0xff as its first byte, followed by 15 bytes of 0x0f padding (not sure why your question indicates you were expecting 0x07 though).
For PKCS7 mode it should be blocksize - contentsize, i.e. 16 - 1 = 15 in your case. Your mistake is that you expect it after decryption but padding happens internally before encryption. There are no guarantees that plainData will contain padded bytes according to mode choosen.
Related
thanks in advance for reading.
The goal was to encrypt from command line and decrypt from c# while being as simple as possible. I'm starting from the docs: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-5.0
I have a file encrypted with this OpenSSL command (no salt):
openssl enc -nosalt -aes-128-cbc -in my_file.txt -out my_file.txt.enc -p -pass pass:hello_this_is_pass
and that outputs the Key and IV
key=2F77B7A1D3BBAA3304E53D791819958A
iv =9DD22E07DD38AF129D42E8CF3689EADD
Once in VS these were read into byte arrays with:
byte[] key = Encoding.ASCII.GetBytes("2F77B7A1D3BBAA3304E53D791819958A");
byte[] iv = Encoding.ASCII.GetBytes("9DD22E07DD38AF129D42E8CF3689EADD");
var encodedBytes = File.ReadAllBytes("myEncFile.txt.enc");
The file is read into a byte array with:
This is passed to the reading method from the docs:
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV; // **This is the line that errors**
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
Setting aesAlg.IV throws the following error:
System.Security.Cryptography.CryptographicException: 'Specified initialization vector (IV) does not match the block size for this algorithm.'
It looks like the default value of aesAlg.IV is byte[16]. I understand that this means the IV I'm providing is twice as big as it should be, so I assumed I must be using the wrong decryptor settings
The Key size is byte[32], and I've tried configuring the instance prior to assigning aesAlg.IV, but these changes seemed to have no effect:
aesAlg.Mode = CipherMode.CBC;
aesAlg.IVSize = 128;
aesAlg.BlockSize = 128;
aesAlg.FeedbackSize = 128;
aesAlg.Padding = PaddingMode.None;
I feel like there is something very obvious I'm missing. The similar questions on here deal with encryption, or the fact that openssl will auto add "Salt__" unless -nosalt is specified. There is also a similary named question "How I could re-encrypt in using C# so that I'd be able to decrypt with OpenSSL CLI tools?" but this does not address the error I am encountering (at least as far as I can tell). I feel like I've stuck so close to the docs that I can't be the only person who would run into this?
If anyone in the future finds this, #Topaco posted the answer in the comments. The solution is to change
byte[] key = Encoding.ASCII.GetBytes("2F77B7A1D3BBAA3304E53D791819958A");
byte[] iv = Encoding.ASCII.GetBytes("9DD22E07DD38AF129D42E8CF3689EADD");
to
byte[] key = StringToByteArrayFastest("2F77B7A1D3BBAA3304E53D791819958A");
byte[] iv = StringToByteArrayFastest("9DD22E07DD38AF129D42E8CF3689EADD");
Using StringToByteArrayFastest() from How can I convert a hex string to a byte array?
public static byte[] StringToByteArrayFastest(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;
}
public 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 using VS to encrypt some data using this function:
public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 6, 99, 26, 12, 68, 22, 89, 12, 49, 55, 92, 82, 87, 86, 10, 75, 98, 122, 73 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 256;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
My question is why when I put plain text to encrypt the output only contains readable characters that can be viewed with any text processor like notepad, but, if the data to encrypt is from a file containing special characters the output now also contains special characters that can't be viewed from text processor... why???
Example of encrypt "hello"
"fMIiLZzIKME2gTAarpQqP7y8kOjQvDS12lSOOBtaCbI="
Example of encrypt binary data:
"b!,˜à4ovƒº1úlÔÊ jô†õ ;>²Ñ)j¦¹‘åüLÖN—nU+5"
Because AES doens't deal with chars, or strings, only with bytes.
If you do want to be able to look at the result in a text editor, you'll have to encode the outputted bytes.
One of such encoders would be the base64 one.
Which would output a human "readable" string.
I found the probem... I'm an idiot... I have a Convert.ToBase64String function called when the input is plain text... I didn't see this.
Sorry.
Thans
I want to migrate following python code into c#.
The entry point is the method encrypted_request
I have no real clue about aes/rsa in python or in c#.
Maybe someone could explain the different code sections and if possible give me a hint how to implement that in c#.
Especially the magic numbers used here and there I do not understand.
modulus = ('00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7'
'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280'
'104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932'
'575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b'
'3ece0462db0a22b8e7')
nonce = '0CoJUm6Qyw8W8jud'
pubKey = '010001'
def encrypted_request(text):
text = json.dumps(text)
secKey = createSecretKey(16)
encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
encSecKey = rsaEncrypt(secKey, pubKey, modulus)
data = {'params': encText, 'encSecKey': encSecKey}
return data
def aesEncrypt(text, secKey):
pad = 16 - len(text) % 16
text = text + chr(pad) * pad
encryptor = AES.new(secKey, 2, '0102030405060708')
ciphertext = encryptor.encrypt(text)
ciphertext = base64.b64encode(ciphertext).decode('u8')
return ciphertext
def rsaEncrypt(text, pubKey, modulus):
text = text[::-1]
rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16)) % int(modulus, 16)
return format(rs, 'x').zfill(256)
def createSecretKey(size):
return binascii.hexlify(os.urandom(size))[:16]
Source: https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
My current state in c#:
private byte[] hex2Binary(string hex) {
byte[] binaryVal = new byte[hex.Length];
for (int i = 0; i < hex.Length; i++) {
string byteString = hex.Substring(i, 1);
byte b = Convert.ToByte(byteString, 16);
binaryVal[i] = b;
}
return binaryVal;
}
private string aesEncryptBase64(String plainText, string key) {
return aesEncryptBase64(plainText, hex2Binary(key));
}
private string aesEncryptBase64(String plainText, byte[] key) {
//pad = 16 - len(text) % 16
//text = text + chr(pad) * pad
int pad = 16 - plainText.Length % 16;
for (int i=0; i<pad; i++) {
plainText = plainText + ((char)pad);
}
byte[] plainBytes = null;
RijndaelManaged aes = new RijndaelManaged();
//aes.KeySize = 16;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = hex2Binary(client.neteaseFix.encryptInfo.iv);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.Close();
byte[] encryptedBytes = ms.ToArray();
return Convert.ToBase64String(encryptedBytes); //decode("u8")
}
Here are a couple of things I see right off the bat, but the question is a bit too open-ended:
In aesEncryptBase64 you are manually applying padding. The AES implementation in .NET does that for you. If you prefer to do it yourself you need to set aes.Padding = PaddingMode.None
In aesEncryptBase64 you create a RijndaelManaged object. Don't do that. You want AES, just use AES.Create(), which returns an AES object (not a Rijndael object).
.NET had support for the larger Rijndael algorithm before AES; and Rijndael with a block size of 128 bits is what got selected as AES, but Rijndael supports modes that AES does not, and you shouldn't really use them interchangeably (though many samples do).
In aesEncryptBase64 your aes, ms, and cs objects are all IDisposable, so you should have them in using statements.
The rsaEncrypt method in Python is doing raw RSA, which isn't supported in .NET (nor generally considered a good idea). Unless it's only called by routines which do the padding (and then it's just a pit of side-channel vulnerabilities).
If your rsaEncrypt (in Python) is only being called from routines which do the signature or encryption (or PSS or OAEP) padding then your .NET equivalent would be (using your method naming casing, instead of the normal ones in .NET)
private static rsaEncrypt(string text, string pubKey, string modulus)
{
RSAParameters rsaParams = new RSAParameters
{
Exponent = hex2Binary(pubKey),
Modulus = hex2Binary(modulus),
};
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParams);
return rsa.Encrypt(Encoding.ASCII.GetBytes(text), YOUNEEDTOPICKTHEPADDINGMODE);
}
}
It would be worlds better to improve all of the code around this, though, so that it doesn't have to do so much string re-parsing.
I'm trying to read a file on the server (in blocks of 5KB), encrypt the block using AES and send it to the client. On the client, i decrypt the received block, and append to a file to get back the original file.
However, my decrypted block size received on the client differs from the plaintext block which is encrypted on the server.
e.g.
I have a 15.5 KB exe file, so i have 15.5*1024/5*1024 = 4 blocks (round figure) to encrypt and send to client (The first 3 blocks are of 5120 bytes and last block is 512 bytes in length). On the client however, the blocks decrypted are of size 5057, 4970, 5016 and 512 bytes which equals a file size of 15.1 KB (less than what was actually sent by the server).
Here is my code snippet:
Server (sends the file to client):
FileStream fs = new FileStream("lcd.exe", FileMode.Open, FileAccess.Read);
//block size = 5KB
int blockSize = 5 * 1024;
//calculate number of blocks in data
long numberOfBlocks = fs.Length / blockSize;
if (fs.Length % blockSize != 0) numberOfBlocks++;
byte[] numberOfBlocksBytes = BitConverter.GetBytes(numberOfBlocks);
//send number of blocks to client
SendMessage(sw, numberOfBlocksBytes);
int count = 0, offset = 0, numberOfBytesToRead=0;
Aes objAes = new Aes();
while (count < numberOfBlocks)
{
byte[] buffer;
numberOfBytesToRead = blockSize;
if (fs.Length < offset + blockSize)
{
numberOfBytesToRead = (int)(fs.Length - offset);
}
buffer = new byte[numberOfBytesToRead];
fs.Read(buffer, 0, numberOfBytesToRead);
//encrypt before sending
byte[] encryptedBuffer = objAes.Encrypt(buffer, Encoding.Default.GetBytes(sessionKey), initVector);
SendMessage(sw, encryptedBuffer);
offset += numberOfBytesToRead;
count++;
}
fs.Close();
Client side code which receives the file:
byte[] numberOfBlocksBytes = ReadMessage(sr);
long numberOfBlocks = BitConverter.ToInt64(numberOfBlocksBytes, 0);
FileStream fs = new FileStream("lcd.exe", FileMode.Append, FileAccess.Write);
//block size = 5KB
int blockSize = 5 * 1024;
Aes objAes = new Aes();
int count = 0, offset = 0;
while (count < numberOfBlocks)
{
byte[] encryptedBuffer = ReadMessage(sr);
byte[] buffer = objAes.Decrypt(encryptedBuffer, sessionKey, initVector);
fs.Write(buffer, 0, buffer.Length);
offset += buffer.Length;
count++;
}
fs.Close();
My AES code for encryption:
private const int StandardKeyLength = 16;
public byte[] Encrypt(byte[] plainText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bPlainBytes = plainText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateEncryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream();
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Write);
objCs.Write(bPlainBytes, 0, bPlainBytes.Length);
objCs.FlushFinalBlock();
var bEncrypted = objMs.ToArray();
return bEncrypted;
}
My AES code for decryption:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
}
These are the results i got while debugging the while loop which sends file blocks on the server:
Actual File Size sent: 15.5 KB = 15872 bytes
Buffer size(plaintext) Encrypted Buffer Size(Sent) Offset Count
5120 5136 5120 0
5120 5136 10240 1
5120 5136 15360 2
512 528 15872 3
These are the results i got while debugging the while loop which receives file blocks on the client:
Actual File Size received: 15.1 KB = 15555 bytes
Received Buffersize Decrypted Buffer Size Offset Count
5136 5057 5057 0
5136 4970 10027 1
5136 5016 15043 2
528 512 15555 3
It is evident that the sending and receiving code is working fine (since encrypted buffer size which is sent = received buffer size). However, the decrypted buffer size does not match the buffer size (plaintext) at all except for the last block which is of length 512 bytes.
What can be possibly wrong with decryption because of which i'm not receiving the file completely on the client side?
You're being tripped up because in your Decrypt statement you are treating your ciphertext as if it is a string. Specifically, these lines:
var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();
return (Encoding.Default.GetBytes(strDecrypted));
Instead you want to be calling Read on your CryptoStream to read a raw byte array into a buffer. You can then return that buffer without attempting to coerce it into a string (which is what is happening by using the stream reader).
You should use something more like:
public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
{
throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
}
var bCipherBytes = cipherText;
var objRm = new RijndaelManaged();
objRm.Key = key;
objRm.IV = initVector;
objRm.Padding = PaddingMode.PKCS7;
objRm.BlockSize = 128;
var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
var objMs = new MemoryStream(bCipherBytes);
var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);
var buffer = new byte[cipherText.Length];
int readBytes = objCs.Read(buffer, 0, cipherText.Length);
var trimmedData = new byte[readBytes];
Array.Copy(buffer, trimmedData, readBytes);
return trimmedData;
}
I would also suggest you take a look at the encryption utilities I maintain on Snipt. Specifically the Symmetric Encrypt and Decrypt methods. Your code as it stands has a lot of using blocks missing and a number of potential resource leaks.
var streamobj = new StreamReader(objCs);
That's pretty unlikely to work well. The StreamReader will assume that the decrypted data is utf-8 encoded text. There is no hint whatsoever that this is actually the case from the code that encrypts the data, it takes a byte[].
Use a FileStream instead so no conversion is made at all. Also helps you avoid the Encoding.Default.GetBytes() data randomizer.
Quick observation, which may just be my ignorance: Encrypt() method uses default encoding to get the session key bytes. On the receiving end the Decrypt() method uses the sessionKey itself as second parameter, i.e., without getting bytes?
I have with this code:
RijndaelManaged rijndaelCipher = new RijndaelManaged();
// Set key and IV
rijndaelCipher.Key = Convert.FromBase64String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678912");
rijndaelCipher.IV = Convert.FromBase64String("1234567890123456789012345678901234567890123456789012345678901234");
I get this exception thrown:
Specified key is not a valid size for this algorithm.
Specified initialization vector (IV) does not match the block size for this algorithm.
What's wrong with this strings ? Can I count at some examples strings from you?
The string "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678912" when base64-decoded yields 48 bytes (384 bits). RijndaelManaged supports 128, 192 and 256 bit keys.
A valid 128-bit key is new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } or if you need to get it from base64 : Convert.FromBase64String("AAECAwQFBgcICQoLDA0ODw==").
The default blocksize is 128 bits, so the same byte-array will work as the IV.
Use the random number generator class (RNGCryptoServiceProvider) to fill a specified buffer with random bytes as follows:
var numberOfBits = 256; // or 192 or 128, however using a larger bit size renders the encrypted data harder to decipher
var ivBytes = new byte[numberOfBits / 8]; // 8 bits per byte
new RNGCryptoServiceProvider().GetBytes(ivBytes);
var rijndaelManagedCipher = new RijndaelManaged();
//Don't forget to set the explicitly set the block size for the IV if you're not using the default of 128
rijndaelManagedCipher.BlockSize = 256;
rijndaelManagedCipher.IV = ivBytes;
Note the same process could be used to derive a key. Hope this helps.
The RijndaelManaged algorithm supports key lengths of 128, 192, or 256 bits. Is your key one of these sizes?
here is the class i created
public class ByteCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private int _Keysize = (int)GlobalConfiguration.DataEncode_Key_Size;
private byte[] saltStringBytes;
private byte[] ivStringBytes;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
private string _passPhrase = GlobalConfiguration.DataEncode_Key;
private const string salt128 = "kljsdkkdlo4454GG";
private const string salt256 = "kljsdkkdlo4454GG00155sajuklmbkdl";
public ByteCipher(string passPhrase = null, DataCipherKeySize keySize = DataCipherKeySize.Key_128)
{
if (!string.IsNullOrEmpty(passPhrase?.Trim()))
_passPhrase = passPhrase;
_Keysize = keySize == DataCipherKeySize.Key_256 ? 256 : 128;
saltStringBytes = _Keysize == 256 ? Encoding.UTF8.GetBytes(salt256) : Encoding.UTF8.GetBytes(salt128);
ivStringBytes = _Keysize == 256 ? Encoding.UTF8.GetBytes("SSljsdkkdlo4454Maakikjhsd55GaRTP") : Encoding.UTF8.GetBytes("SSljsdkkdlo4454M");
}
public byte[] Encrypt(byte[] plainTextBytes)
{
if (plainTextBytes.Length <= 0)
return plainTextBytes;
using (var password = new Rfc2898DeriveBytes(_passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(_Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = _Keysize;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return cipherTextBytes;
}
}
}
}
}
}
public byte[] Decrypt(byte[] cipherTextBytesWithSaltAndIv)
{
if (cipherTextBytesWithSaltAndIv.Length <= 0)
return cipherTextBytesWithSaltAndIv;
var v = Encoding.UTF8.GetString(cipherTextBytesWithSaltAndIv.Take(_Keysize / 8).ToArray());
if (v != salt256 && v != salt128)
return cipherTextBytesWithSaltAndIv;
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((_Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((_Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(_passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(_Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.BlockSize = _Keysize;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return plainTextBytes;
}
}
}
}
}
}
}
I don't know the length of rijndaelCipher.Key
if it is 24, then rijndaelCipher.Key = s.SubString(0, 24);
So easy.