pbkdf2 computation not consistent between C# and JavaScript - c#

Hi my question is I've Encrypted a password with crypto.pbkdf2 on windows azure server side Javascript I'm pretty sure that there is a public library which you can look up. The problem is I'm trying to encrypt the same password in C# on my system because I want the credentials to be universal but despite using Rfc2898DeriveBytes and the salt generated the first time I'm not able to get back to the same hashed password.
Thank you for your help :)
function hash(text, salt, callback) {
crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){
if (err) { callback(err); }
else {
var h = new Buffer(derivedKey).toString('base64');
callback(null, h);
}
});
}
And the C# code:
byte[] salt = Convert.FromBase64String(user.salt);
using (var deriveBytes = new System.Security.Cryptography.Rfc2898DeriveBytes(password, salt, 1000))
{
byte[] newKey = deriveBytes.GetBytes(32);
// user is the user object drawn from the database in existence
if (Convert.ToBase64String(newKey).Equals(user.password))
{
FormsAuthentication.RedirectFromLoginPage(Request.Form["username"], false);
}
}
hex generated by C# = 3lRSQF5ImYlQg20CGFy2iGUpWfdP5TD0eq2cTHhLono=
hex generated by JS = w4PDh8K6YMKGwr3DgcObRsOsFFUgDMOJw5PCnkdAwrTCgcOOV8OCKMKFdcKRwrLCqMK2VA==
Salt generated by JS and used at both = /Ij0hgDsvAC1DevM7xkdGUVlozdCxXVd0lgfK2xEh2A=
All the above info is in base64 format
Another thing that might be useful
item.salt = new Buffer(crypto.randomBytes(bytes)).toString('base64'); crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){
which means the JS function accepts a string

I want the credentials to be universal but despite using Rfc2898DeriveBytes and the salt generated the first time I'm not able to get back to the same hashed password.
The obvious stuff is hash algorithm, salt, and iteration count. Can you confirm (for both languages):
the hash algorithm
the salt
the iteration count
The non-obvious is the encoding of the password and possibly salt. I included the salt because its often stored as a string.
To keep it portable among languages, you should use UTF-8. That's because you could encounter a default encoding, a UTF16-BE, UTF16-LE or any number of other encoding.
In C#, the setup would be:
byte[] utf8_salt = Encoding.UTF8.GetBytes(salt);
byte[] utf8_pass = Encoding.UTF8.GetBytes(password);
You would then pass utf8_salt and utf8_pass to the PBKDF2 function.
I don't know how to do the same in Javascript.

Alright kids daddy has figured out the answer.. took long enough..
Buffer(encodedPassword, 'binary').toString('base64')
on Javascript side will suffice now the tutorial I looked at was clearly not accurate.. the 'binary' was missing.
Thank you all for the help :)
and happy new year

Related

Good practice web authentication with PBKDF2 and nonce in .NET

