Last active
March 31, 2020 17:18
-
-
Save Elorucov/039ddfa52b10f6ba0650d700fea7809c to your computer and use it in GitHub Desktop.
CIpher sample from https://stackoverflow.com/a/10177020 optimized for UWP.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Linq; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| using Windows.Storage; | |
| namespace Elorucov.Laney.Helpers.Security { | |
| // https://stackoverflow.com/a/10177020 | |
| // optimized for UWP: using 128 bits instead 256 | |
| // and save entropy (salt/iv) in local settings. | |
| public static class Cipher { | |
| public static string EncryptToBase64(this string plaintext, string passPhrase) { | |
| return Convert.ToBase64String(plaintext.Encrypt(passPhrase)); | |
| } | |
| public static string DecryptFromBase64(this string cipherText, string passPhrase) { | |
| return Convert.FromBase64String(cipherText).Decrypt(passPhrase); | |
| } | |
| // 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 = 128; | |
| // This constant determines the number of iterations for the password bytes generation function. | |
| private const int DerivationIterations = 1237; | |
| public static byte[] Encrypt(this 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 = Generate128BitsOfRandomEntropy(); | |
| var ivStringBytes = Generate128BitsOfRandomEntropy(); | |
| 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 = 128; | |
| 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 cipherTextBytes; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| public static string Decrypt(this byte[] cipherTextBytesWithSaltAndIv, string passPhrase) { | |
| // Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes. | |
| var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray(); | |
| // Get the IV bytes by extracting the next 16 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 32 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 = 128; | |
| 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)) { | |
| var plainTextBytes = new byte[cipherTextBytes.Length]; | |
| var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); | |
| memoryStream.Close(); | |
| cryptoStream.Close(); | |
| return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private static byte[] Generate128BitsOfRandomEntropy() { | |
| var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits. | |
| object val = ApplicationData.Current.LocalSettings.Values["iv"]; | |
| if(val != null && val is string s) { | |
| byte[] b = Convert.FromBase64String(s); | |
| return b; | |
| } else { | |
| using(var rngCsp = new RNGCryptoServiceProvider()) { | |
| // Fill the array with cryptographically secure random bytes. | |
| rngCsp.GetBytes(randomBytes); | |
| ApplicationData.Current.LocalSettings.Values["iv"] = Convert.ToBase64String(randomBytes); | |
| } | |
| } | |
| return randomBytes; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment