I have the following code. I expected the console output to be 32 (bytes), but it's actually a lot longer. Why is that? Can I trim that down?
ECDsaCng keyPair = new ECDsaCng(256);
byte[] privateKey = keyPar.ExportECPrivateKey();
//This will be 165 bytes
Console.WriteLine(privateKey.Length);
This doesn't makes sense to me, since as I understand both the public and private keys should be exactly 32 bytes long. I found this other question which helped me a bit, but I don't understand why we need to stores keys in formats.
Can't I just get the private key as a 32 bytes long byte array? What do I need a format for?
I am using Rfc2898DeriveBytes to generate an AES key and iv. However, I heard that the iv should not be dependent on the password. Here's how I'm doing it right now:
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
byte[] iv = rfc.GetBytes(16); // Should I do this or generate it randomly?
}
My question: Is it OK (secure) to generate the iv from Rfc2898DeriveBytes? Or should I generate it randomly using RNGCryptoServiceProvider?
Let's look at your code;
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
byte[] iv = rfc.GetBytes(16); // Should I do this or generate it randomly?
}
Random salt - Good
Rfc2898DeriveBytes with salt; this is good as long as the user password has good strength. The strength (not entropy!) of the derived key cannot exceed the passwords' strength.
Call GetBytes(32) for Key - Good, this is what is expected.
Call GetBytes(16) for IV -
This is good, too; since
Repeated calls to this method will not generate the same key; instead, appending two calls of the GetBytes method with a cb parameter value of 20 is the equivalent of calling the GetBytes method once with a cb parameter value of 40.
For each encryption, you can continue to get a new IV by calling GetBytes(16). Of course, there is a limit to that. PKKDF2 standard limits the output 2^32-1 * hLen, see in RFC 8018.
There is nothing wrong with outputting some part as IV and keeping some part as the encryption key. There are already tons of password schemes using PBKDF2 and non have been broken even the password hash and salt has been known.
If you are fearing that is not a good idea, then you can use either;
Generate two salts and derive the IV and encryption key separately form password as;
byte[] saltForKey = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfcKey = new Rfc2898DeriveBytes(plainStrPassword, saltForKey)) {
byte[] aesKey = rfcKey.GetBytes(32);
byte[] saltForIV = GenerateRandomBytes(32); // Generates 32 random bytes
using (Rfc2898DeriveBytes rfcIV = new Rfc2898DeriveBytes(plainStrPassword, saltForIV)) {
byte[] iv = rfcIV.GetBytes(16); // Should I do this or generate it randomly?
}
Generate random Salt and derive the encryption key and jus generate a random IV
byte[] salt = GenerateRandomBytes(32); // Generates 32 random bytes for Salt
byte[] IV = GenerateRandomBytes(16); // Generates 16 random bytes of IV
using (Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(plainStrPassword, salt)) {
byte[] aesKey = rfc.GetBytes(32);
}
Note that, you did not define the encryption mode. For a mode like
CTR mode, the 96-bit nonce, and 32-bit counter are common. For this, the 96-bit nonce can be generated by a counter/LFSR, too. Make sure that a (key,IV) pair never occurs.
CBC mode, the nonce must be random and unpredictable. The above is fine for this.
Of course, you should forget those and use authenticated encryption modes like AES-GCM, ChaCha20-Poly1305. If you fear the IV reuse then use AES-GCM-SIV that can only leak that you sent the same message, nothing else leaked. SIV mode is just to times slower, since it must pass the plaintext to derive the IV, then encryption is executed.
No, it's not secure to derive the IV from the same source from which you derive the key. The IV exists so that encryption of identical messages under the same key produces different ciphertexts.
You should use a cryptographically secure random source (such as RNGCryptoServiceProvider you identified) to derive the IV and communicate it alongside the ciphertext (typically either prepended to the ciphertext as one stream of bytes or in a separate field within a more structured file format).
Many cryptographic algorithms are expressed as iterative algorithms. E.g., when encrypting a message with a block cipher in CBC mode, each message "block" is first XORed with the previous encrypted block, and the result of the XOR is then encrypted. The first block has no "previous block" hence we must supply a conventional alternate "zero-th block" which we call "initialization vector". Generally speaking, an IV is whatever piece of data is needed to begin running an algorithm, and is not secret (if it was secret, we would call it a "key", not an IV).
IV is an arbitrary constant so any value will work. Make sure your encryptor and decryptor uses the same value. For more info you can refer these links:
https://crypto.stackexchange.com/questions/732/why-use-an-initialization-vector-iv
https://crypto.stackexchange.com/questions/3965/what-is-the-main-difference-between-a-key-an-iv-and-a-nonce
Based on this MS Docs, it is fine to use Rfc2898DeriveBytes for generating iv from password. Rfc2898DeriveBytes is implementation of PBKDF2, which the purpose is for: password-based key derivation functionality. See the example there.
PS: you should use RNGCryptoServiceProvider for generating salt.
I'm using the RSACryptoServiceProvider to encrypt and decrypt simple strings and while it works with normal certificates, it's giving me some grief when it comes to smart cards.
Here is the code I'm using:
private X509Certificate2 _cert // this certificate is set early on in the program
private string Encrypt(string Data)
{
if (string.IsNullOrEmpty(Data)) return default(string);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)_cert.PublicKey.Key;
return Convert.ToBase64String(((rsa.Encrypt(Encoding.Unicode.GetBytes(Data), true))));
}
private string Decrypt(string CipherText)
{
if (string.IsNullOrEmpty(CipherText)) return default(string);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)_cert.PrivateKey;
return Encoding.Unicode.GetString(((rsa.Decrypt(Convert.FromBase64String(CipherText), true))));
}
It does decrypt successfully (no errors) but the encoding isn't correct. It's giving me results like
\u08d8黔㡉Ẑ༴쨳층器\u0888諬翉烽偪䚘螷퓰薑낯ꄯ鯪ꘇ台ᾨ鳞텟칆蘟マ⺁ൿ䤳譻宐Ṹ鉱㒎艴偃堎え뢈癘蚰૩�⸮賆슉ଞ맿댿䀵㓹摵�뼚⡨ቾ᳓낣쏀ꖌ엷ὦ楐豎⸌ꅑ뙳餱Ч鎋筧粅嚄罜칮嬒쐞ڮ묭泊䘐쫦⊗邀☇仇挃箍絁绺罽华⏓፦귪ﻳ咷믭鹺簽艉闼敹Ԓ嵯젨泪ꔤ狫ꆙ\uab41軧\n"
What's also interesting is no matter what I encrypt, the decrypted byte[] length is always 256.
I know smart card private key operations always take place on the card itself so I'm sure that has something to do with it but I was hoping some of you have experience with this and can save me the mounds of trial-and-error.
Thanks!
It seems your Smart Card performs so called raw or textbook RSA. In other words, it only performs modular exponentiation, leaving the PKCS#1 v1.5 (EME-PKCS1-v1_5) or - in your case - OAEP (EME-OAEP decoding) decoding up to you. This padding has the same size as the key size / modulus size. In your case that would be 256 bytes or 2048 bits.
So something is very wrong with the Smart Card that's used. Unpadding should be an integral part of the functionality provided by it; it makes little sense to do that on the PC.
The padding largely consists of random (looking) data so it's little wonder that it looks like random text, especially now UTF-16 LE is being used (incorrectly called Unicode by .NET).
I need to derive a key from a salted password using PBKDF2 encryption in a C# & C++ Metro (WinRT) application. What should I use to derive a key using PBKDF2 (like OpenSSL's PKCS5_PBKDF2_HMAC_SHA1 call does) on Metro? Is there a version of OpenSSL that builds on WinRT? (I've read that it only builds on Windows for the desktop platform.) Or is there some other solution I should use?
BTW I could call the function from either C# or C++, so either is fine. Any advice would be much appreciated!
EDIT:
I just found a .NET function named "Rfc2898DeriveBytes" -- details here. If I'm reading that correctly it will do the same thing as OpenSSL's PKCS5_PBKDF2_HMAC_SHA1 call -- is that correct?
EDIT #2:
Unfortunately it looks like I can't use Rfc2898DeriveBytes after all in my Windows 8.1 Metro app because despite what the Microsoft documentation for Rfc2898DeriveBytes says, that API method does not exist in the 'Windows.Security.Cryptography' namespace when building a Windows 8.1 app. Is there anything else I can use?
After much digging around I finally found this link. Here is what I ended up doing in my Metro app:
private static bool GetPBKDFDerivedKey(string password,
byte[] salt, // length = 32 bytes (256 bits)
out byte[] encryptionKeyOut) // length = 32 bytes (256 bits)
{
IBuffer saltBuffer = CryptographicBuffer.CreateFromByteArray(salt);
KeyDerivationParameters kdfParameters = KeyDerivationParameters.BuildForPbkdf2(saltBuffer, 10000); // 10000 iterations
// Get a KDF provider for PBKDF2 and hash the source password to a Cryptographic Key using the SHA256 algorithm.
// The generated key for the SHA256 algorithm is 256 bits (32 bytes) in length.
KeyDerivationAlgorithmProvider kdf = KeyDerivationAlgorithmProvider.OpenAlgorithm(KeyDerivationAlgorithmNames.Pbkdf2Sha256);
IBuffer passwordBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8);
CryptographicKey passwordSourceKey = kdf.CreateKey(passwordBuffer);
// Generate key material from the source password, salt, and iteration count
const int keySize = 256 / 8; // 256 bits = 32 bytes
IBuffer key = CryptographicEngine.DeriveKeyMaterial(passwordSourceKey, kdfParameters, keySize);
// send the generated key back to the caller
CryptographicBuffer.CopyToByteArray(key, out encryptionKeyOut);
return true; // success
}
You can use Rfc2898DeriveBytes as the RFC actually defines PBKDF2. Note that you need to make sure you use the same character encoding, salt size and number of rounds to be compatible. Normally SHA1 is used as underlying hash function (which is fine) but beware that PBKDF2 may also use other hash functions. Rfc2898DeriveBytes utilizes SHA1 for the HMAC functionality.
Note that Rfc2898DeriveBytes utilizes UTF-8; this is not documented (even after multiple requests) by Mickeysoft. You can use byte arrays instead if you are unsure about the encoding on both platforms. You should especially be aware of this if you allow characters out of the US ASCII range.
Imagine I have this key (base 64):
Jdn1jJsD5hFrip4jzHODyA==
If I want to encrypt a string using AES 128 bit and the above key, what facilities does the .NET framework provide for this case?
I've tried searching on MSDN, but haven't found anything useful I could use.
Any guidance?
Your key appears to be encoded in Base64.
byte[] binKey = System.Convert.FromBase64String(textKey);
will give you a 16 byte key. All encryption classes use byte[] keys.