AES256 encryption, output characters depending on input... why? - c#

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

Related

Decrypting AES in C# where the file was encryped using OpenSSL -nosalt; the AES is expecting a size 16 byte array IV?

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));
}

AES / RSA implementation from Python to C#

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.

C# AES PKCS #7 wrong padding bytes?

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.

Specified key is not a valid size for this algorithm

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.

C# RSA encryption using modulus and public exponent

I have to implement a digital envelope using AES and RSA, but I am having problems with the .NET implementation of the RSA algorithm.
I have managed to encrypt the data (AES) with the random symmetric key, but now I have to encrypt the key with RSA.
The key is an array of bytes (byte[]) and the public key I have tells me only the modulus and the public exponent, both arrays of bytes (byte[]).
Using only those two parameters, how can I encrypt my AES generated key with RSA?
The following code retrieves the message from file and encrypts it with AES.
Afterwards, the public key is read from the public key file and the modulus and the exponent are in their appropriate byte arrays. How would I continue to encrypt the symmetricKey with RSA?
String msgString = Systematic.GetFileContents(messagePath);
Byte[] initVector = new byte[] { 50, 60, 70, 80, 90, 40, 50, 60, 70, 80, 90, 40, 60, 80, 70, 90 };
Byte[] symetricKey = AesCrypt.GenerateRandomKey();
Byte[] encryptedMessage = AesCrypt.Encrypt(msgString, symetricKey, initVector, mode);
Byte[] modulus = null;
Byte[] publicExp = null;
DataFormatHelper.ReadPublicKey(publicKeyPath, "RSA", ref modulus, ref publicExp);
P.S. In reply to the answer mentioning rsa.ImportParameters:
I've tried with the rsa.ImportParameters(keyInfo) but it throws a CryptographicException ("Bad Data"). What about array sizes?
Currently, the modulus is 128 bytes and the exponent 64 bytes.
Using RSACryptoServiceProvider
static public byte[] RSAEncrypt(byte[] data,
RSAParameters keyInfo,
bool doOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This only needs
//toinclude the public key information.
rsa.ImportParameters(keyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or later.
encryptedData = rsa.Encrypt(data, doOAEPPadding);
}
return encryptedData;
}
So what you need are the RSAParameters but all you need to set are the Modulus and the Exponent to encrypt.

Categories

Resources