I'm building a web application and need to authenticate a user with a user password. I'm trying to build it to what would be considered a good security practice in 2021. As far as I've been able to gather from what I've read online, the following would be expected from sending the password from the client to the server over HTTPS (only).
[Edit: Context about the server]
On the server side I intend to store a salt per user and a hashed version of their password. On the wire I obviously shouldn't send the clear text password, but also, to prevent playbacks, I shouldn't send the hashed password value either. Hence the client side algorithm below.
[End edit]
User's password is hashed on the client [Edit: with the same salt as used server side].
Nonce is generated on the client [Edit: This should be server generated and given to the client, see comment]
The hashed password plus nonce is hashed on the client.
The nonce and final hash is sent from the client to the server over HTTPS.
Be sure to cleanup the password on the client (not in my code example).
Here is my experimental sample code:
public const int HASH_SIZE = 24; // size in bytes
public const int ITERATIONS = 100000; // number of pbkdf2 iterations
public const int NONCE_SIZE = 8; // size in bytes
public static string PasswordFlow(string userPassword, byte[] userSalt)
{
// Hash the user password + user salt
var hpwd = KeyDerivation.Pbkdf2(userPassword, userSalt, KeyDerivationPrf.HMACSHA512, ITERATIONS, HASH_SIZE);
// Generate an 8 byte nonce using RNGCryptoServiceProvider
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] nonce = new byte[NONCE_SIZE];
rng.GetBytes(nonce);
// Hash the hpwd byte[] converted to Base64 with the nonce byte array as salt
var final = KeyDerivation.Pbkdf2(Convert.ToBase64String(hpwd), nonce, KeyDerivationPrf.HMACSHA512, ITERATIONS, HASH_SIZE);
return Convert.ToBase64String(nonce)+"$"+ Convert.ToBase64String(final);
}
I would appreciate thoughts on the process above. Did I misunderstand it, screw it up or miss anything? I'm also trying to understand:
Is it OK to use PBKDF2 twice?
Is 100,000 iterations a reasonable size for PBKDF iterations?
Is 24 bytes a reasonable hash size for PBKDF2?
I presume 8 bytes a reasonable size for the nonce (64-bit number)?
Is it a problem running PBKDF2 on the hash in base64 and nonce? (it expects a string input).
I'm not a security expert and I'm also a C# noob so please excuse any blunders.
PBKDF2 is designed to reduce brute-force attacks by increasing computational cost.
It is not intended to resolve problem of sending plaintext password - this should be done by other security mechanism - secure communication (i.e. TLS 1.3).
If secure communication is broken, then it does not matter if you have sent plaintext or hash of the password.
What you are referring as NONCE should be called SALT.
Basically, PBKFD2:
Takes any data you send (i.e. password)
Adds SALT
Applies PRF (Pseudo-Random Function) number of times
Returns n-bits of derived password
So, answering your questions:
It is ok to run PBKDF2 twice, however I would increase number of iterations, rather than run it twice
100,000 is reasonable number of iterations
24 bytes (192 bits) is reasonable hash size. Although you are using HMACSHA512 as PFR which produces hash of size 512 bits.
PBKDF2 standard allows 8 bytes SALT, however NIST recommends min. 16 bytes - I would increase SALT size
As mentioned earlier, you can run PBKDF2 on any string input. In most cases it would be password or passphrase

Rijndael Cryptography Function - Key/IV/Salt Setup

