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.
Related
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 trying to convert SSID and passphrase to an encrypted WPA key, there is an implementation I found but not in .NET and fairly complicated. With some research I've found out the class Rfc2898DeriveBytes which is almost surely used to create WPA key. The problem is the salt used here is an array of byte while the SSID (I think this is the salt specifically used in WPA) is a string.
If the SSID's length is equal or greater than 8 characters, I can use UTF8.GetBytes method to convert it to byte[] and used as salt OK. The test showed that it produces the encrypted key as exactly as what the other implementation I found does.
var ssid = "mySSID";//this is less than 8 and can cause exception
//later on
var salt = Encoding.UTF8.GetBytes(ssid);
var wpaEncryptor = new Rfc2898DeriveBytes("myPassword", salt, 4096);
var bytes = wpaEncryptor.GetBytes(32);
But now if the SSID's length is less than 8 characters, I don't know how to convert it to byte[] (the salt requires the minimum length of 8). I think we have to perform some padding algorithm here but not sure how that could be done.
What I found is written in JavaScript, I can of course convert that to C# without much modification but I really would like to use the class Rfc2898DeriveBytes. If you're interested in the JavaScript implementation, you can look at the page source of this http://jorisvr.nl/wpapsk.html
The JavaScript code you pointed at contains the following snippet:
while (hash.length < 64) {
/* prepare 20-byte (5-word) output vector */
var u = [ 0, 0, 0, 0, 0 ];
/* prepare input vector for the first SHA1 update (salt + block number) */
i++;
var w = stringtowords(salt, i);
Now as you can see this means that the salt is used directly as an input of PBKDF2 (yes, I followed it from the SSID input variable). Unfortunately that means that you cannot directly use PBKDF2 for salts lower than 8 bytes; the salt is directly mixed into multiple iterations within PBKDF2.
Unfortunately the designer of Rfc2898DeriveBytes - the PBKDF2 API in .NET - considered it as part of his task to build in security constraints into the API. This is not so strange after what happened to their implementation of PBKDF1, but it is perhaps a bit over-zealous.
As an alternative you can use the Mono implementation of Rfc2898DeriveBytes and remove the constraint on the salt size (although, if I'm not mistaken, that constraint is only present on the setter). That should solve the problems at the location where they should be solved.
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'm writing an encryption sequence for sensitive data in our database.
Currently I'm taking a GUID based on the UserId, and putting that through a hash. Then, I run the hash through a Rfc2898DeriveBytes to get Key and IV which I use to encrypt the data using the Rijndael function.
My code looks like this:
var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
const int iterations = 1000;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(GenerateHash("2525"), salt, iterations)) {
_key = rfc2898DeriveBytes.GetBytes(32);
_iv = rfc2898DeriveBytes.GetBytes(16);
}
I then pass the _key and _iv along to decrypt or encrypt the data.
My goal is to have each user always have access to their unique key through every session. That being said, what can be randomized and still maintain this function? Do I always have to use the same salt and the same IV to get the data I want?
Rfc2898DeriveBytes is an implementation of PBKDF2. Obviously RFC 2898 is a reference to the standard where this Password Based Key Derivation Function has been defined. Note that the standard is broader than just the KDF; it's full title is "PKCS #5: Password-Based Cryptography Specification, Version 2.0".
PBKDF2 is a successor of PKCS#5 v1 which defined PBKDF / PBKDF1. The 1 was only added after PBKDF2 came into being. The class PasswordDeriveBytes is an implementation of PBKDF1. It should not be used anymore because both the KDF is outdated but also because Microsoft screwed up the implementation severely; it may repeat output keying material if more than the output of the underlying hash - SHA-1 so 20 bytes - is requested.
Besides being used as KDF, PBKDF2 can also be used as password hashing function, where the hash instead of the password is stored in a database. That way passwords can be verified, while the password cannot easily be retrieved even if the hash data is retrieved by an adversary. This is described in the followup RFC 8018 which contains the 2.1 version of the protocol.
Internally, PBKDF2 is just a repetition of a hash function over the password and salt. The iteration count is the work factor; it specifies how much work you (and adversaries) have to do before one hash is calculated. The salt makes sure that rainbow table attacks are impossible, and that identical passwords (of different users) don't lead to the same hash.
Due to a design error which requires the full amount of work to be repeated if more than one hash output is required, it is not recommended to request more data from it than the output of the hash function. In that case it is better to use another method to expand the output keying material (bytes), e.g. HKDF-Expand.
Observations on the code in the question:
The GenerateHash method is spurious, Rfc2898DeriveBytes will do this for you;
You should use something less predictable than a UID to create a key; the data should not be directly available to an attacker as this would completely defeat the purpose of PBKDF2;
If you want to use the same set of UID + salt + iterations for multiple encryption operations, then you should generate a random IV and prepend it to the ciphertext, having a non-random IV completely defeats the purpose of the IV;
You can change the salt to get multiple keys, but you would have to go through the PBKDF2 function for each and every encryption.
Just a general hint, only use the resulting key to encrypt data specific keys created out of a secure random function. Then you don't even need to bother about an IV, and you may be able to "re-encrypt" by decrypting the data specific key, and encrypting that with a new key.
I want to use encryption algorithm available in .Net Security namespace, however I am trying to understand how to generate the key, for example AES algorithm needs 256 bits, that 16 bytes key, and some initialization vector, which is also few bytes.
Can I use any combination of values in my Key and IV? e.g. all zeros in Key and IV are valid or not? I know the detail of algorithm which does lots of xors, so zero wont serve any good, but are there any restrictions by these algorithms?
Or Do I have to generate the key using some program and save it permanently somewhere?
I want to store data in database after encryption, the secure profile data like username, password, phone number etc, and the key will be available to database user mentioned in connection string only, and to the administrator.
You really ought to do this the correct way :)
1) Use a securely generated random IV
2) Use a securely generated random key
3) Don't use ECB mode - EVER
AesManaged aes = new AesManaged();
aes.GenerateKey();
aes.GenerateIV();
The code above will correctly and securely generate a random IV and random key for you.
Sounds like you need to read into the Rfc2898DeriveBytes class.
Rfc2898DeriveBytes.GetBytes();
It has a method(above) that allows you to tailor the size of byte arrays that are fed into the .Key and .IV properties on a symmetric encryption algorithm, simply by feeding an int value. The MS official 70-536 book suggests doing this pro-grammatically by dividing the KeySize property / 8.
I.e TripleDes or AESManaged. Whatever you use, the algorithm itself will have some pre-reqs that will need meeting first. I.e satisfying the key size conditions. The RunTime will automatically fill the properties and fields and etc the best and most strongest values for you. But the IV and Key needs to come from you. This how you can do the following:
RijndaelManaged myAlg = new RiRijndaelManaged();
byte[] salt = Encoding.ASCII.GetBytes("Some salt value");
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes("some password", salt);
myAlg.Key = key.GetBytes( myAlg.KeySize / 8);
myAlg.IV = key.GetBytes( myAlg.BlockSize / 8);
// myAld should now fully set-up.
Above you can see what I mean by doing it pro-grammatically, as it should pretty much
do it all for you, without you even really having to bat an eye-lid as to meeting it's pre-reqs.
The Microsoft 70-536 book states that the .Key properties expect the byte arrays you supply
to them in bytes and not bits. The RFC class works in bytes where as an algorithms KeySize property works in bits. 1 byte = 8 bits. Can you see where this is going ... ?
This should give you an idea as to why the above sample peice of code is done the way it is! I studied it and it makes pretty darn good sense to me!
The above answer should allow you to create your algorithm object with supplied password and a static salt value that can be hard code at both ends. Only thing you need to do is worry about how you going to make sure that the byte arrays stored at .Key and .IV are safely transported to a recipient so that can successfully decrypt the message you encrypted. By safely reconstructing the same algorithm object.
OBTW:
AESManaged has a keysize req': 128Bits = 16 Bytes !!!
(8*8 = 64, 64Bit / 8bits per Byte = 8 Bytes) Therefore
64*2 = 128Bit, 8*2, ==> 16bytes key size !
256Bit = 32Bytes !!!!
According to the 70-536 official training kit book, Aes is limited to having keysize of 128bits in size. 256bits,192 and 128 key size for example can be used with the Rijndael class.
You could on the other hand completely forget all that crap and simply use .GenerateKey and GenerateIV methods instead to save you all the hassle of sorting out a pre-shared and agreed password and static salt values. Your only concern is figuring out a way of storing and retrieving the key and IV byte arrays. Binary Formatter? .
If you are using encryption to exchange data then you will need a key exchange protocol, but you don't make one yourself instead use one off-the-shelf like TLS or SSL.
If you use encryption to store data then you generate the IV using CryptGenRandom (or its .net equivalent RandomNumberGenerator.GetBytes) and save it along the document (in clear, no need to protect the IV). You never write down the key, the key is provided by the user. Usualy you derive the key from a password phrase using CryptDeriveKey, or its .Net equivalent PasswordDeriveKey.CryptDeriveKey.
Update
To store a secret in the database that is available only to the user and an administrator you need to use 3 keys:
one to encrypt the data with (call it the DK key)
one user key to encrypt the DK key (call it UK)
one administrator key to encrypt the DK key (call it AK)
In theory you encrypt the data with DK and then encrypt the DK with UK and save it, and encrypt the DK with AK and save it. This way the user can use again the UK to decrypt the DK and then decrypt the data, and the administrator can use the AK to decrypt the DK and then decrypt the data. The big problem is the fact that the system is always automated, so the system needs access to the administrator's key which means is not truly a persnal key of the administrator, but instead is a system key (it cannot be used for purposes of non-repudiation for instance).
As a heads up, knowledge of what IV is or how to use AES from C# and how cryptography algorithm work will get you exactly 0 (zero) traction in solving this kind of problems. The issue is never what IV and key to use, the issue is always key provisioning. For actual crypto operations, just use the built-in support from the database, see Cryptography in SQL Server. I can easily argue that the only facility you need is TDE (Transparent Data Encryption) to protect against accidental loss of media.
Generate a random letters / hex code in a specific length.
This function (taken from here) return a random key in a specific length:
private static string CreateSalt(int size)
{
//Generate a cryptographic random number.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
// Return a Base64 string representation of the random number.
return Convert.ToBase64String(buff);
}
Use System.Security.Cryptography.RandomNumberGenerator to generate random bytes:
var rnd = new System.Security.Cryptography.RandomNumberGenerator.Create();
var key = new byte[50];
rnd.GetBytes(key);
It really depends on what you ned to do with the key.
If the key is to be generated by the computer (and can be any random value) I generally take a SHA256 of a couple GUIDs. This is about as random as you're going to get without a hardware random number generator.
You can use keys with all 0s but obviously it won't be very secure.