Encrypt/Decrypt string using any length password/passphrase - c#

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. :)

So I seem to have figured out a solution using
https://www.c-sharpcorner.com/article/encryption-and-decryption-using-a-symmetric-key-in-c-sharp/
generating AES 256 bit key value
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! :)

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.

Related

Cannot decrypt data .net 6 with bare bones implementation keep getting The input data is not a complete block

There are lots of topics on this but none of them seem to solve the problem.
So here is the code I am using, ive gone to a bare bones implementation which should work:
public class CustomEncryptor : IEncryptor
{
public string Key { get; } // 32 bytes i.e 6GUNtgba8Rqgm5tc9JGhzxjN6XbzuyNM
public CustomEncryptor(string key)
{
Key = key;
}
public byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(Key);
aes.IV = RandomNumberGenerator.GetBytes(16);
aes.Padding = PaddingMode.PKCS7;
return aes.EncryptCbc(data, aes.IV);
}
public byte[] Decrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Encoding.UTF8.GetBytes(Key);
aes.IV = RandomNumberGenerator.GetBytes(16);
aes.Padding = PaddingMode.PKCS7;
return aes.DecryptCbc(data, aes.IV);
}
}
It encrypts the data fine, but when I come to decrypt it I always get:
System.Security.Cryptography.CryptographicException: The input data is
not a complete block.
Given I am using the aes built in methods and im providing a key of 32 bytes, then generating 16 bytes for the initialisation vector it should all be fine right, ive tried just giving it a blank new byte[16] instead and that doesn't help, I have also tried different padding modes and nothing works.
To give some context the app that uses this has an extension method it uses to simplify string scenarios, the use cases are often user does something, it encrypts it and saves it somewhere, then when the app is reloaded it decrypts it and puts it in a var for usage.
public static string Encrypt(this IEncryptor encryptor, string textToEncrypt)
{
var textAsBytes = Encoding.UTF8.GetBytes(textToEncrypt);
var encryptedData = encryptor.Encrypt(textAsBytes);
return Convert.ToBase64String(encryptedData);
}
public static string Decrypt(this IEncryptor encryptor, string encryptedText)
{
var textAsBytes = Convert.FromBase64String(encryptedText);
var decryptedData = encryptor.Decrypt(textAsBytes);
return Encoding.UTF8.GetString(decryptedData);
}
I am baffled as to why it doesnt work as lots of examples online do same sort of thing.
== Update 1 ==
It was mentioned in comments that the key & iv need to be consistent so I have tried setting that to explicitly be the same thing like so:
public class CustomEncryptor : IEncryptor
{
public byte[] Key { get; }
public byte[] IV { get; }
public CustomEncryptor(byte[] key, byte[] iv)
{
IV = iv;
Key = key;
}
public byte[] Encrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Key;
return aes.EncryptCbc(data, IV);
}
public byte[] Decrypt(byte[] data)
{
using var aes = Aes.Create();
aes.Key = Key;
return aes.DecryptCbc(data, IV);
}
}
// Example usage
var key = Encoding.UTF8.GetBytes("UxRBN8hfjzTG86d6SkSSNzyUhERGu5Zj"); // 32 bytes
var iv = Encoding.UTF8.GetBytes("7cA8jkRMJGZ8iMeJ"); // 16 bytes
services.AddSingleton<IEncryptor>(new CustomEncryptor(key, iv));
Still get same error

"Padding is invalid and cannot be removed" during decryption