I'm working on a simple utility to encrypt files as a learning experience.
Everything seems to be working fine, but I'm wondering if I have setup the Key/IV/Salt data securely.
I have noticed that when it comes to cryptography most people seem to envision a working environment that is loaded with malware manned by a wizard remotely ready to dive through the memory of a running application/page file to get at these secure files.
Let's pretend that you're on a clean machine and you encrypt some files and turn off your computer.
What I want to know is whether or not someone can take your hard drive and retrieve the contents of the files using the code I have proposed.
The attack vector I am most concerned with is ensuring that the page files/file caches are inaccessible.
I also want to make sure that the Key/IV system used is not going to make a rainbow table/hash based attack feasible.
Entering the Password:
The password is entered using a text box with the passwordchar value set to true.
I'm not really concerned with the string being in memory as long as it is properly removed after the encryption. I read that using SecureString is kind of pointless at this point because if you have malware on your computer already, you could just as easily have a keylogger on there which renders everything else useless.
private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";
I salt the password using a hard coded 32 character string.
(The above string is just an example.)
To get at it, it will require someone to decompile/view the .exe file itself with a hex editor (something that I know if very easy to do, but an extra step nonetheless).
I have considered making this salt editable, but I'm not sure how I could securely store it. I think it's a little ridiculous to encrypt your salt because then you will have the same issue etc, so just leaving it as a hard coded string inside the exe itself seems to make the most sense to me.
The way this works is if you decide to make your password "thepassword", it is actually saved as "thepasswordfaDADFexA8932F7Dz3J3X".
The main key here is that you always have a 32 character password, regardless of what you enter.
The Key and IV:
The Key and IV are also salted as follows.
This is what I wanted to get some input on, because to be honest I'm not entirely sure what it's doing:
UnicodeEncoding UE = new UnicodeEncoding();
byte[] keysalt = UE.GetBytes("Xjafe231x42X423XadXCadfkhjeAdS"); //Another string of random characters hard coded in the exe
byte[] IVSodium = UE.GetBytes("83Xkda7l78Dkx85KdJazppoqq6SaxDs"); //Another string of random characters hard coded in the exe
byte[] key = new Rfc2898DeriveBytes(password, keysalt).GetBytes(32); //Derive the key using the password and salt
byte[] IV = new Rfc2898DeriveBytes(password, IVSodium).GetBytes(16); //Derive the IV using the password and salt
My main concern here is that the IV is based on the key. Again, I'm not sure if this will cause any issues and I was hoping you guys could let me know if there are issues, what they are.
Also, is this another scenario where hard coding the salt is a bad practice? Should this be stored in the encrypted file, and if so, does it really make it more secure? Should I make this editable as well?
The crypto streams are setup using the using keyword:
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
byte[] buffer = new byte[4096]; //4096 is kind of arbitrary - better idea?
int data;
long bytesRead = 0;
while((data = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
bytesRead += data;
/////////////////////////////////////////
// Handle Aborts and Update Progress Bar
/////////////////////////////////////////
if (!caller.isClosing)
caller.Invoke((MethodInvoker)delegate {
caller.fileProgressBar.Value = ((int)(((double)bytesRead / totalBytes) * 100));
});
else
return false; //Encryption Aborted
/////////////////////////////////////////
cs.Write(buffer, 0, data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
return true;
}
}
}
}
}
Thanks for your time and please let me know if there is a better way to setup the Key/IV/Salt.
I think that it is most likely secure enough as long as there is not a mathematical issue with the IV and Key containing similar characters. If so, should I use a hard coded IV as well? That seems weird.
Note that I'm not saving a hash of the password or anything like that. The password is not saved anywhere. It is just used to generate the Key and the IV.
Thanks for your time.
Edit: Here are the changes recommended for anyone looking in the future.
Note that this is not using a pepper - just a random salt, although it would be easy enough to add
byte[] salt = new byte[32]; //Create a 32 byte salt
rand.NextBytes(salt); //Fill it with random values (use RandomNumberGenerator rand = new RNGCryptoServiceProvider(); to be safe
byte[] IV = new byte[16]; //Create a 16 byte IV
rand.NextBytes(IV); //Fill it with random numbers
byte[] key = new Rfc2898DeriveBytes(password, salt).GetBytes(32); //Derive our Key by mixing our password with the salt
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
fsCrypt.Write(salt, 0, salt.Length); //Write our salt to the file
fsCrypt.Write(IV, 0, IV.Length); //Write our IV to the file
fsIn.CopyTo(cs); //Encrypt and Write
}
}
}
}
The salt is used for two purposes:
to prevent rainbow table attacks (and it does if applied correctly);
to prevent identical passwords to generate the same password hash.
To do this the salt needs to be 8 to 16 bytes (not characters) of random data, stored with the password hash. Using a static hash as you do defeats both purposes of the hash.
If you need strings, use base 64 to encode the salt and password hash. If you want you can add static data (sometimes called "pepper") to the salt before calling the password hash function. This may add some security if the program data cannot be easily read by an attacker.
You should never directly mix the salt and the password yourself; the Rfc2898DeriveBytes (which is an implementation of PBKDF2) already mixes the two. You should also never store the password, nor should you have to append any data to it. PBKDF2 can handle any size of input, so it doesn't add any functionality.
Now the IV can be taken from the PBKDF2 function (using GetBytes). There is however a problem, it's likely that this will double the initial amount of iterations of PBKDF2 function, which costs CPU time and reduces the advantage over an attacker. It's probably better to just generate a random IV and prefix it to the ciphertext.
So in the end you should store salt | IV | ciphertext, then use salt | pepper as salt and calculate your key, then encrypt/decrypt using the calculated key and IV.
As far as I know,
IV/Salt does not need to be private. It can be stored in plain-text on the hard drive. In fact, salt must be in plain-text, otherwise you cannot generate the same output with it.
It is not a good idea to use your key information for generating the IV as it may leak your key information.
There is no way you can prevent attacks like rainbow. But with salt, rainbow attack becomes expensive as the talbes only work with this salt value.
There is a standard of key derivation fucntion that may useful to you (http://en.wikipedia.org/wiki/PBKDF2).

Error in C# encrypt code when decrypting!

A bit more background info as suggested:
I'm finsihing of an Intranet CMS web app where I have to use the products API (ASP.NET based). Because of time constraints and issues with Windows authen' I need another way to ensure staff do not need to re login everytime they visit the site to view personalised content. The way it works is that once a user logs in (username/password), a Session ID storing a new different Security context value is generated that is used to display the personalised content. The API login method called uses the username and password as parameters. The only way I can think of automatically logging in the next time the staff visits the site is by storing the password in a enrypted cookie and checking of its existing when the site is visited and then calling the API login method using the username and decrypted password cookie values.
Any other ideas as an alternative welcomed.
Mo
Hi,
I'm using some code found on the web to encrypt and decrypt a password string. It encrypts fine but when it calls the code below to decrypt the string it throws the error "Length of the data to decrypt is invalid" How can I resolve this?
Thanks in advance.
Mo
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] myByteArray = enc.GetBytes(_pword);
SymmetricAlgorithm sa = DES.Create();
MemoryStream msDecrypt = new MemoryStream(myByteArray);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(), CryptoStreamMode.Read);
byte[] decryptedTextBytes = new Byte[myByteArray.Length];
csDecrypt.Read(decryptedTextBytes, 0, myByteArray.Length);
csDecrypt.Close();
msDecrypt.Close();
string decryptedTextString = (new UnicodeEncoding()).GetString(decryptedTextBytes);
A couple of things here...
You shouldn't encrypt passwords usually. You should hash them.
If you decide to continue down the road of encryption..
You are using the DES algorithm. This is considered insecure and flawed. I'd recommend looking at the AES algorithm.
Depending on how much data you are working with, the CryptoStream might be overkill.
Using the ASCII encoding can cause loss of data that isn't ASCII, like Cyrillic letters. The recommended fix is to use something else, like UTF8.
Here is an example:
string text = "Hello";
using (var aes = new AesManaged())
{
var bytes = System.Text.Encoding.UTF8.GetBytes(text);
byte[] encryptedBytes;
using (var encrypt = aes.CreateEncryptor())
{
encryptedBytes = encrypt.TransformFinalBlock(bytes, 0, bytes.Length);
}
byte[] decryptedBytes;
using (var decrypt = aes.CreateDecryptor())
{
decryptedBytes = decrypt.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
}
var decryptedText = System.Text.Encoding.UTF8.GetString(decryptedBytes);
Console.Out.WriteLine("decryptedText = {0}", decryptedText);
}
This will use a random key every time. It is likely that you will need to encrypt some data, then decrypt it at a later time. When you create the AesManaged object, you can store the Key and IV property. You can re-use the same Key if you'd like, but different data should always be encrypted with a different IV (Initialization Vector). Where you store that key, is up to you. That's why hashing might be a better alternative: there is no key, and no need to worry about storing the key safely.
If you want to go down the hashing route, here is a small example:
var textToHash = "hello";
using (SHA1 sha = new SHA1Managed())
{
var bytesToHash = System.Text.Encoding.UTF8.GetBytes(textToHash);
var hash = sha.ComputeHash(bytesToHash);
string base64hash = Convert.ToBase64String(hash);
}
This uses the SHA1 algorithm, which should work fine for passwords, however you may want to consider SHA256.
The concept is simple: a hash will produce a (mostly) unique output for an input, however the output cannot be converted back to the input - it's destructive. Whenever you want to check if a user should be authenticated, check hash the password they gave you, and check it against the hash of the correct password. That way you aren't storing anything sensitive.
I've actually had this error before and it took me 3 days to figure out the solution. The issue will be the fact that the machine key you need for descryption needs to be registered on your machine itself.
Read fully up on DES encryption, it works by an application key, and a machine-level key. The error you're getting is likely because of the machine key missing.
Compare the bytes used to create the _pword string (in the encryption method) to the bytes retrieved with GetBytes. Probably you will notice a change in the data there.
To store the encrypted bytes, I think you should use Convert.ToBase64String and Convert.FromBase64String turn the encrypted password to/from a string.
I also do not see the code where you set the Key and IV. So I guess you are using a different key to encrypt and decrypt the password.
If the current Key property is null,
the GenerateKey method is called to
create a new random Key. If the
current IV property is null, the
GenerateIV method is called to create
a new random IV.
DES is a block based cipher - only certain lengths of buffers are valid. If I remember correctly, the block size for DES is 64 bits, so you need to ensure that your byte array is a multiple of 8 bytes long.
(That should fix your immediate problem, but I'd reference other peoples advice here - you really ought not to be using DES for any new code, and for passwords it's usually more appropriate to hash than to encrypt).

