using System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Security.Cryptography; using System.Text.RegularExpressions; using AMWD.Common.Tests.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AMWD.Common.Tests.Utilities { [TestClass] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public class CryptographyHelperTests { private string keyFile; private CryptographyHelper cryptoHelper; [TestInitialize] public void Initialize() { keyFile = Path.GetTempFileName(); cryptoHelper = new CryptographyHelper(keyFile); } [TestCleanup] public void Cleanup() { File.Delete(keyFile); } #region Static #region Encryption #region AES [TestMethod] public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself { // arrange using var disposable = CryptographyHelperSaltMock.Create(0); byte[] bytes = new byte[] { 0xaf, 0xfe }; string str = "ABC"; string password1 = "P@ssw0rd!"; string password2 = "P@ssw0rd"; byte[] expectedBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc }; // act byte[] cipherBytes1 = CryptographyHelper.AesEncrypt(bytes, password1); string cipherStr1 = CryptographyHelper.AesEncrypt(str, password1); byte[] cipherBytes2 = CryptographyHelper.AesEncrypt(bytes, password2); string cipherStr2 = CryptographyHelper.AesEncrypt(str, password2); // assert CollectionAssert.AreEqual(expectedBytes, cipherBytes1); Assert.AreEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr1); CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2); Assert.AreNotEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr2); } [TestMethod] public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself { // arrange using var disposable = CryptographyHelperSaltMock.Create(0); string cipherStr = "ueLuhFNpCuYmx8v3hczHtg=="; byte[] cipherBytes = new byte[] { 0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc }; string password1 = "P@ssw0rd!"; string password2 = "P@ssw0rd"; byte[] expectedBytes = new byte[] { 0xaf, 0xfe }; // act byte[] plainBytes1 = CryptographyHelper.AesDecrypt(cipherBytes, password1); string plainStr1 = CryptographyHelper.AesDecrypt(cipherStr, password1); try { CryptographyHelper.AesDecrypt(cipherBytes, password2); Assert.Fail(); } catch (CryptographicException) { } try { CryptographyHelper.AesDecrypt(cipherStr, password2); Assert.Fail(); } catch (CryptographicException) { } // assert CollectionAssert.AreEqual(expectedBytes, plainBytes1); Assert.AreEqual("ABC", plainStr1); } [TestMethod] public void ShouldEncryptDecryptAesBytes() { // arrange byte[] plain = new byte[] { 0xaf, 0xfe }; string password = "P@ssw0rd!"; // act byte[] cipher1 = CryptographyHelper.AesEncrypt(plain, password); byte[] cipher2 = CryptographyHelper.AesEncrypt(plain, password); byte[] plain1 = CryptographyHelper.AesDecrypt(cipher1, password); byte[] plain2 = CryptographyHelper.AesDecrypt(cipher2, password); // assert CollectionAssert.AreNotEqual(cipher1, cipher2); Assert.AreEqual(24, cipher1.Length); Assert.AreEqual(24, cipher2.Length); CollectionAssert.AreEqual(plain, plain1); CollectionAssert.AreEqual(plain, plain2); } [TestMethod] public void ShouldEncryptDecryptAesString() { // arrange string plain = "ABC"; string password = "P@ssw0rd!"; // act string cipher1 = CryptographyHelper.AesEncrypt(plain, password); string cipher2 = CryptographyHelper.AesEncrypt(plain, password); string plain1 = CryptographyHelper.AesDecrypt(cipher1, password); string plain2 = CryptographyHelper.AesDecrypt(cipher2, password); // assert Assert.AreNotEqual(cipher1, cipher2); Assert.AreEqual(32, cipher1.Length); Assert.AreEqual(32, cipher2.Length); Assert.AreEqual(plain, plain1); Assert.AreEqual(plain, plain2); } #endregion AES #region TripleDES [TestMethod] public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself { // arrange using var disposable = CryptographyHelperSaltMock.Create(0); byte[] bytes = new byte[] { 0xaf, 0xfe }; string str = "ABC"; string password1 = "P@ssw0rd!"; string password2 = "P@ssw0rd"; byte[] expectedBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 }; // act byte[] cipherBytes1 = CryptographyHelper.TripleDesEncrypt(bytes, password1); string cipherStr1 = CryptographyHelper.TripleDesEncrypt(str, password1); byte[] cipherBytes2 = CryptographyHelper.TripleDesEncrypt(bytes, password2); string cipherStr2 = CryptographyHelper.TripleDesEncrypt(str, password2); // assert CollectionAssert.AreEqual(expectedBytes, cipherBytes1); Assert.AreEqual("1l74soBuuEI=", cipherStr1); CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2); Assert.AreNotEqual("1l74soBuuEI=", cipherStr2); } [TestMethod] public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself { // arrange using var disposable = CryptographyHelperSaltMock.Create(0); string cipherStr = "1l74soBuuEI="; byte[] cipherBytes = new byte[] { 0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7 }; string password1 = "P@ssw0rd!"; string password2 = "P@ssw0rd"; byte[] expectedBytes = new byte[] { 0xaf, 0xfe }; // act byte[] plainBytes1 = CryptographyHelper.TripleDesDecrypt(cipherBytes, password1); string plainStr1 = CryptographyHelper.TripleDesDecrypt(cipherStr, password1); try { CryptographyHelper.TripleDesDecrypt(cipherBytes, password2); Assert.Fail(); } catch (CryptographicException) { } try { CryptographyHelper.TripleDesDecrypt(cipherStr, password2); Assert.Fail(); } catch (CryptographicException) { } // assert CollectionAssert.AreEqual(expectedBytes, plainBytes1); Assert.AreEqual("ABC", plainStr1); } [TestMethod] public void ShouldEncryptDecryptTdesBytes() { // arrange byte[] plain = new byte[] { 0xaf, 0xfe }; string password = "P@ssw0rd!"; // act byte[] cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password); byte[] cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password); byte[] plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password); byte[] plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password); // assert CollectionAssert.AreNotEqual(cipher1, cipher2); Assert.AreEqual(16, cipher1.Length); Assert.AreEqual(16, cipher2.Length); CollectionAssert.AreEqual(plain, plain1); CollectionAssert.AreEqual(plain, plain2); } [TestMethod] public void ShouldEncryptDecryptTdesString() { // arrange string plain = "ABC"; string password = "P@ssw0rd!"; // act string cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password); string cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password); string plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password); string plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password); // assert Assert.AreNotEqual(cipher1, cipher2); Assert.AreEqual(24, cipher1.Length); Assert.AreEqual(24, cipher2.Length); Assert.AreEqual(plain, plain1); Assert.AreEqual(plain, plain2); } #endregion TripleDES #endregion Encryption #region Hash [TestMethod] public void ShouldReturnMd5Hash() { // arrange string text = "Hello World!"; byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef }; string fileName = Path.GetTempFileName(); // act string textHash = CryptographyHelper.Md5(text); string bytesHash = CryptographyHelper.Md5(bytes); File.WriteAllText(fileName, text); string fileHash = CryptographyHelper.Md5File(fileName); File.Delete(fileName); // assert Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", textHash); Assert.AreEqual("2f249230a8e7c2bf6005ccd2679259ec", bytesHash); Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", fileHash); } [TestMethod] public void ShouldReturnSha1Hash() { // arrange string text = "Hello World!"; byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef }; string fileName = Path.GetTempFileName(); // act string textHash = CryptographyHelper.Sha1(text); string bytesHash = CryptographyHelper.Sha1(bytes); File.WriteAllText(fileName, text); string fileHash = CryptographyHelper.Sha1File(fileName); File.Delete(fileName); // assert Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", textHash); Assert.AreEqual("d78f8bb992a56a597f6c7a1fb918bb78271367eb", bytesHash); Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", fileHash); } [TestMethod] public void ShouldReturnSha256Hash() { // arrange string text = "Hello World!"; byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef }; string fileName = Path.GetTempFileName(); // act string textHash = CryptographyHelper.Sha256(text); string bytesHash = CryptographyHelper.Sha256(bytes); File.WriteAllText(fileName, text); string fileHash = CryptographyHelper.Sha256File(fileName); File.Delete(fileName); // assert Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", textHash); Assert.AreEqual("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953", bytesHash); Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", fileHash); } [TestMethod] public void ShouldReturnSha512Hash() { // arrange string text = "Hello World!"; byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef }; string fileName = Path.GetTempFileName(); // act string textHash = CryptographyHelper.Sha512(text); string bytesHash = CryptographyHelper.Sha512(bytes); File.WriteAllText(fileName, text); string fileHash = CryptographyHelper.Sha512File(fileName); File.Delete(fileName); // assert Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", textHash); Assert.AreEqual("1284b2d521535196f22175d5f558104220a6ad7680e78b49fa6f20e57ea7b185d71ec1edb137e70eba528dedb141f5d2f8bb53149d262932b27cf41fed96aa7f", bytesHash); Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", fileHash); } #endregion Hash #region Random [TestMethod] public void ShouldReturnRandomBytes() { // arrange int length1 = 12; int length2 = 12; int length3 = 42; // act byte[] bytes1 = CryptographyHelper.GetRandomBytes(length1); byte[] bytes2 = CryptographyHelper.GetRandomBytes(length2); byte[] bytes3 = CryptographyHelper.GetRandomBytes(length3); // assert Assert.AreEqual(length1, bytes1.Length); Assert.AreEqual(length2, bytes2.Length); Assert.AreEqual(length3, bytes3.Length); Assert.IsTrue(bytes1.Length == bytes2.Length); CollectionAssert.AreNotEqual(bytes1, bytes2); } [TestMethod] public void ShouldReturnRandomString() { // arrange int length1 = 12; int length2 = 12; int length3 = 42; // act string str1 = CryptographyHelper.GetRandomString(length1); string str2 = CryptographyHelper.GetRandomString(length2); string str3 = CryptographyHelper.GetRandomString(length3); // assert Assert.AreEqual(length1, str1.Length); Assert.AreEqual(length2, str2.Length); Assert.AreEqual(length3, str3.Length); Assert.IsTrue(str1.Length == str2.Length); Assert.IsFalse(str1 == str2); } [TestMethod] public void ShouldReturnRandomStringWithPool() { // arrange int length = 12; string pool = "0123456789abcdef"; // act string str1 = CryptographyHelper.GetRandomString(length, pool); string str2 = CryptographyHelper.GetRandomString(length, pool); // assert Assert.AreEqual(length, str1.Length); Assert.AreEqual(length, str2.Length); Assert.IsFalse(str1 == str2); Assert.IsFalse(Regex.IsMatch(str1, "[^0-9a-f]")); Assert.IsFalse(Regex.IsMatch(str2, "[^0-9a-f]")); } #endregion Random #region Secure probing [TestMethod] public void ShouldTakeSameTimeToCompareString() { // arrange string str1 = "Hello World!"; string str2 = "Hello World!"; string str3 = "Hallo World!"; string str4 = "Hello World?"; string nullStr = null; string strLen = "Hello World"; var sw = new Stopwatch(); // act bool nullCompare = CryptographyHelper.SecureEquals(nullStr, str1); bool lenCompare = CryptographyHelper.SecureEquals(strLen, str1); sw.Start(); bool compare1 = CryptographyHelper.SecureEquals(str1, str2); long time1 = sw.ElapsedMilliseconds; bool compare2 = CryptographyHelper.SecureEquals(str1, str3); long time2 = sw.ElapsedMilliseconds; bool compare3 = CryptographyHelper.SecureEquals(str1, str4); long time3 = sw.ElapsedMilliseconds; sw.Stop(); // assert Assert.IsFalse(nullCompare); Assert.IsFalse(lenCompare); Assert.IsTrue(compare1); Assert.IsFalse(compare2); Assert.IsFalse(compare3); Assert.AreEqual(time1, time2); Assert.AreEqual(time1, time3); Assert.AreEqual(time2, time3); } [TestMethod] public void ShouldTakeSameTimeToCompareBytes() { // arrange byte[] bytes1 = CryptographyHelper.GetRandomBytes(200); byte[] bytes2 = new byte[bytes1.Length]; byte[] bytes3 = new byte[bytes1.Length]; byte[] bytes4 = new byte[bytes1.Length]; byte[] nullBytes = null; byte[] lenBytes = new byte[bytes1.Length + 1]; Array.Copy(bytes1, bytes2, bytes1.Length); Array.Copy(bytes1, bytes3, bytes1.Length); Array.Copy(bytes1, bytes4, bytes1.Length); bytes3[10] = (byte)(bytes1[10] + 1); bytes4[190] = (byte)(bytes1[190] + 1); var sw = new Stopwatch(); // act bool nullCompare = CryptographyHelper.SecureEquals(nullBytes, bytes1); bool lenCompare = CryptographyHelper.SecureEquals(lenBytes, bytes1); sw.Start(); bool compare1 = CryptographyHelper.SecureEquals(bytes1, bytes2); long time1 = sw.ElapsedMilliseconds; bool compare2 = CryptographyHelper.SecureEquals(bytes1, bytes3); long time2 = sw.ElapsedMilliseconds; bool compare3 = CryptographyHelper.SecureEquals(bytes1, bytes4); long time3 = sw.ElapsedMilliseconds; sw.Stop(); // assert Assert.IsFalse(nullCompare); Assert.IsFalse(lenCompare); Assert.IsTrue(compare1); Assert.IsFalse(compare2); Assert.IsFalse(compare3); Assert.AreEqual(time1, time2); Assert.AreEqual(time1, time3); Assert.AreEqual(time2, time3); } #endregion Secure probing #endregion Static #region Instance [TestMethod] public void ShouldCreateDefaultFile() { // arrange string executingAssemblyDir = AppContext.BaseDirectory; string filePath = Path.Combine(executingAssemblyDir, "crypto.key"); // act bool fileExistsBefore = File.Exists(filePath); var helper = new CryptographyHelper(); bool fileExistsAfter = File.Exists(filePath); string content = fileExistsAfter ? File.ReadAllText(filePath) : null; File.Delete(filePath); // assert Assert.IsFalse(fileExistsBefore); Assert.IsNotNull(helper); Assert.IsTrue(fileExistsAfter); Assert.IsTrue(!string.IsNullOrWhiteSpace(content)); } [TestMethod] public void ShouldEncryptAesUsingKeyFile() { // arrange string str = "Hello World!"; byte[] bytes = CryptographyHelper.GetRandomBytes(32); string password = File.ReadAllText(keyFile); // act string cipherStr = cryptoHelper.EncryptAes(str); byte[] cipherBytes = cryptoHelper.EncryptAes(bytes); string plainStr = CryptographyHelper.AesDecrypt(cipherStr, password); byte[] plainBytes = CryptographyHelper.AesDecrypt(cipherBytes, password); // assert Assert.AreNotEqual(str, cipherStr); Assert.AreEqual(str, plainStr); CollectionAssert.AreNotEqual(bytes, cipherBytes); CollectionAssert.AreEqual(bytes, plainBytes); } [TestMethod] public void ShouldDecryptAesUsingKeyFile() { // arrange string str = "Hello World!"; byte[] bytes = CryptographyHelper.GetRandomBytes(32); string password = File.ReadAllText(keyFile); // act string cipherStr = CryptographyHelper.AesEncrypt(str, password); byte[] cipherBytes = CryptographyHelper.AesEncrypt(bytes, password); string plainStr = cryptoHelper.DecryptAes(cipherStr); byte[] plainBytes = cryptoHelper.DecryptAes(cipherBytes); // assert Assert.AreNotEqual(str, cipherStr); Assert.AreEqual(str, plainStr); CollectionAssert.AreNotEqual(bytes, cipherBytes); CollectionAssert.AreEqual(bytes, plainBytes); } [TestMethod] public void ShouldEncryptTdesUsingKeyFile() { // arrange string str = "Hello World!"; byte[] bytes = CryptographyHelper.GetRandomBytes(32); string password = File.ReadAllText(keyFile); // act string cipherStr = cryptoHelper.EncryptTripleDes(str); byte[] cipherBytes = cryptoHelper.EncryptTripleDes(bytes); string plainStr = CryptographyHelper.TripleDesDecrypt(cipherStr, password); byte[] plainBytes = CryptographyHelper.TripleDesDecrypt(cipherBytes, password); // assert Assert.AreNotEqual(str, cipherStr); Assert.AreEqual(str, plainStr); CollectionAssert.AreNotEqual(bytes, cipherBytes); CollectionAssert.AreEqual(bytes, plainBytes); } [TestMethod] public void ShouldDecryptTdesUsingKeyFile() { // arrange string str = "Hello World!"; byte[] bytes = CryptographyHelper.GetRandomBytes(32); string password = File.ReadAllText(keyFile); // act string cipherStr = CryptographyHelper.TripleDesEncrypt(str, password); byte[] cipherBytes = CryptographyHelper.TripleDesEncrypt(bytes, password); string plainStr = cryptoHelper.DecryptTripleDes(cipherStr); byte[] plainBytes = cryptoHelper.DecryptTripleDes(cipherBytes); // assert Assert.AreNotEqual(str, cipherStr); Assert.AreEqual(str, plainStr); CollectionAssert.AreNotEqual(bytes, cipherBytes); CollectionAssert.AreEqual(bytes, plainBytes); } #endregion Instance } }