First, I realize there are dozens of other posts that have answers to this question and I have read and tried them all. I still can't seem to get past this issue so am looking for a little help from somebody that knows more about crypto than I do.
Second, the code I am going to share is legacy and because I am not a crypto expert it is still not 100% clear on what everything means. It may be that some or all of this code is total rubbish and should be scrapped; however, there are a lot of other systems already using it and have stored encrypted values that have gone through this code. Changing things like the crypto algorithm is not exactly an option at this point. With that said, the private methods are the legacy code as well as the testing values (i.e. the encryption key) are all things that can't change. The two public static methods are what is new and likely causing problems, but I can't seem to figure it out.
On with the code......
class Program
{
public static string Encrypt(string key, string toEncrypt)
{
var keyArray = Convert.FromBase64String(key);
var info = Encoding.ASCII.GetBytes(toEncrypt);
var encrypted = Encrypt(keyArray, info);
return Encoding.ASCII.GetString(encrypted);
}
public static string Decrypt(string key, string cipherString)
{
var keyArray = Convert.FromBase64String(key);
var cipherText = Encoding.ASCII.GetBytes(cipherString);
var decrypted = Decrypt(keyArray, cipherText);
return Encoding.ASCII.GetString(decrypted);
}
private static byte[] Encrypt(byte[] key, byte[] info)
{
using (var cipher = Aes.Create())
{
cipher.Key = key;
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.ISO10126;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, cipher.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(info, 0, info.Length);
}
var ciphertext = ms.ToArray();
var message = new byte[cipher.IV.Length + ciphertext.Length];
cipher.IV.CopyTo(message, 0);
ciphertext.CopyTo(message, cipher.IV.Length);
return message;
}
}
}
private static byte[] Decrypt(byte[] key, byte[] ciphertext)
{
using (var cipher = Aes.Create())
{
cipher.Key = key;
cipher.Mode = CipherMode.CBC;
cipher.Padding = PaddingMode.ISO10126;
var ivSize = cipher.IV.Length;
var iv = new byte[ivSize];
Array.Copy(ciphertext, iv, ivSize);
cipher.IV = iv;
var data = new byte[ciphertext.Length - ivSize];
Array.Copy(ciphertext, ivSize, data, 0, data.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, cipher.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
}
return ms.ToArray();
}
}
}
static void Main(string[] args)
{
var newEncryptionKey = Guid.NewGuid().ToString().Replace("-", string.Empty);
var encryptedValue = Encrypt(newEncryptionKey, "test");
Console.WriteLine($"New encrypted value: {encryptedValue}");
var decryptedValue = Decrypt(newEncryptionKey, encryptedValue);
Console.WriteLine($"New decrypted value: {decryptedValue}");
}
}
So there it is. Basically, I am trying to use a test string of "test" and encrypt it using a GUID as a key. Again, I didn't choose this key and there are encrypted values already using a GUID as a key so I can't change that if at all possible. The encryption works fine, but when I go to do the decryption, I get the exception noted in the title of this question.
Any help would be GREATLY appreciated.
You can't just convert a byte[] of ciphertext to ASCII. It doesn't work like that. Character encodings are scary beasts and should not be muddled with if you don't understand them. I don't think there is a real person alive that does ;)
What you should do instead is return your result as base64, which is still a collection of ASCII characters but they are safe to be moved around as a string, and don't result in the loss of any characters.
See the modified code below:
public static string Encrypt(string key, string toEncrypt)
{
var keyArray = Convert.FromBase64String(key);
var info = Encoding.ASCII.GetBytes(toEncrypt);
var encrypted = Encrypt(keyArray, info);
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string key, string cipherString)
{
var keyArray = Convert.FromBase64String(key);
var cipherText = Convert.FromBase64String(cipherString);
var decrypted = Decrypt(keyArray, cipherText);
return Encoding.ASCII.GetString(decrypted);
}

Encode and Decode Passwords in WPF Applications