Import MD5 Passwords from a PHP app to ASP.NET

So I'm in the process of writing an ASP.NET application that requires to import users from a PHP application. The passwords were hashed using MD5 into the database, so the user table records looks more or less like this:
user Password
user1 827ccb0eea8a706c4c34a16891f84e7b
user2 e10adc3949ba59abbe56e057f20f883e
And so on. I have access to the PHP source code and I can see that there is no salt or anything else, it is juts a straight MD5 application. Now, Back on my ASP.NET, I tried to use MD5 using the following logic:
public static string HashPassword(string Password)
{
//Declarations
Byte[] originalBytes;
Byte[] encodedBytes;
MD5 md5;
originalBytes = ASCIIEncoding.Default.GetBytes(Password);
md5 = new MD5CryptoServiceProvider();
encodedBytes = md5.ComputeHash(originalBytes);
return BitConverter.ToString(encodedBytes);
}
Problem is, that is returning strings like 50-F8-4D-AF-3A-6D-FD-6A-9F-20-C9-F8-EF-42-89-42, which of course is not going to match with the information in the database. What should I do so I don't have to reset 500+ user passwords?
Return this instead:
return Encoding.UTF8.GetString(encodedBytes).ToLowerInvariant();
BitConverter explicitly inserts dashes in the string value.
Or of course you could keep using BitConverter and just do .Replace("-","").ToLower()

