I want to verify the user credentials (name and password) for Linux users with native C# code (.NET Core). The user credentials on Linux systems are normally stored in the file: /etc/shadow.
For example the user called "csharp" with the password "1337" has the following entry inside the file /etc/shadow:
string entry = "csharp:$6$qVnvjWpk$DOUTmbC9ROZ6s1h0hCZTYWLFfVeUWDbz8f0EUFPEJEC2UKQV0gRiIfoGpnaA.8i4RCcrGpOEHEd9xrAUDpo3Y/:18337:0:99999:7:::"
The string before the first ":" is the name of the user => entry.Split(":")[0] (csharp)
The char after the first "$" is the hash method => entry.Split("$")[1] (6 = SHA256). Possible methods are: MD5 => 1; SHA256 => 5; SHA512 => 6
The string after the second "$" is the salt (or hash?) => entry.Split("$")[2] (qVnvjWpk)
When using the program openssl on the Linux system with "openssl passwd -6 -salt qVnvjWpk 1337", then it will give me the following output:
$6$qVnvjWpk$DOUTmbC9ROZ6s1h0hCZTYWLFfVeUWDbz8f0EUFPEJEC2UKQV0gRiIfoGpnaA.8i4RCcrGpOEHEd9xrAUDpo3Y/
As you can see, the openssl output is part of the /etc/shadow entry:
entry.Split(":")[1] == openssl_output
But how could this be done using native csharp code and without using the program openssl?
I have the following approach for doing this.
CheckCredentials("csharp", "1337");
public static bool CheckCredentials(string username, string password) {
string[] shadow = File.ReadAllLines("/etc/shadow");
foreach(string entry in shadow) {
// entry = csharp:$6$qVnvjWpk$DOUTmbC9ROZ6s1h0hCZTYWLFfVeUWDbz8f0EUFPEJEC2UKQV0gRiIfoGpnaA.8i4RCcrGpOEHEd9xrAUDpo3Y/:18337:0:99999:7:::
if(entry.Split(":")[0] == username) {
// hash = 6f0ac65fe01188660aad900bfe16c566ebf0e56c0a7d4a15bd831049108de80bd3a2fbf1a8b91662433a40458ec208a207cab073f190bd65b889e95e4fca8e09
// $6 => SHA512
string hash = HashSHA512(password);
// salt = qVnvjWpk
string salt = entry.Split("$")[2];
// >>> how to check now the given password "1337"? <<<
break;
}
throw new Exception($"User '{username}' was not found");
}
return false;
}
public static string HashSHA512(string input)
{
SHA512Managed crypt = new SHA512Managed();
StringBuilder hash = new StringBuilder();
byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(input));
foreach (byte append in crypto)
{
hash.Append(append.ToString("x2"));
}
return hash.ToString();
}