How to compare encrypted password in sql - c#

I already got my password encrypted and store it in database but now I want to compare the encrypted value to the password that a user type upon loading a page. Consider this code:
string userName = txtusername.Text;
string password = txtpassword.Text;
Encryptor en = new Encryptor(EncryptionAlgorithm.Rc2, CreateRandomPassword(7));
password = en.Encrypt(password);
DataTable dt = uMManager.ValidateUser(userName, password);
CreateRandomPassword Method
private static string CreateRandomPassword(int passwordLength)
{
string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!#$?_-";
char[] chars = new char[passwordLength];
Random rd = new Random();
for (int i = 0; i < passwordLength; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
Encryptor Class
public class Encryptor
{
EncryptEngine engin;
public byte[] IV;
public Encryptor(EncryptionAlgorithm algID, string key)
{
engin = new EncryptEngine(algID, key);
}
public EncryptEngine EncryptEngine
{
get
{
return engin;
}
set
{
engin = value;
}
}
public string Encrypt(string MainString)
{
MemoryStream memory = new MemoryStream();
CryptoStream stream = new CryptoStream(memory, engin.GetCryptTransform(), CryptoStreamMode.Write);
StreamWriter streamwriter = new StreamWriter(stream);
streamwriter.WriteLine(MainString);
streamwriter.Close();
stream.Close();
IV = engin.Vector;
byte[] buffer = memory.ToArray();
memory.Close();
return Convert.ToBase64String(buffer);
}
}
I made a local method to generate random string for RC2 encryption. EncryptionAlgorithm is a Enums for the types of encryption.
Now how can I compare 'password' to the password field in my database to check if the credential is correct

You can't check if the credential is correct, since you've encrypted it with a key you've thrown away. If you store the key along with the password, the encryption serves no purpose. If you don't, you can't verify.
Instead of trying to create a new way to store passwords, why not use one of the ways that's known to work?

Don't encrypt passwords. Hash them. Encryption allows for retrieval of the plaintext password, which is a Bad Thing. Hashing still allows you to check if what the user inputs matches with what he did before.

Here is the flow of the program:
When user register new account -> You encrypt his password -> Save it in database
When user login -> Encrypt input password -> Get user with password in database -> If user not null -> Login successful -> Else -> Login fail

it looks like you are using every time a random key to encrypt your password
so if
u encrypt "test" the first time and then u encrypt "test" a
second time. the result of the two encryption is not the same.
u should simply use a hash algorithm

Related

Is this property Password's insecure string cached in memory

If I do the following in a class, is my password cached/discoverable in memory?
public class ConnectionInfo
{
private SecureString _password;
public string UserName;
public string Password
{
get
{
IntPtr valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(_password);
return Marshal.PtrToStringUni(valuePtr);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
set
{
_password = new SecureString();
foreach (char c in value)
{
_password.AppendChar(c);
}
}
}
}
In other words, if I use it like this
ConnectionInfo connectionInfo = new Models.DomainInfo();
connectionInfo.Password = "Password1";
and later use it with a directoryEntry
DirectoryEntry entry = new DirectoryEntry("LDAP://Domain.com", $"Domain\\{connectionInfo.UserName}", connectionInfo.Password);
is the cleartext password cached via the property Password? (I am not referring to any leaks that might occur via DirectoryEntry etc., only the Property)
Password is stored in web/app.config and retrieved via this
staticKey = Encoding.UTF8.GetBytes(sEncryptionKey.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
inputByteArray = Convert.FromBase64String(stringToDecrypt);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(staticKey, staticIV), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
Encoding encoding = Encoding.UTF8;
return encoding.GetString(ms.ToArray());
Is the encryption strong enough?
The answer is complicated - yes, the Property itself is secure, there is no caching done.
BUT - the string returned will be managed by the Garbage Collector and exist until garbage collected.
I honestly do not thing SecureString is all that worthwile. It somewhat protects against analysing a memory dump, but it only shortens the threat window. Since input und usage are usually plain old strings, the password WILL show up in the memory dump sooner or later.
Also, how do you get the password to the application? That's usually the part where an attacker can get the PW.

Is the KeyStore encrypted by default?

I currently have some code that stores my sensitive information in the KeyStore like so:
static readonly char[] Password = null;
//Create KeyStore
ks = KeyStore.GetInstance(KeyStore.DefaultType);
prot = new KeyStore.PasswordProtection(Password);
//AddUserName
var alias = MakeAlias("UserName", serviceId);
var usernameSecretKey = new SecretAccount(username);
var usernameEntry = new KeyStore.SecretKeyEntry(usernameSecretKey);
ks.SetEntry(alias, usernameEntry, prot);
now this has got PasswordProtection but the password is null.
Pulling my Keystore file off the device I can see the data is encrypted (not in plain text). But is this data encrypted enough or is it simple by decrypting by calling something like File.Decrypty(password = null);
Keystore is not encrypted by default but it contains the encrypted key information with it, which is password protected
https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html

Encryption is not the same using the same method to encrypt the same plaintext

Use the same method below to encrypt the same plaintext twice,but the ciphertext are not the same after encryption.Why?
I want to encrypt my password when saving user information to database,and use the encryption method below.But I want to complete the edit-user feature,after inputing the old password and new password,find out the user according to the selected userid, and encrypt the old password,try to validate the old password typed is matched the password stored in database.But they never matched even though I input the right password.So is there any way to encrypt the password and validate if they are the same after using the same method to encrypt the password.
//encrypt the plainText
public static string Encrypt(string plainText)
{
if (plainText == null || plainText == "")
throw new ArgumentNullException("plainText");
var temp = Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = ProtectedData.Protect(temp, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted);
}
Based on the MSDN documentation of the ProtectedData class, it looks like it is performing 2-way encryption using a machine- or user-specific key. That means that if the machine or user that is active checking the key is different than the machine that originally encrypted it, you will get garbage results. If you only want to check the validity of a password, I would suggest a 1-way esecure hashing algorithm instead of 2-way encryption that supports decryption. Other posts on StackOverflow talk about using secure hashing algorithms (What is the most secure hashing algorithm in the .NET framework?).
Edit: I haven't tested this code on various systems under various users, but I expect it would work more universally because I don't expect that it is based on a machine or user key.
System.Security.Cryptography.SHA256Managed sha = new System.Security.Cryptography.SHA256Managed();
byte[] hashed = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(args[0]));
Console.WriteLine("Hash of {0}={1}", args[0], Convert.ToBase64String(hashed));
Edit 2: I would also add that my understanding of cryptography also suggests that you should probably include some "salt" in your hashed value. In other words, add something (like the user name) to the end of the password string before hashing it (both when entered and when checked) so that users with the same password don't end up with the same hash value, for example.
Some randomness in the output is actually a feature for encryption.
You get two choices:
If you ever need to recover the password from its encrypted form as part of your requirements, use encryption as you do, but decipher the result before making the comparison between expected and entered password.
If you don't really need to recover the password, use a hash algorithm instead (with a suitable salt). You'll be able to compare the two hashed values.
Here is something I threw together in a Console application. The Ciphers will not match, but as you can see, you can decrypt them just fine :)
class Program
{
private static string salt = "My Salt Brings All The Boys To The Yard... Wait A Second.....";
static void Main(string[] args)
{
for (int i = 0; 0 < 20; i++)
{
string password = "Guess my password!";
string cipher = Encrypt(password, salt);
string decipher = Decrypt(cipher, salt);
Console.WriteLine(decipher);
Thread.Sleep(500);
}
Console.ReadKey();
}
static public string Encrypt(string password, string salt)
{
byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
byte[] saltBytes = Encoding.Unicode.GetBytes(salt);
byte[] cipherBytes = ProtectedData.Protect(passwordBytes, saltBytes, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(cipherBytes);
}
static public string Decrypt(string cipher, string salt)
{
byte[] cipherBytes = Convert.FromBase64String(cipher);
byte[] saltBytes = Encoding.Unicode.GetBytes(salt);
byte[] passwordBytes = ProtectedData.Unprotect(cipherBytes, saltBytes, DataProtectionScope.CurrentUser);
return Encoding.Unicode.GetString(passwordBytes);
}
}

Is there a way to authenticate with DirectoryServices to LDAP using MD5?

I'm using VS 2012, C#.NET and creating a form to authenticate through LDAP.
I have this code, and it's working well:
root = new DirectoryEntry(
"LDAP://192.168.116.20:389",
username,
password
);
Both username and password are plain-text.
But I want to create a "Remember password?" checkbox where I can save the username and password md5-hashed in a file.
So, how can I authenticate using the md5-hash with DirectoryEntry and LDAP?! Is it possible?
I don't believe so, LDAP is a protocol, and it works against LM / NT hashes, which are DES & MD4 respectfully, but that's lower level. What you probably want to do is encrypt the password, save it, then decrypt it and pass it to the LDAP string.
If you chose to encrypt the data to a a file, you should use the System.Security.ProtectedData class.
The data you encrypt can be bounded to the current user or the current machine that the encoding/decoding is taking place on.
There are two simple method you should use:
Protect - Takes a byte array and encrypt the data.
Unprotect - Takes an encrypted data and returns a byte array.
Examples:
private static void EncryptData(string data, Stream stream)
{
if (stream.CanWrite == false)
throw new IOException("Cannot write to stream.");
var bytes = Encoding.UTF8.GetBytes(data);
var encryptedBytes = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
stream.Write(encryptedBytes , 0, encryptedBytes .Length);
}
private static string DecryptData(Stream stream)
{
if (stream.CanRead == false)
throw new IOException("Cannot read fromstream.");
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
var encryptedBytes = memoryStream.ToArray();
var decryptedBytes = ProtectedData.Unprotect(encryptedBytes, null, DataProtectionScope.CurrentUser)
return Encoding.UTF8.GetString(decryptedBytes);
}
}
Now in order to use these with a FileStream simply:
public static void Encrypt(string password)
{
using (var fileStream = new FileStream(#"MyFile.dat", FileMode.Create))
{
EncryptData(password, fileStream);
fileStream.Close();
}
}
public static string Decrypt()
{
string password;
using (var fileStream = new FileStream(#"MyFile.dat", FileMode.Open))
{
password = DecryptData(fileStream);
fileStream.Close();
}
return password;
}
By the way, if you want to increase the complexity of the encryption you can pass an Entropy to the Protect and Unprotect methods.
For more information see: http://msdn.microsoft.com/en-us/library/system.security.cryptography.protecteddata.protect(v=vs.110).aspx
I don't believe so, LDAP is a protocol, and it works against LM / NT
hashes, which are DES & MD4 respectfully, but that's lower level.
Well LDAP is a protocol, but LDAP does NOT use LM / NT hashes.
From LDAP the LM/NT/Kerboros AND md5-hash could be done via SASL from LDAP, but only if the LDAP client and LDAP server has those capabilities to utilize SASL.
A quik look at a (I think wk3 server) shows int he ROOTDSE:
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: EXTERNAL
supportedSASLMechanisms: GSS-SPNEGO
supportedSASLMechanisms: GSSAPI
which implies that DIGEST-MD5 is supported in AD. I do not know if this is supported in the Directory Services API.

How to get private key from PKCS#12 (.p12) file using C#

Im trying to sign some data using PKCS#12 certificate ,however i have problem with obtaining private key from PKCS#12 (.p12) file.
public byte[] sign(string text)
{
string password = "1111";
X509Certificate2 cert = new X509Certificate2("c:\\certificate.p12",password);
byte[] certData = cert.Export(X509ContentType.Pfx,password);
X509Certificate2 newCert = new X509Certificate2(certData, password);
RSACryptoServiceProvider crypt = (RSACryptoServiceProvider)newCert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
return crypt.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
The problem is that newCert.PrivateKey is null but if i am using .pfx certicitae in similar way it works.
public byte[] sign(string text)
{
string password = "1234";
X509Certificate2 cert = new X509Certificate2("c:\\certificate.pfx", password);
RSACryptoServiceProvider crypt = (RSACryptoServiceProvider)cert.PrivateKey;
SHA1Managed sha1 = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
return crypt.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
}
So the question is how to get that private key from .p12 file ?
I had a similar problem which I posted here, although it is not the same thing for you, the problem may be also permissions.
My suggestions are, first, you have to make sure (which I suppose you already did) that the private key is exportable and you have permissions to the file.
Next, try exporting the content type as X509ContentType.Pkcs12 instead of X509ContentType.Pfx
Finally, if it is possible, why don't you try importing it to the certstore. I believe that's more secure. The steps are in the link above.
Have a look at this question. It looks very similar.
In the docs, it says that .export() doesn't support the Pfx type, only Cert, SerializedCert, and Pkcs12.
This was done for using Android - so the R.raw.key below was my file in the Android Raw folder.
I opened key.p12 as as input stream. Which I then converted to the private key using the libraries as seen in the example.
http://www.flexiprovider.de/examples/ExampleSMIMEsign.html
My code looks like this
Security.addProvider(new de.flexiprovider.core.FlexiCoreProvider());
// Next, we have to read the private PKCS #12 file, since the the
// private key used for signing is contained in this file:
DERDecoder dec = new DERDecoder(getResources().openRawResource(
R.raw.key));
PFX pfx = new PFX();
try {
pfx.decode(dec);
SafeBag safeBag = pfx.getAuthSafe().getSafeContents(0)
.getSafeBag(0);
PKCS8ShroudedKeyBag kBag = (PKCS8ShroudedKeyBag) safeBag
.getBagValue();
char[] password = "my password for the p12".toCharArray();
privKey = kBag.getPrivateKey(password);
new AsyncLoadStorage(this).execute();
} catch (ASN1Exception e) {

Categories

Resources