I'm working on software which encrypts/decrypts files.
I would like to be able to guess the length of the data after the encryption but I can't use CryptoStream.Length (It throws a NotSupportedException).
Is there any way to guess it ?
I'm using RijndaelManaged (.Net Framework 4.0)
This says it much better than I can
http://www.obviex.com/Articles/CiphertextSize.aspx
From there:
In the most generic case, the size of the ciphertext can be calculated as:
CipherText = PlainText + Block - (PlainText MOD Block)
where CipherText, PlainText, and Block indicate the sizes of the ciphertext, plaintext, and encryption block respectively. Basically, the resulting ciphertext size is computed as the size of the plaintext extended to the next block. If padding is used and the size of the plaintext is an exact multiple of the block size, one extra block containing padding information will be added.
Let's say that you want to encrypt a nine-digit Social Security Number (SSN) using the Rijndael encryption algorithm with the 128-bit (16-byte) block size and PKCS #7 padding. (For the purpose of the illustration, assume that dashes are removed from the SSN value before the encryption, so that "123-45-6789" becomes "123456789", and the value is treated as a string, not as a number.) If the digits in the SSN are defined as ASCII characters, the size of the ciphertext can be calculated as:
CipherText = 9 + 16 - (9 MOD 16) = 9 + 16 - 9 = 16 (bytes)
Notice that if the size of the plaintext value is the exact multiple of the block size, an extra block containing padding information will be appended to the ciphertext. For example, if you are to encrypt a 16-digit credit card number (defined as a 16-character ASCII string), the size of the ciphertext will be:
CipherText = 16 + 16 - (16 MOD 16) = 16 + 16 - 0 = 32 (bytes)
That depends on the cipher you use... usually the length is the same as the length of the original stream... worstcase is that it gets padded to a multiple of the block length of the cipher
This is my code with RijndaelManaged:
MemoryStream textBytes = new MemoryStream();
string password = #"myKey123"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsInput = new FileStream(#"C:\myEncryptFile.txt", FileMode.Open);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsInput, RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
cs.CopyTo(textBytes);
cs.Close();
fsInput.Close();
string myDecriptText = Encoding.UTF8.GetString(textBytes.ToArray());
You can use this function to get both length and data.
public static int GetLength(CryptoStream cs, out byte[] data)
{
var bytes = new List<byte>();
int b;
while ((b = cs.ReadByte()) != -1)
bytes.Add((byte)b);
data = bytes.ToArray();
return data.Length;
}
Related
I am using AES encryption in my application. I used 3 types of encryption AES-128,AES-192, AES-256 keysize.
When I encrypt with different keysize(128 or 192 or 256) with the same text, the encrypted string length is same for all keysize(128 and 192 and 256) whereas encrypted characters only differs. Is this correct? Is the length of the encrypted string length always same for every keysize?
static string GetEncryptedString_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
byte[] encrypted;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return Convert.ToBase64String(encrypted);
}
Checkout the link below. The size of your key doesn't change your output length. (Block cipher encryption)
Size of data after AES/CBC and AES/ECB encryption
Depending on the padding, key sizes etc and algo used you would get odd sizes
I always like to validate it so I just fidle around with the type I like to use and the data size I expect the input to have, like this i know what the size for the data needs to be in the database.
Try and play with your data in the link and see what you need.
Are you using SQL Server 2005 or above? If so you could just use VARCHAR(MAX) or NVARCHAR(MAX) for the column type.
If you want to be a bit more precise...
The maximum block size for RijndaelManaged is 256 bits (32 bytes).
Your maximum input size is 20 characters, so even if we assume a worst-case scenario of 4 bytes per character, that'll only amount to 80 bytes, which will then be padded up to a maximum of 96 bytes for the encryption process.
If you use Base64 encoding on the encrypted output that will create 128 characters from the 96 encrypted bytes. If you use hex encoding then that will create 192 characters from the 96 encrypted bytes (plus maybe a couple of extra characters if you're prefixing the hex string with "0x"). In either case a column width of 200 characters should give you more than enough headroom.
(NB: These are just off-the-top-of-my-head calculations. I haven't verified that they're actually correct!)
I have found an example that uses AES encrypt to encrypt text. The code is this:
public static string Encrypt(string PlainText, string Password,
string Salt = "Kosher", string HashAlgorithm = "SHA1",
int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
int KeySize = 256)
{
if (string.IsNullOrEmpty(PlainText))
return "";
byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
RijndaelManaged SymmetricKey = new RijndaelManaged();
SymmetricKey.Mode = CipherMode.CBC;
byte[] CipherTextBytes = null;
using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
{
using (MemoryStream MemStream = new MemoryStream())
{
using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
{
CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
CryptoStream.FlushFinalBlock();
CipherTextBytes = MemStream.ToArray();
MemStream.Close();
CryptoStream.Close();
}
}
}
SymmetricKey.Clear();
return Convert.ToBase64String(CipherTextBytes);
}
My question is: how is the key for the AES algorithm generated? These 2 lines:
PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
First, it creates a derived key of 256 bytes, and later, create a key getting pseudo random bytes of this derived key. It has to be divided by 8 because the AES algorithm need 128, 182 or 256 bits, not bytes. In this case, how derived key is 256 bytes, the key for AES will be 256 bits.
But why does it do that? Wouldn't it better create the derived key with the needed length, not 256 bytes but 256 bits (256 bytes / 8)? So It wouldn't be needed to create a new key taken the 1/8 bytes of the derived key.
Also, the getBytes() method, in the description of the method, it says it returns pseudo-random key bytes. So doesn't it do the AES key would be different in each case? How to generate again the AES key from decryption if it is pseudo random key bytes?
Thanks.
First, it creates a derived key of 256 bytes
Where? I don't see any 256-byte key being created.
and later, create a key getting pseudo random bytes of this derived key. It has to be divided by 8 because the AES algorithm need 128, 182 or 256 bits, not bytes
Yes, the function input of KeySize (which should be keySize by normal C# naming conventions) is in bits, but GetBytes wants an input in bytes. x / 8 is one of the three right answers for that conversion ((x + 7) / 8 is another, and x & 7 == 0 ? x / 8 : throw new ArgumentException(nameof(x)) is the third)
But why does it do that? Wouldn't it better create the derived key with the needed length, not 256 bytes but 256 bits (256 bytes / 8)? So It wouldn't be needed to create a new key taken the 1/8 bytes of the derived key.
It would be good to do that. But since it is already doing that, there's no "better" to be had.
Also, the getBytes() method, in the description of the method, it says it returns pseudo-random key bytes. So doesn't it do the AES key would be different in each case? How to generate again the AES key from decryption if it is pseudo random key bytes?
I have to make a pedantic point: There is no getBytes method. C# is a case-sensitive language, and the method name is GetBytes.
pseudorandom: noting or pertaining to random numbers generated by a definite computational process to satisfy a statistical test.
PasswordDeriveBytes is an implementation of PBKDF1 (except it continues beyond the limits of PBKDF1), which is a deterministic algorithm. Given the same inputs (password, seed, iteration count, pseudo-random function (hash algorithm)) the same output is produced. Change any of the inputs slightly, and the output is significantly different.
Rfc2898DeriveBytes (an implementation of PBKDF2) is also a deterministic, but chaotic, algorithm.
So you produce the same answer again in either of them (but not across them) by giving all the same inputs.
When using password-based encryption (PKCS#5) the flow is
Pick a PRF
Pick an iteration count
Generate a random salt
Write down these choices
Apply these three things, plus the password to generate a key
Encrypt the data
Write down to the encrypted data
When decrypting one
Read the PRF
Read the iteration count
Read the salt
Apply these three things, plus the password to generate a key
Read the encrypted data
Decrypt it
Party on
While this code is doing that part right, the IV and Salt should not be ASCII (or UTF8) strings, they should be "just bytes" (byte[]). If they need to be transported as strings then they should be base64, or some other "arbitrary" binary-to-text encoding.
I'm still studying cryptography. I'm trying to create a simple static function in C# that encrypts string to DES (with a Base64 ouput). I learned that DES use 8-Byte as its key. I want the user to input string of any length, use it as the key to encrypt the message, then convert it to Base64. Example is in this site.
public static string EncryptDES(string phrase, string key)
{
string encrypted = "";
byte[] phraseBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(phrase);
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(key);
System.Security.Cryptography.MD5CryptoServiceProvider hashMD5Provider
= new System.Security.Cryptography.MD5CryptoServiceProvider();
System.Security.Cryptography.DESCryptoServiceProvider provider
= new System.Security.Cryptography.DESCryptoServiceProvider();
provider.Mode = System.Security.Cryptography.CipherMode.CBC;
System.Security.Cryptography.ICryptoTransform transform
= provider.CreateEncryptor(keyBytes, keyBytes);
System.Security.Cryptography.CryptoStreamMode mode
= System.Security.Cryptography.CryptoStreamMode.Write;
System.IO.MemoryStream memStream = new System.IO.MemoryStream();
System.Security.Cryptography.CryptoStream cryptoStream
= new System.Security.Cryptography.CryptoStream(memStream, transform, mode);
cryptoStream.Write(phraseBytes, 0, phraseBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] encryptedMessageBytes = new byte[memStream.Length];
memStream.Position = 0;
memStream.Read(encryptedMessageBytes, 0, encryptedMessageBytes.Length);
encrypted = System.Convert.ToBase64String(encryptedMessageBytes);
return (encrypted);
} // private static string EncryptDES(string phrase, string key) { }
Then call it like this in Main:
SimpleEncryption.EncryptDES("A message regarding some secure 512-bit encryption", "AnUltimatelyVeryVeryLongPassword");
When a user inputs a random number of string length (whether greater than or less than 8 characters), a cryptographic exception always happens in this line:
System.Security.Cryptography.ICryptoTransform transform = provider.CreateEncryptor(keyBytes, keyBytes);
It says Specified key is not a valid size for this algorithm.
Removing parts of the key to fit in the length of 8 characters (with or without hashing) doesn't seems to be a secure solution (there might be a high rate of collision).
How can I implement DES (not 3DES) with a user input string?
You need to generate a hash from the user's password and take only 8 bytes to use as your key.
var fullHash = hashMD5Provider.ComputeHash(System.Text.Encoding.ASCII.GetBytes(key));
var keyBytes = new byte[8];
Array.Copy(fullHash , keyBytes, 8);
Your question expressed concern about hash collisions from throwing away part of the hash; yes, that certainly does increase the risk, but (assuming your hash algorithm is good) you're no worse off than if you just used a hash algorithm that only produced 8 bytes to begin with. A good hash algorithm should distribute the entropy evenly.
I need to encrypt a guid and the encrypted string length should be 32 char max, not more than that. Please suggest me an encryption method available in C# for that.
I was using AES in CFB mode, as in Code Project, but that is producing 64 char long.
Well, a GUID is inherently 16 bytes of data... so that's what you should encrypt. That's a single block in AES. As per Reid's comment, the exact size of the output will depend on how you've configured things, but to convert the result into text, you'll probably want to use base64 if you need ASCII text with a fairly minimal size.
Base64 allows you to use 24 bytes to produce a 32 character result - so you should try different padding/cipher modes until you find one where the output is 24 bytes or less, if this 32 character requirement is a "hard" one (and you need ASCII; if you don't need ASCII then there's a lot more room to play...)
If a GUID is 16 bytes (I'll take that as a given) then you can simply do a single AES ECB mode encrypt without padding of the plaintext (i.e. the GUID). You can then convert to hexadecimals. That will with 100% certainty result in a 32 character result.
Note that ECB does not use an IV, which means that you can distinguish different GUID's from each other (as each GUID will be mapped to exactly one ciphertext). But the ciphertext should otherwise simply be identical to the security of the used block cipher and key.
public class EncryptGUI
{
private Aes aes;
public EncryptGUI (byte[] key)
{
aes = Aes.Create ();
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = key;
}
public String encryptUID (byte[] guid)
{
ICryptoTransform aesDecryptor = aes.CreateDecryptor ();
byte[] result = aesDecryptor.TransformFinalBlock (guid, 0, guid.Length);
return ToHex (result);
}
public static string ToHex (byte[] data)
{
StringBuilder hex = new StringBuilder (data.Length * 2);
foreach (byte b in data)
hex.AppendFormat ("{0:x2}", b);
return hex.ToString ();
}
public static void Main (string[] args)
{
byte[] key = new byte[16];
EncryptGUI main = new EncryptGUI (key);
byte[] guid = new byte[16];
Console.Out.WriteLine (main.encryptUID (guid));
}
}
I know very little about Encryption, but my goal is to essentially decrypt strings. I have been given the AES(128) key.
However, I must retrieve the IV from the Encrypted string, which is the first 16 bits.
Heres the doc for salesforce for more information (if what i explained was incorrect)
Encrypts the blob clearText using the specified algorithm and private
key. Use this method when you want Salesforce to generate the
initialization vector for you. It is stored as the first 128 bits (16
bytes) of the encrypted blob
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_crypto.htm (encryptWithManagedIV)
For Retrieving the IV I've tried something like this (I don't believe it's right though):
public string retrieveIv()
{
string iv = "";
string input = "bwZ6nKpBEsuAKM8lDTYH1Yl69KkHN1i3XehALbfgUqY=";
byte[] bytesToEncode = Encoding.UTF8.GetBytes(input);
for(int i = 0; i <= 15; i++){
iv += bytesToEncode[i].ToString(); ;
}
return iv;
}
(Just ignore the fact that the input is hardcoded and not parameterized; easier for testing purposes)
Then use the Best answer from this question to decrypt the string
The IV shouldn't be expressed as a string - it should be as a byte array, as per the AesManaged.IV property.
Also, using Encoding.UTF8 is almost certainly wrong. I suspect you want:
public static byte[] RetrieveIv(string encryptedBase64)
{
// We don't need to base64-decode everything... just 16 bytes-worth
encryptedBase64 = encryptedBase64.Substring(0, 24);
// This will be 18 bytes long (4 characters per 3 bytes)
byte[] encryptedBinary = Convert.FromBase64String(encryptedBase64);
byte[] iv = new byte[16];
Array.Copy(encryptedBinary, 0, iv, 0, 16);
return iv;
}