diff --git a/Commons.Test/Commons.Test.csproj b/Commons.Test/Commons.Test.csproj index a2c9484..d0c35ad 100644 --- a/Commons.Test/Commons.Test.csproj +++ b/Commons.Test/Commons.Test.csproj @@ -11,7 +11,7 @@ - + diff --git a/Commons.Test/Util/StringCypherTest.cs b/Commons.Test/Util/StringCypherTest.cs new file mode 100644 index 0000000..f1baad3 --- /dev/null +++ b/Commons.Test/Util/StringCypherTest.cs @@ -0,0 +1,26 @@ +using CG.Commons.Util; +using Xunit; +using FluentAssertions; + +namespace CG.Commons.Test.Util +{ + public class StringCypherTest + { + private const string Passphrase = "1F267C5E-FF0D-4C11-DG23-F0855348D2B8"; + + [Fact] + public void TestEncryptDecrypt() + { + //arrange + const string testString = "This is a test string. It will be encrypted and decrypted."; + + //act + var encrypted = StringCipher.Encrypt(testString, Passphrase); + var decrypted = StringCipher.Decrypt(encrypted, Passphrase); + + //assert + encrypted.Should().NotBeNullOrEmpty(); + decrypted.Should().NotBeNullOrEmpty().And.Should().BeEquivalentTo(testString); + } + } +} \ No newline at end of file diff --git a/Commons/Commons.csproj b/Commons/Commons.csproj index bb98a2c..b13e7c5 100644 --- a/Commons/Commons.csproj +++ b/Commons/Commons.csproj @@ -6,7 +6,7 @@ true A collection of useful C# utilities, extensions, and data structures. Chris Gonzales - 1.0.8 + 1.0.9 Commons C# DataStructures Dictionary Extensions Copyright 2021 Chris Gonzales https://chrisg32.github.io/assets/cg_logo.png @@ -19,18 +19,18 @@ CG Commons Library Git https://github.com/chrisg32/Commons - Fix for string Cypher AES bit length. + Fix for string Cypher AES broken decryption. WARNING! make break the ability to decrypt strings encrypted with 1.0.7 and below. CG.Commons en-US - 1.0.8 + 1.0.9 Commons 9 - 1.0.8 - 1.0.8 + 1.0.9 + 1.0.9 - + diff --git a/Commons/Util/StringCipher.cs b/Commons/Util/StringCipher.cs index 3348d48..f3b89ad 100644 --- a/Commons/Util/StringCipher.cs +++ b/Commons/Util/StringCipher.cs @@ -11,6 +11,8 @@ 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; + + private const int BlockSize = 128; // This constant determines the number of iterations for the password bytes generation function. private const int DerivationIterations = 1000; @@ -29,7 +31,7 @@ public static string Encrypt(string plainText, string passPhrase) using (var symmetricKey = Aes.Create()) { //https://stackoverflow.com/questions/9300340/got-error-specified-block-size-is-not-valid-for-this-algorithm-while-initiali - symmetricKey.BlockSize = 128; + symmetricKey.BlockSize = BlockSize; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)) @@ -55,22 +57,26 @@ public static string Encrypt(string plainText, string passPhrase) public static string Decrypt(string cipherText, string passPhrase) { if (cipherText == null) return null; + + var saltLength = Keysize / 8; + var ivLength = BlockSize / 8; + // 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(); + var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(saltLength).ToArray(); + // Get the IV bytes by extracting the next 16 bytes from the supplied cipherText bytes. + var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(saltLength).Take(ivLength).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(); + var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(saltLength + ivLength).Take(cipherTextBytesWithSaltAndIv.Length - (saltLength + ivLength)).ToArray(); using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) { var keyBytes = password.GetBytes(Keysize / 8); using (var symmetricKey = Aes.Create()) { - symmetricKey.BlockSize = 256; + symmetricKey.BlockSize = BlockSize; symmetricKey.Mode = CipherMode.CBC; symmetricKey.Padding = PaddingMode.PKCS7; using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))