I have a WPF applications that contains a user form. I have a password field I need to decript it, I just have an encrypt method for this:
public static string Encode(string value)
{
var hash = System.Security.Cryptography.SHA1.Create();
var encoder = new System.Text.ASCIIEncoding();
var combined = encoder.GetBytes(value ?? "");
return BitConverter.ToString(hash.ComputeHash(combined)).ToLower().Replace("-", "");
}
How to create a Decode method for this?
The key in this situation is that you don't want to decrypt the password. What you want to do is to encrypt the password entered by the user using the same function and comparing the result to what is stored in the database for the user's account.
Hashing is the right thing to do, but you should also hash the value that you are storing in the database and compare the two hashes, you should not, if at all possible, be storing passwords, even if they are encrypted.
You should store the one-way hash in the database instead of a reversible encryption. What the user's actual password happens to be is not your concern, only that the hash of the user supplied password when salted matches the hash in your database. You might, however, encrypt it for transport to be decrypted at the server and then hashed for storage in the database.
Retrieve salt and hashed password from database.
Salt candidate password and hash it.
Compare values.
SHA1 is a hash function which means it is basically one-way function, you cannot get the original content back.
you should use cryptographic functions if you need to get the original value back.
there basically two classes of encryption algorithms - symmetric and asymmetric cryptographic functions.
symetric is for example AES
asymetric would be Public/private key encrytion such as RSA
take a look at this sample: http://www.codeproject.com/Articles/10877/Public-Key-RSA-Encryption-in-C-NET
You can't decrypt SHA1 hash because it's a one way hash. Hashing is not a reversible operation.
Below a helper class that both encrypt and decrypte a string
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace EncryptStringSample
{
public static class StringCipher
{
// This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
// This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be
// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes("tu89geji340t89u2");
// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
public static string Encrypt(string plainText, string passPhrase)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
using(PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using(RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using(ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes))
{
using(MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using(CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
}
}
The above class can used quite simply with code similar to the following:
using System;
using System.Linq;
namespace EncryptStringSample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter a password to use:");
string password = Console.ReadLine();
Console.WriteLine("Please enter a string to encrypt:");
string plaintext = Console.ReadLine();
Console.WriteLine("");
Console.WriteLine("Your encrypted string is:");
string encryptedstring = StringCipher.Encrypt(plaintext, password);
Console.WriteLine(encryptedstring);
Console.WriteLine("");
Console.WriteLine("Your decrypted string is:");
string decryptedstring = StringCipher.Decrypt(encryptedstring, password);
Console.WriteLine(decryptedstring);
Console.WriteLine("");
Console.ReadLine();
}
}
}
Here's another way to decrypt and encrypt using RSA
Replace your_rsa_key with your RSA key.
var provider = new System.Security.Cryptography.RSACryptoServiceProvider();
provider.ImportParameters(your_rsa_key);
var encryptedBytes = provider.Encrypt(
System.Text.Encoding.UTF8.GetBytes("Hello World!"), true);
string decryptedTest = System.Text.Encoding.UTF8.GetString(
provider.Decrypt(encryptedBytes, true));
Here's a good link on how RSA works with a clear exmaple

How can I decrypt below functions string with Rfc2898DeriveBytes?

How can I decrypt string that is encrypted with below function?
private static byte[] salt = new byte[] { 23, 21, 32, 33, 46, 59, 60, 74 };
private static int iterations = 1000;
private static int cb = 32;
public static string GetEncrypt(string plainText)
{
Rfc2898DeriveBytes k = new Rfc2898DeriveBytes(plainText, salt, iterations);
byte[] data = k.GetBytes(cb);
return Convert.ToBase64String(data);
}
You can't. You've misunderstood the purpose of Rfc2898DeriveBytes. It's not meant to be an reversible encryption algorithm - it's just meant to be a way of deriving a key which you then use within another encryption algorithm.
The idea is that you ask for a password, convert it (with a salt) into an encryption key, and use that key to encrypt the data (e.g. with TripleDES).
Then later, you ask for the password again, use the same salt to generate the same key, and then you can use that to decrypt the encrypted data.
Read RFC 2898 for more information.
public static Tuple<string, string, string> Encrypt(string encryptText)
{
byte[] bytesBuff = Encoding.Unicode.GetBytes(encryptText);
string keyValue;
string vectorValue;
using (Aes aes = Aes.Create())
{
var crypto = new Rfc2898DeriveBytes(encryptText, saltSize, iterations);
aes.Key = crypto.GetBytes(32);
aes.IV = crypto.GetBytes(16);
using var mStream = new MemoryStream();
using (var cStream = new CryptoStream(mStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cStream.Write(bytesBuff, 0, bytesBuff.Length);
cStream.Close();
}
encryptText = Convert.ToBase64String(mStream.ToArray());
keyValue = Convert.ToBase64String(aes.Key);
vectorValue = Convert.ToBase64String(aes.IV);
}
return new Tuple<string, string, string>(encryptText, keyValue, vectorValue);
}
public static string Decrypt(string decriptText, string keyValue, string vectorValue)
{
decriptText = decriptText.Replace(" ", "+");
byte[] bytesBuff = Convert.FromBase64String(decriptText);
using (Aes aes = Aes.Create())
{
aes.Key = Convert.FromBase64String(keyValue);
aes.IV = Convert.FromBase64String(vectorValue);
using var mStream = new MemoryStream();
using (var cStream = new CryptoStream(mStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cStream.Write(bytesBuff, 0, bytesBuff.Length);
cStream.Close();
}
decriptText = Encoding.Unicode.GetString(mStream.ToArray());
}
return decriptText;
}

Encrypting & Decrypting a String in C# [duplicate]

This question already has answers here:
Encrypt and decrypt a string in C#? [closed]
(29 answers)
Closed 6 years ago.
What is the most modern (best) way of satisfying the following in C#?
string encryptedString = SomeStaticClass.Encrypt(sourceString);
string decryptedString = SomeStaticClass.Decrypt(encryptedString);
BUT with a minimum of fuss involving salts, keys, mucking about with byte[], etc.
Been Googling and confused at what I'm finding (you can see the list of similar SO Qs to see this is a deceptive question to ask).
UPDATE 23/Dec/2015: Since this answer seems to be getting a lot of upvotes, I've updated it to fix silly bugs and to generally improve the code based upon comments and feedback. See the end of the post for a list of specific improvements.
As other people have said, Cryptography is not simple so it's best to avoid "rolling your own" encryption algorithm.
You can, however, "roll your own" wrapper class around something like the built-in RijndaelManaged cryptography class.
Rijndael is the algorithmic name of the current Advanced Encryption Standard, so you're certainly using an algorithm that could be considered "best practice".
The RijndaelManaged class does indeed normally require you to "muck about" with byte arrays, salts, keys, initialization vectors etc. but this is precisely the kind of detail that can be somewhat abstracted away within your "wrapper" class.
The following class is one I wrote a while ago to perform exactly the kind of thing you're after, a simple single method call to allow some string-based plaintext to be encrypted with a string-based password, with the resulting encrypted string also being represented as a string. Of course, there's an equivalent method to decrypt the encrypted string with the same password.
Unlike the first version of this code, which used the exact same salt and IV values every time, this newer version will generate random salt and IV values each time. Since salt and IV must be the same between the encryption and decryption of a given string, the salt and IV is prepended to the cipher text upon encryption and extracted from it again in order to perform the decryption. The result of this is that encrypting the exact same plaintext with the exact same password gives and entirely different ciphertext result each time.
The "strength" of using this comes from using the RijndaelManaged class to perform the encryption for you, along with using the Rfc2898DeriveBytes function of the System.Security.Cryptography namespace which will generate your encryption key using a standard and secure algorithm (specifically, PBKDF2) based upon the string-based password you supply. (Note this is an improvement of the first version's use of the older PBKDF1 algorithm).
Finally, it's important to note that this is still unauthenticated encryption. Encryption alone provides only privacy (i.e. message is unknown to 3rd parties), whilst authenticated encryption aims to provide both privacy and authenticity (i.e. recipient knows message was sent by the sender).
Without knowing your exact requirements, it's difficult to say whether the code here is sufficiently secure for your needs, however, it has been produced to deliver a good balance between relative simplicity of implementation vs "quality". For example, if your "receiver" of an encrypted string is receiving the string directly from a trusted "sender", then authentication may not even be necessary.
If you require something more complex, and which offers authenticated encryption, check out this post for an implementation.
Here's the code:
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;
namespace EncryptStringSample
{
public static class StringCipher
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 256;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
using (var streamReader = new StreamReader(cryptoStream, Encoding.UTF8))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
}
The above class can be used quite simply with code similar to the following:
using System;
namespace EncryptStringSample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter a password to use:");
string password = Console.ReadLine();
Console.WriteLine("Please enter a string to encrypt:");
string plaintext = Console.ReadLine();
Console.WriteLine("");
Console.WriteLine("Your encrypted string is:");
string encryptedstring = StringCipher.Encrypt(plaintext, password);
Console.WriteLine(encryptedstring);
Console.WriteLine("");
Console.WriteLine("Your decrypted string is:");
string decryptedstring = StringCipher.Decrypt(encryptedstring, password);
Console.WriteLine(decryptedstring);
Console.WriteLine("");
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
}
(You can download a simple VS2013 sample solution (which includes a few unit tests) here).
UPDATE 23/Dec/2015:
The list of specific improvements to the code are:
Fixed a silly bug where encoding was different between encrypting and
decrypting. As the mechanism by which salt & IV values are generated has changed, encoding is no longer necessary.
Due to the salt/IV change, the previous code comment that incorrectly indicated that UTF8 encoding a 16 character string produces 32 bytes is no longer applicable (as encoding is no longer necessary).
Usage of the superseded PBKDF1 algorithm has been replaced with usage of the more modern PBKDF2 algorithm.
The password derivation is now properly salted whereas previously it wasn't salted at all (another silly bug squished).
using System.IO;
using System.Text;
using System.Security.Cryptography;
public static class EncryptionHelper
{
public static string Encrypt(string clearText)
{
string EncryptionKey = "abc123";
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
return clearText;
}
public static string Decrypt(string cipherText)
{
string EncryptionKey = "abc123";
cipherText = cipherText.Replace(" ", "+");
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
}
If you are targeting ASP.NET Core that does not support RijndaelManaged yet, you can use IDataProtectionProvider.
First, configure your application to use data protection:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection();
}
// ...
}
Then you'll be able to inject IDataProtectionProvider instance and use it to encrypt/decrypt data:
public class MyService : IService
{
private const string Purpose = "my protection purpose";
private readonly IDataProtectionProvider _provider;
public MyService(IDataProtectionProvider provider)
{
_provider = provider;
}
public string Encrypt(string plainText)
{
var protector = _provider.CreateProtector(Purpose);
return protector.Protect(plainText);
}
public string Decrypt(string cipherText)
{
var protector = _provider.CreateProtector(Purpose);
return protector.Unprotect(cipherText);
}
}
See this article for more details.
Try this class:
public class DataEncryptor
{
TripleDESCryptoServiceProvider symm;
#region Factory
public DataEncryptor()
{
this.symm = new TripleDESCryptoServiceProvider();
this.symm.Padding = PaddingMode.PKCS7;
}
public DataEncryptor(TripleDESCryptoServiceProvider keys)
{
this.symm = keys;
}
public DataEncryptor(byte[] key, byte[] iv)
{
this.symm = new TripleDESCryptoServiceProvider();
this.symm.Padding = PaddingMode.PKCS7;
this.symm.Key = key;
this.symm.IV = iv;
}
#endregion
#region Properties
public TripleDESCryptoServiceProvider Algorithm
{
get { return symm; }
set { symm = value; }
}
public byte[] Key
{
get { return symm.Key; }
set { symm.Key = value; }
}
public byte[] IV
{
get { return symm.IV; }
set { symm.IV = value; }
}
#endregion
#region Crypto
public byte[] Encrypt(byte[] data) { return Encrypt(data, data.Length); }
public byte[] Encrypt(byte[] data, int length)
{
try
{
// Create a MemoryStream.
var ms = new MemoryStream();
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
var cs = new CryptoStream(ms,
symm.CreateEncryptor(symm.Key, symm.IV),
CryptoStreamMode.Write);
// Write the byte array to the crypto stream and flush it.
cs.Write(data, 0, length);
cs.FlushFinalBlock();
// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
byte[] ret = ms.ToArray();
// Close the streams.
cs.Close();
ms.Close();
// Return the encrypted buffer.
return ret;
}
catch (CryptographicException ex)
{
Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
}
return null;
}
public string EncryptString(string text)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(text)));
}
public byte[] Decrypt(byte[] data) { return Decrypt(data, data.Length); }
public byte[] Decrypt(byte[] data, int length)
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream ms = new MemoryStream(data);
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream cs = new CryptoStream(ms,
symm.CreateDecryptor(symm.Key, symm.IV),
CryptoStreamMode.Read);
// Create buffer to hold the decrypted data.
byte[] result = new byte[length];
// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
cs.Read(result, 0, result.Length);
return result;
}
catch (CryptographicException ex)
{
Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
}
return null;
}
public string DecryptString(string data)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data))).TrimEnd('\0');
}
#endregion
}
and use it like this:
string message="A very secret message here.";
DataEncryptor keys=new DataEncryptor();
string encr=keys.EncryptString(message);
// later
string actual=keys.DecryptString(encr);
If you need to store a password in memory and would like to have it encrypted you should use SecureString:
http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx
For more general uses I would use a FIPS approved algorithm such as Advanced Encryption Standard, formerly known as Rijndael. See this page for an implementation example:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx
You may be looking for the ProtectedData class, which encrypts data using the user's logon credentials.
The easiest way that I've seen to do encryption is through RSA
Check out the MSDN on it: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx
It does involve using bytes, but when it comes down to it you kind of do want encryption and decryption to be tough to figure out otherwise it will be easy to hack.

Categories