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.
Related
I'm creating a library to do some REST API calls that is using OAuth2. I would like to cache the refresh token so the user doesn't have to re-authenticate at every log in, although I know almost nothing about cryptography. Microsoft Docs say that for small pieces of data like strings, asymmetric crypto is the way to go. Here is my class to store the encrypted token on a file somewhere on the computer (specified by the calling code). It works, I'm just not sure how secure it is.
string _file;
const string _containerName = "MyTokenCache";
public EncryptedTokenCache(string fileLocation)
{
_file = fileLocation;
}
public void SaveToken(string token)
{
var rsa = CreateRSAProvider(_containerName);
var encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(token), RSAEncryptionPadding.Pkcs1);
WriteToFile(encryptedData);
}
public string GetToken()
{
var rsa = CreateRSAProvider(_containerName);
var encryptedData = ReadFromFile();
var decryptedData = rsa.Decrypt(encryptedData, RSAEncryptionPadding.Pkcs1);
return Encoding.UTF8.GetString(decryptedData);
}
private RSA CreateRSAProvider(string containerName)
{
CspParameters parameters = new CspParameters
{
KeyContainerName = containerName
};
return new RSACryptoServiceProvider(parameters);
}
private void WriteToFile(byte[] data)
{
using(var fs = new FileStream(_file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
{
fs.Write(data, 0, data.Length);
}
}
private byte[] ReadFromFile()
{
byte[] data;
using (var fs = new FileStream(_file, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None))
{
data = new byte[fs.Length];
fs.Read(data, 0, (int)fs.Length);
}
return data;
}
Well, it's just RSA / PKCS#1 v1.5 encryption. In principle you'd better use OAEP because it is:
provable secure and
less vulnerable to padding oracle attacks.
But for storing tokens PKCS#1 v1.5 should be fine.
Furthermore, you could use symmetric encryption using AES as well, as you currently create the key pair each time you create your class. That means that the private key is available at the same location as the public key, so the security that asymmetric crypto brings is not directly used. That said, I don't see any pressing need to use AES instead for this particular use case.
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.
I've got a program that under Windows and .NET Framework 4 works perfectly well however, under Mono (built in MonoDevelop 2.6), the Encrypt() and Decrypt() function seem to only half work...
To the point at which if I locally encrypt something and then immediately decrypt it (under Mono), the first 10 or so characters of the message are scrambled jiberish, but anything following looks perfectly valid!
The Encrypt function is as follows:
public byte[] Encrypt(string plainText)
{
DateTime now = DateTime.Now;
string timeStamp = now.Millisecond.ToString("000") + "." + now.Second.ToString("00") + "." +
now.Minute.ToString("00") + "." + now.Hour.ToString("00") + Constants.MessageSplitChar;
plainText = plainText.Insert(0, timeStamp);
MemoryStream memoryStream = new MemoryStream();
lock (this.encryptor)
{
CryptoStream cryptoStream = new CryptoStream(memoryStream, this.encryptor, CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
try
{
writer.Write(plainText);
}
finally
{
writer.Close();
cryptoStream.Close();
memoryStream.Close();
}
}
byte[] encryptedMessage = memoryStream.ToArray();
return this.AppendArrays(BitConverter.GetBytes(encryptedMessage.Length), encryptedMessage);
}
The Decrypt function is as follows:
public string Decrypt(byte[] cipherText)
{
try
{
string plainText = string.Empty;
MemoryStream memoryStream = new MemoryStream(cipherText);
lock (this.decryptor)
{
CryptoStream cryptoStream = new CryptoStream(memoryStream, this.decryptor, CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
try
{
plainText = reader.ReadToEnd();
plainText = plainText.Substring(plainText.IndexOf("|") + 1);
plainText = plainText.TrimEnd("\0".ToCharArray());
}
finally
{
reader.Close();
cryptoStream.Close();
memoryStream.Close();
}
}
return plainText;
}
catch (Exception ex)
{
CMicroBingoServer.LogManager.Write(ex.ToString(), MessagePriority.Error);
return "DECRYPTION_FAILED";
}
}
Your code does not show how your decryptor and encryptor instances are created.
This can be a problem because if you're reusing the instance then you must check ICryptoTransform.CanReuseTransform. If it returns false then you cannot reuse the same encryptor/decryptor and must create new instances.
That's even more important since Mono and .NET have different defaults for some algorithms. But in any case skipping this check means that any changes in future .NET framework (or via configuration files, since cryptography is pluggable using CryptoConfig) is likely to break your code someday.
It may be important how many characters bytes at the beginning are seemingly gibberish... If the first block is coming out nonsense but the rest is fine then it could be that your initialization vector is not correct on decryption.
Under the most common block mode, CBC, when decrypting the IV only effects teh decryption of the first block of data since after that its the cipher text that acts as the IV for later blocks.
Are you explicitly setting IVs for encrypting and decrypting? If not then I would imagine that the two have different behaviours when dealing with unset IVs (eg windows uses all zeros and mono generates a random IV - this would cause windows to decrypt fine becuase the IV is the same whereas mono may be generating two different IVs for the encrypt and decrypt process.
I don't know mono stuff well enough to look into the exact solution but something along these lines seems likely.
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
I'm using Rijndael to encrypt some sensitive data in my program.
When the user enters an incorrect password, most of the time a CryptographicException is thrown with the message "Padding is invalid and cannot be removed.".
However, with very small probability, the CryptStream does not throw an exception with the wrong password, but instead gives back an incorrectly decrypted stream. In other words, it decrypts to garbage.
Any idea how to detect/prevent this? The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
But if there's an easier way, I'd love to hear it!
HMAC is what you need. It is exactly made for this purpose. It combines the key and the message (which in this case, will be your password) and hashes them in a way that it will ensure the authenticity and integrity of the content, as long as the hash function used is secure. You can attach the HMAC to the encrypted data, and it can be used later to validate if the decryption was made correctly.
HMAC, Wikipedia
System.Security.Cryptography.HMAC
Checksums are exactly for this purpose. Get a hash of your data before encrypting. Encrypt the data and put it along with the hash into storage. After decrypting, get the hash of the decrypted data and compare it with the former. If you use a crypto grade hash (i.e. SHA512) your data will be safe. After all, this is exactly what encrypted compression software does.
For ultimate security, you can encrypt both the hashes and data separately then decrypt and compare. If both data and hash decrypts to corrupted data, there is very minuscule chances that they will match.
To check if the password you are using is correct, you can use this code
Dim decryptedByteCount As Integer
Try
decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
Catch exp As System.Exception
Return "Password Not Correct"
End Try
in essence, check if an error message is generated during decryption.
I report all the decoding code below
Public Shared Function Decrypt(ByVal cipherText As String) As String
If System.Web.HttpContext.Current.Session("Crypto") = "" Then
HttpContext.Current.Response.Redirect("http://yoursite.com")
Else
If cipherText <> "" Then
'Setto la password per criptare il testo
Dim passPhrase As String = System.Web.HttpContext.Current.Session("Crypto")
'Ottieni lo stream completo di byte che rappresentano: [32 byte di Salt] + [32 byte di IV] + [n byte di testo cifrato]
Dim cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText)
'Ottieni i Salt bytes estraendo i primi 32 byte dai byte di testo cifrato forniti
Dim saltStringBytes = cipherTextBytesWithSaltAndIv.Take((Keysize)).ToArray
'Ottieni i IV byte estraendo i successivi 32 byte dai byte testo cifrato forniti.
Dim ivStringBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize)).Take((Keysize)).ToArray
'Ottieni i byte del testo cifrato effettivo rimuovendo i primi 64 byte dal testo cifrato.
Dim cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(((Keysize) * 2)).Take((cipherTextBytesWithSaltAndIv.Length - ((Keysize) * 2))).ToArray
Dim password = New Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)
Dim keyBytes = password.GetBytes((Keysize))
Dim symmetricKey = New RijndaelManaged
symmetricKey.BlockSize = 256
symmetricKey.Mode = CipherMode.CBC
symmetricKey.Padding = PaddingMode.PKCS7
Dim decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)
Dim memoryStream = New MemoryStream(cipherTextBytes)
Dim cryptoStream = New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
Dim plainTextBytes = New Byte((cipherTextBytes.Length) - 1) {}
Dim decryptedByteCount As Integer
Try
decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
Catch exp As System.Exception
Return "La password di Cryptazione non รจ corretta"
End Try
memoryStream.Close()
cryptoStream.Close()
Return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount)
Else
Decrypt = ""
End If
End If
End Function
Though I can agree somewhat with Teoman Soygul post about CRC/Hash there is one very important thing to note. Never encrypt the hash as this can make it easier to find the resulting key. Even without encrypting the hash you still gave them an easy way to test if they have successfully gained the correct password; however, let's assume that is already possible. Since I know what kind of data you encrypted, be it text, or serialized objects, or whatever, it's likely I can write code to recognize it.
That said, I've used derivations of the following code to encrypt/decrypt data:
static void Main()
{
byte[] test = Encrypt(Encoding.UTF8.GetBytes("Hello World!"), "My Product Name and/or whatever constant", "password");
Console.WriteLine(Convert.ToBase64String(test));
string plain = Encoding.UTF8.GetString(Decrypt(test, "My Product Name and/or whatever constant", "passwords"));
Console.WriteLine(plain);
}
public static byte[] Encrypt(byte[] data, string iv, string password)
{
using (RijndaelManaged m = new RijndaelManaged())
using (SHA256Managed h = new SHA256Managed())
{
m.KeySize = 256;
m.BlockSize = 256;
byte[] hash = h.ComputeHash(data);
byte[] salt = new byte[32];
new RNGCryptoServiceProvider().GetBytes(salt);
m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(hash, 0, hash.Length);
ms.Write(salt, 0, salt.Length);
using (CryptoStream cs = new CryptoStream(ms, m.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
}
public static byte[] Decrypt(byte[] data, string iv, string password)
{
using (MemoryStream ms = new MemoryStream(data, false))
using (RijndaelManaged m = new RijndaelManaged())
using (SHA256Managed h = new SHA256Managed())
{
try
{
m.KeySize = 256;
m.BlockSize = 256;
byte[] hash = new byte[32];
ms.Read(hash, 0, 32);
byte[] salt = new byte[32];
ms.Read(salt, 0, 32);
m.IV = h.ComputeHash(Encoding.UTF8.GetBytes(iv));
m.Key = new Rfc2898DeriveBytes(password, salt) { IterationCount = 10000 }.GetBytes(32);
using (MemoryStream result = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, m.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] buffer = new byte[1024];
int len;
while ((len = cs.Read(buffer, 0, buffer.Length)) > 0)
result.Write(buffer, 0, len);
}
byte[] final = result.ToArray();
if (Convert.ToBase64String(hash) != Convert.ToBase64String(h.ComputeHash(final)))
throw new UnauthorizedAccessException();
return final;
}
}
catch
{
//never leak the exception type...
throw new UnauthorizedAccessException();
}
}
}
I like Can Gencer's answer; you cannot really verify a decryption without the HMAC.
But, if you have a very a very large plaintext, then the decrypting can be very expensive. You might do a ton of work just to find out that the password was invalid. It would be nice to be able to do a quick rejection of wrong passwords, without going through all that work. There is a way using the PKCS#5 PBKDF2. (standardized in RFC2898, which is accessible to your c# program in Rfc2898DeriveBytes).
Normally the data protocol calls for generation of the key from a password and salt using PBKDF2, at 1000 cycles or some specified number. Then maybe also (optionally) the initialization vector, via a contniuation of the same algorithm.
To implement the quick password check, generate two more bytes via the PBKDF2. If you don't generate and use an IV, then just generate 32 bytes and keep the last 2. Store or transmit this pair of bytes adjacent to your cryptotext. On the decrypting side, get the password, generate the key and (maybe throwaway) IV, then generate the 2 additional bytes, and check them against the stored data. If the pairs don't match you know you have a wrong password, without any decryption.
If they match, it is not a guarantee that the password is correct. You still need the HMAC of the full plaintext for that. But you can save yourself a ton of work, and maybe wall clock time, in most cases of "wrong password", and without compromising the security of the overall system.
ps: you wrote:
The simplest way I can think of would be to put a "magic number" at the start of the message when encrypting, and check if it's still there after decrypting.
Avoid putting plaintext into the cryptotext. It only exposes another attack vector, makes it easier for an attacker to eliminate wrong turns. The password verification thing I mentioned above is a different animal, does not expose this risk.
Public Sub decryptFile(ByVal input As String, ByVal output As String)
inputFile = New FileStream(input, FileMode.Open, FileAccess.Read)
outputFile = New FileStream(output, FileMode.OpenOrCreate, FileAccess.Write)
outputFile.SetLength(0)
Dim buffer(4096) As Byte
Dim bytesProcessed As Long = 0
Dim fileLength As Long = inputFile.Length
Dim bytesInCurrentBlock As Integer
Dim rijandael As New RijndaelManaged
Dim cryptoStream As CryptoStream = New CryptoStream(outputFile, rijandael.CreateDecryptor(encryptionKey, encryptionIV), CryptoStreamMode.Write)
While bytesProcessed < fileLength
bytesInCurrentBlock = inputFile.Read(buffer, 0, 4096)
cryptoStream.Write(buffer, 0, bytesInCurrentBlock)
bytesProcessed = bytesProcessed + CLng(bytesInCurrentBlock)
End While
Try
cryptoStream.Close() 'this will raise error if wrong password used
inputFile.Close()
outputFile.Close()
File.Delete(input)
success += 1
Catch ex As Exception
fail += 1
inputFile.Close()
outputFile.Close()
outputFile = Nothing
File.Delete(output)
End Try
I use that code to decrypt any file. Wrong password detected on cryptostream.close(). Catch this line as error when a wrong key is used to decrypt file. When error happens, just close the output stream and release it (set outputFile to Nothing), then delete output file. It's working for me.