C#, how to check if value is encrypted using MD5 passphrase?

I have the following code to encrypt a value (listed below). Now I would like to write a bool isEncrypted() method. Is there a fool proof and reliable way to check if a value has been encrypted using this function. I have the decrypt routine and can control the pass phrase, but not sure if that will help.
The reason is - when the app first runs, values in a configuration file are not encrypted, in this case the app should auto encrypt these values. On 2nd run I don't want to encrypt again because obviously that would cause havoc. Lastly I don't want to have to add an isEncrypted attribute to the config value. I want it to work and look as dynamic as possible.
So far I am leaning towards using the len (128) as deciding factor, but there is always a remote chance of the unencrypted value also being this length.
Thanks in advance.
public static string encrypt(string text)
{
// Locals
var passphrase = "5ab394ed-3920-4932-8d70-9c1b08f4ba4e";
byte[] results;
var utf8 = new UTF8Encoding();
// Step 1. We hash the passphrase using MD5
// We use the MD5 hash generator as the result is a 128 bit byte array
// which is a valid length for the TripleDES encoder we use below
var hashProvider = new MD5CryptoServiceProvider();
var tdesKey = hashProvider.ComputeHash(utf8.GetBytes(passphrase));
// Step 2. Create a new TripleDESCryptoServiceProvider object
// Step 3. Setup the encoder
var tdesAlgorithm = new TripleDESCryptoServiceProvider
{
Key = tdesKey,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
};
// Step 4. Convert the input string to a byte[]
var dataToEncrypt = utf8.GetBytes(text);
// Step 5. Attempt to encrypt the string
try
{
var encryptor = tdesAlgorithm.CreateEncryptor();
results = encryptor.TransformFinalBlock(dataToEncrypt, 0, dataToEncrypt.Length);
}
finally
{
// Clear the TripleDes and Hashprovider services of any sensitive information
tdesAlgorithm.Clear();
hashProvider.Clear();
}
// Step 6. Return the encrypted string as a base64 encoded string
return Convert.ToBase64String(results);
}
What you could do in the isEncrypted method is to try to decrypt the message.
Since you are using PKCS7 padding most likely an unencrypted message will fail to decrypt since the padding does not conform to the set padding mode.
The decryption will throw an exception and you'll have to catch this and return false in this case.
There is a remote chance that the decryption will go through (when the message is not encrypted) if the data conforms to the padding mode. This is however most unlikely.
What I would do in this case would be to add some kind of flag in the encrypted data or append some data to encrypted message since I can then remove it in the decryption. This would be the most foolproof way.
First, as a serious issue, it's an exceedingly poor idea to use cryptographic primitives on your own. You've chosen to use the Electronic Codebook mode of encryption, which has the property that identical plaintext blocks produce identical cyphertext blocks. Check out the example at Wikipedia.
That said, a simple solution is to prepend a token such as 'ENC:' to the encrypted password. If you need to worry about malicious tampering with the config file, you should proceed to use a message authentication code, such as HMAC.
As your function returns a string there's no reason you can't add a plaintext code to the beginning of the encrypted data that the IsEncrypted function can look for, say "MD5ENC"+ [ciphertext].
The disadvantage of this is that it will let anyone who has the raw string know what algorithm was used for encryption. But as we keep getting reminded security through obscurity is no security at all. Anyone should be allowed to know how something was encrypted and have no easy way of breaking that encryption.
Note my use of the word should.
Anyhow, to return to my original suggestion. The advantage of this is that the longer your introductory code on the string the more vanishingly tiny the chances of it being generated by accident in another unrelated Base64 encrypted string becomes.
Should the ciphertext need decrypting just snip off your standard length encryption ident code and away you go...

Categories