C# emulating php crypt - c#

I need to hash passwords with C# in a way that another software understands it .
Originally php's crypt function is doing that. It has the following output
$6$rounds=1000$1f$yeKGQo0b8MqqMpocFla8uKLE6GOpEygSQUH4qMi4msJZsD50Eh00bU4GwoGGPEeLMdG6C17ehl/l8SrcOABdC0
I guess it is SHA512. . How can I achieve php's crypt functionality with C#
original php
$salt = '$6$rounds=1000$'.dechex(rand(0,15)).dechex(rand(0,15)).'$';
$crypted = crypt($password, $salt);

CryptSharp computes this for all common crypt variations.
Generating a SHA-512 salted hash (the default number of rounds for this algorithm is 5000):
using CryptSharp;
string hash = Crypter.SHA512.Crypt(password);
Using a custom number of rounds:
var saltOptions = new CrypterOptions() { { CrypterOption.Rounds, 10000 } };
string salt = Crypter.SHA512.GenerateSalt(saltOptions);
string hash = Crypter.SHA512.Crypt(password, saltOptions);
Verifying a hash:
bool matched = Crypter.CheckPassword(testPassword, hash);
On another note, that original PHP should really be secured. The salt is only 8 bit and generated with rand (use openssl_random_pseudo_bytes instead). The hash specifically chooses a fifth of the default number of rounds.

Related

How to hash a string with a secret key and MD5 algorithm in C#?

I am working on an API which requires a header for API authentication. The header contains a hash string which is created using md5 algorithm and a secret key. I want to write a function like this:
public string CreateMD5Hash(string input, string secretKey)
{
return output;
}
I tried to use bouncy castle API. But I couldn't find proper documentation. That is why I couldn't make it use.
Okay BASICALLY let's break this into two simple concepts Cryptography and Hashing.
Cryptography
There are three fields secrete key a value and an encrypted value, and two methods.
Encrypt(value, secret-key) this method gets value and secret key and returns the encrypted value.
Decrypt(encrypted-value, secret-key) and this method gets encrypted value and secret key and returns the value.
like AES, DES, etc ...
Hashing
There are just two fields value and hashed value, and one method.
Hash(value) this method gets the value and returns the hashed value.
like MD5, SHA family, etc ...
Simple right!
So your question is not correct because MD5 is a hashing algorithm that usually is used for hashing passwords and comparing the hash of them.
Now I recommend taking a look at the API you are talking about for authentication.
You can share the link of documentation or an already encrypted header (if it is not sensitive data) to help you.
Update
According to the link, it is using HMAC with MD5.
This is the sample in the document, written in PHP.
$hash = hash_hmac('md5', $string, $key);
You can use this code for C#:
using System.Security.Cryptography;
using System.Text;
...
public string HashHmacMD5(string message, string secret)
{
Encoding encoding = Encoding.UTF8;
using (HMACMD5 hmac = new HMACMD5(encoding.GetBytes(secret)))
{
var msg = encoding.GetBytes(message);
var hash = hmac.ComputeHash(msg);
return BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
}
}
Your question is somewhat confusing.
For simple API authentication, you want to generate a secret that you want to share with the caller. This secret should be validated on your side.
Normally you would generate a random secure string with length about 50 - this is the secret. You share this secret with the caller - normally you warn the caller to securely store the value, since there is no way to recover it. Do not store this secret in the database.
You would generate a salt and hash the secret. You store both the salt and the hashed secret in the database. When the request comes in, you extract the value in the header, hash it with the salt and compare the result with the hashed secret in the database. User would be authenticated if they are match.
Is this more inline with your thinking? Is your question, how to hash it properly?

Digital Signatures: Encrypting the Hash vs Signing the Hash?

