I just know I will get referenced to a billion SO questions, BUT, everytime I try to use one for use with .net Maui and therefore using .Net 6 and to be .Net 7 I get a warning about OBSOLETE (and deprecated) functions. I can't tell you how many solutions I've tried. And of course I simply don't have the brains or time to dig deep.
So I'd be very grateful if someone could simply post some code that does the following:
(password could be ANY length, but I'll accept restrictions, but min 4 chars (like a pin). Yes I know it might not be very secure, but it's all I need for my use case.)
public static string Encrypt(string plainText, string password)
{
}
public static string Decrypt(string cipherText, string password)
{
}
I have seen that there are all kinds of extras like salts and pbkdf2 for hashing the password, but I'd really like all that gubbins to be hidden from me, so I don't have to bother. But please feel free to add explanations of why I should bother...
Many thanks and please be gentle. :)
CodePudding user response:
So I seem to have figured out a solution using
namespace Census.Classes;
public static class EncryptionHelper
{
public static string Encrypt(string plainText, byte[] encryptionKeyBytes)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = encryptionKeyBytes;
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new())
{
using (CryptoStream cryptoStream = new((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
public static string Decrypt(string cipherText, byte[] encryptionKeyBytes)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = encryptionKeyBytes;
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new(buffer))
{
using (CryptoStream cryptoStream = new((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
private static readonly byte[] Salt = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80 };
public static byte[] CreateKey(string password, int keyBytes = 32)
{
const int Iterations = 300;
var keyGenerator = new Rfc2898DeriveBytes(password, Salt, Iterations);
return keyGenerator.GetBytes(keyBytes);
}
}
I use it as follows:
//get an encryption key from the password
byte[] encryptionKeyBytes = EncryptionHelper.CreateKey(Password);
mystring = EncryptionHelper.Encrypt(plaintext, encryptionKeyBytes);
I do it like this because I call it very many times.
I'd be grateful if someone more knowledgeable could cast their eye over it and see if there are any improvements that could be made, but at least it works and doesn't throw up any errors about obsolete or deprecated code - yay! :)
CodePudding user response:
If you want to store a secret on a particular computer only, and for a particular username then use the ProtectedData
class provided in System.Security.Cryptography
.
public static string Encrypt(string plainText, string password = null)
{
var data = Encoding.Default.GetBytes(plainText);
var pwd = !string.IsNullOrEmpty(password) ? Encoding.Default.GetBytes(password) : Array.Empty<byte>();
var cipher = ProtectedData.Protect(data, pwd, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(cipher);
}
public static string Decrypt(string cipherText, string password = null)
{
var cipher = Convert.FromBase64String(cipherText);
var pwd = !string.IsNullOrEmpty(password) ? Encoding.Default.GetBytes(password) : Array.Empty<byte>();
var data = ProtectedData.Unprotect(cipher, pwd, DataProtectionScope.CurrentUser);
return Encoding.Default.GetString(data);
}
it used the windows cryptographic store to keep the secret. Optionally you can make it available to all users of the machine (provided they have the password) by changing the scope to DataProtectionScope.LocalMachine
.
Note that the password is optional, as it uses your windows credentials for key generation.