I have a little hard time understanding the RSACryptoServiceProvider class... I'm supposed to encrypt a message of length 256 bits, with a key , which is also 256 bits long. Shouldn't the output of also be 256 bits long?
Here's my code:
//key generation
byte[] bytes = new byte[32];
var rng = new RNGCryptoServiceProvider();
rng.GetBytes(bytes);
k2 = bytes;
//encryption function
static public byte[] Encryption(byte[] Data, RSAParameters RSAKey, bool DoOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportParameters(RSAKey);
encryptedData = RSA.Encrypt(Data, DoOAEPPadding);
}
return encryptedData;
}
And then finally calculating
ciphertext = Encryption(k2, RSA.ExportParameters(false), false);
produces a byte[128] ciphertext aka 1024 bits. Shouldn't I get ciphertext of size byte[32]?
It seems that you use the key, k2, as data for RSA encryption. That's OK if you want e.g. to wrap a 256 bit AES key using RSA. But your RSA key is the second parameter, not the first.
The data in k2 is then padded (according to the older PKCS#1 v1.5 scheme), after which modular exponentiation will be performed using the public exponent and modulus of the RSA key. The modulus of the RSA key determines the key size. This modulus exponentiation will always produce a result between zero and modulus - 1. However, the result is always left-padded to the key size in bytes (with a function called I2OSP).
So it seems your result is 1024 bits, which means that your RSA key pair is also 1024 bits.
Related
I need to create RSA key pair with custom public exponent size (specifically 64 bytes). Creating like this:
var rsa = new RSACng();
PrivateKey = rsa.ExportParameters(true);
or this
var rsa = RSA.Create();
PrivateKey = rsa.ExportParameters(true);
Always results in 3-bytes exponent: 1,0,1.
I've seen people are writing, that RSACng should support custom size exponent, but I can not find any good example.
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.
public string Encrypt(string Code)
{
string result = string.Empty;
byte[] encryptResult = null;
var CodeInByte = Encoding.ASCII.GetBytes(Code);
try
{
using (MemoryStream memo = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = KeySize;
AES.BlockSize = BlockSize;
var key = new Rfc2898DeriveBytes(CodeInByte, salt, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var encrypt = new CryptoStream(memo, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
encrypt.Write(CodeInByte, 0, CodeInByte.Length);
encrypt.Close();
}
encryptResult = memo.ToArray();
}
}
result = Convert.ToBase64String(encryptResult);
return result;
}
catch (Exception err)
{
MsgCode = 99;
MsgDesc = err.Message;
return string.Empty;
}
}
It's just a simple AES encrypting method from string
The point I want to ask, when generating the key, at
var key = new Rfc2898DeriveBytes(CodeInByte, salt, 1000);
is the key generated from inputted string, or it's just a random generated byte array?
and, is the salt needs to be static or not
As the documentation on MSDN suggests:
Rfc2898DeriveBytes takes a password, a salt, and an iteration count, and then generates keys through calls to the GetBytes method.
In other words, it will derive bytes using the input parameters you give it. If you give it different parameters, the derived key will be different. If you give it the same parameters, it will generate the same bytes.
Symmetrical encryption algorithms (such as AES) require a fixed length key - 16 bytes in this case for AES128. However, you don't want to mandate that passwords are fixed length as this makes them much easier to attack. You also might want much longer keys than a feasible password - AES256 would require a 32byte key, for example. Finally, passwords tend to be alphanumeric and perhaps have some symbols, whereas an encryption key is made up of bytes that can range from 0x00-0xFF, if you made the encryption key a 32 character ASCII password, then you'd reduce the range quite considerably as the printable ASCII character range is much smaller than 0x00-0xFF.
For this reason, you want to derive the encryption key from a given password in such a way that you get a strong key of the exact length you require. That's where Rfc2898DeriveBytes comes in.
I am implementing some classes for .NET that (among other things) simplify encryption and decryption.
My current algorithm creates an 8-byte salt, and uses that salt with the password to generate both the key and IV. I then store the salt, unencrypted, with my encrypted data.
This is nice because the salt appears to always be 8 bytes and that's all the overhead it adds to my encrypted data. However, is there any downside to using the same value for both my key and IV? Is there a better way?
Relevant code:
SymmetricAlgorithm algorithm = CreateAlgorithm();
byte[] salt = CreateSalt();
byte[] keyBytes = DeriveBytes(salt, algorithm.KeySize >> 3);
byte[] ivBytes = DeriveBytes(salt, algorithm.BlockSize >> 3);
Supporting code:
private static readonly int SaltLength = 8;
internal byte[] CreateSalt()
{
byte[] salt = new byte[SaltLength];
using (RNGCryptoServiceProvider generator = new RNGCryptoServiceProvider())
{
generator.GetBytes(salt);
}
return salt;
}
public byte[] DeriveBytes(byte[] salt, int bytes)
{
Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(Password, salt, 1000);
return derivedBytes.GetBytes(bytes);
}
OK, as long as you use a new, randomly created salt for each message, you are close to what I might do. The random salt means the IV will change with each new message, and this means that the exact same message will be different crypto-text each transmission. All good. The one thing I would change if I were you is instead of using DeriveBytes to get the key and then to get the IV, I would have DeriveBytes give a set of bytes the size of the key and IV together, then split them and use them separately. The IV should not have to be secret from anyone. The key must be. So if you DeriveBytes once from the same salt and password, then split those bytes into key and IV, the attacker is still no closer to knowing the key after looking at the IV than he was before.
Alternatively, you could use a nonce to create a known permutation between the IV bytes and the key bytes. For example, excuse my pseudocode:
IV = DeriveBytes(salt + password + "IV")
key = DeriveBytes(salt + password + "key")
Either way is secure. But I would just DeriveBytes on, say, 32 bytes and then use 16 of them for the IV and 16 of them for the key. There is no information in the first 16 bytes that will help an attacker calculate the next 16 bytes.
Yes, it defeats the purpose of the IV. The IV is used so if you encrypt the same message with the same key you don't get the same ciphertext. You might as well just use a constant value of 0, it adds the same amount of security.
In your case here you are using the same value for your
Key
IV
Conceptually this is a bad idea because the IV is supposed to be non-secret, and different for each encryption. You've solved the "different for each encryption", but you have it identical to your key.
The thing you're trying to defend against is making sure that two encryptions with the same key will not give the same ciphertext. In your case, this will only happen if the RNG generates two identical 128-bit AES keys.
While the odds of this are low, you should just not have it.
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));
}
}