I'm trying to implement SHA256-RSA digital signatures and I'm confused with the terminology and implementation in C#.
AFAIK, "signing a file" is to generate the hash of a file, and then encrypt that hash. I've also heard the phrase "signing the hash". Is this the same thing? Or is this hashing a hash and then encrypting hash'?
Here's the code in question:
public void SignatureTest(byte[] data, X509Certificate2 cert)
{
var sha256 = new SHA256CryptoServiceProvider();
var rsa = (RSACryptoServiceProvider)cert.PrivateKey;
var hashOfData = sha256.ComputeHash(data);
var encryptedHash = rsa.Encrypt(hashOfData, false);
var encryptedHashOAEP = rsa.Encrypt(hashOfData, true);
var signedHash = rsa.SignHash(hashOfData, "SHA256");
//Shouldn't one of these be true?
var false1 = CompareAsBase64Str(encryptedHash, signedHash);
var false2 = CompareAsBase64Str(encryptedHashOAEP, signedHash);
//This is the one that actually matches
var true1 = CompareAsBase64Str(signedHash, rsa.SignData(data, sha256));
}
public bool CompareAsBase64Str(byte[] b1, byte[] b2)
{
return (Convert.ToBase64String(b1) == Convert.ToBase64String(b2));
}
Here's what MSDN says on RSACryptoServiceProvider:
SignHash() Computes the signature for the specified hash value by encrypting it with the private key.
Encrypt() Encrypts data with the RSA algorithm.
Shouldnt SignHash(hash) and Encrypt(hash) be the same?
You need to separate concerns, this will help you understand the terminology.
Any arbitrary blob of data can be hashed and/or encrypted in any combination.
Hash means: use a cryptographic algorithm to generate a value that is irreversible (that is, simply by knowing algorithm and hash you are unable to reconstitute original data) and consistent (that is, given the same data and algorithm, the value of the hash produced is always the same).
Encrypt means: use a cryptographic algorithm to encipher data (altogether or in blocks) with a given key (a key can be symmetric or asymmetric).
Sign means: Hash the data and Encrypt the hash with a given key. Then, given the pair (for asymmetric) or same (for symmetric) key, a consumer can validate that:
hash is matching, that means the data has not been altered in transit
hash did come from the source that at least has the pair key (for asymmetric) or same key (for symmetric)
The answer given by zaitsman is a good explanation of the topics related to your questions and I think should be the accepted answer, but just to help tie it back to your specific question of why encrypting the hash doesn't give you the same result as signing the hash (the rsa.SignHash(hashOfData, "SHA256") in your code):
Signing a hash doesn't just encrypt the hash data -- it also encrypts the name (or some identifier) of the hashing algorithm used to generate the hash along with it. Without that, the receiver wouldn't know what algorithm to use when computing their own hash (of the message being sent) to compare with the one they just decrypted in order to verify the authenticity of the message (which, of course, is the whole point).
When you encrypted the hash yourself (with rsa.Encrypt(hashOfData, false) and rsa.Encrypt(hashOfData, true)), you only encrypted the hash data and not the combination of hash data and algorithm identifier ("SHA256" in your code). In other words, you encrypted different data, so you got different (encrypted) results.
The reason the return value of that SignHash call does match the value returned by rsa.SignData(data, sha256) is that the latter method does the same thing, except it does the hashing and hash signing as one operation, so you don't have to compute the hash as a separate step if you don't need it for any purpose other than signing it.
From RSACryptoServiceProvider.SignData Method on MSDN:
Computes the hash value of the specified data and signs it.
Also see: Why does SignHash need to know what hash algorithm was used?

How to convert SSID with less than 8 characters to byte[] to be used as salt for Rfc2898 (WPA)?

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.

PHP and C# MD5 crypt does not give the same output?

This is the encryption I have when people register on my site:
$salt = generateSalt();
$hashedPassword = crypt($userPass, $salt);
and here is my generateSalt function:
function generateSalt() {
$salt = uniqid(mt_rand(), true);
$salt = '$1$' . $salt;
return $salt;
}
When I encrypt a password with this I get for example:
$1$92999442$AK4yZPjnj6BKc9yj4CXKu1
But when I crypt the same password on C# with this function:
hashedPassword = GenerateMD5(uName, salt);
GenerateMD5 function:
public String GenerateMD5(String input, String salt)
{
Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input + salt);
System.Security.Cryptography.MD5Cng md5hashstring = new System.Security.Cryptography.MD5Cng();
byte[] hash = md5hashstring.ComputeHash(bytes);
string hex = BitConverter.ToString(hash).Replace("-", string.Empty);
return hex;
}
I get a complete different output. With the same password and the same salt I get this output:
9DE11D48C3F7DF1BF89FC76D755A2596
What function should I use in PHP and C# to get the same output?
Because you're using two completely different algorithms. In PHP you're using crypt() which uses DES, and in C# you're using MD5. They're never going to produce the same output. If you want the same output, you should use md5() in PHP instead of crypt()
Also, don't use MD5, it's deprecated. You should be using at least SHA-2 now
http://php.net/md5
http://blogs.msdn.com/b/csharpfaq/archive/2006/10/09/how-do-i-calculate-a-md5-hash-from-a-string_3f00_.aspx
and adding a random salt to your input is part of them problem. you'll end up with a different input every time, hence a different hash output.
If I were you I'd consider using password_hash instead. Does all that crypt work for you in a nice, neat package, complete with random salt.
As to why your function doesn't match, you're using MD5 in your C# code. I'm no expert in C# but you should use some sort of bcrypt hashing system. There is an open source bcrypt for C# that might do the trick for you. In theory, since they use the same system, one should be able to validate the other since they all store the salt in the string. Just pluck the salt from the string and plug the password and salt into the other one and they should match.
This is so called md5crypt by Poul-Henning Kamp, not to be confused with MD5. Md5crypt for first used to protect FreeBSD passwords from bruteforce, but then became more widespread. It was incorporated into GNU libc crypt() and many programs had interfaces to this system call, including PHP, and some PHP developers made use of it. Md5crypt invokes MD5 no less than 1000 times to make brute-force harder (but nowadays md5crypt is considered outdated by its author!). I have seen implementation of md5crypt for many programming languages, this one is for C#.

Understanding how Rfc2898DeriveBytes works

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.

Categories