Passwords and Configuration Files
It is so easy to embed connection strings in Web.config and App.config files. Maybe that’s the problem: It Is So Easy.
App.config files are plain text, human readable, documents. They are easily read as they are plain XML formatted text files.
XML Files presents security risks
… For User Id and Passwords
Using XML File as a Configuration Convention has its pros and cons.
As text files, the configuration data complies with the Open / Closed Principle. They allow you to modify the results of the consuming software entity without opening the code for that software entity.
Files like Web.config and App.config are Valuable Tools
This is good when the settings in these files are defined as immutable data required at design startup time and generally do not change during run-time.
If the same data is stored in a repository for modification during run-time then the XML data probably violates the DRY Principle: “Don’t Repeat Yourself”.
The Cryptology Cross Cutting Concern Class
TCMS uses the Infrastructure Services sub-domain to hold a Cryptology Class. It is located inside the Helpers folder with the InfoServices.Common.Helpers namespace.
The Cryptology Class is an implementation of the “RijndaelManaged” class that derives from the “Rijndael” base class.
That Class is derived from the “SymmetricAlgorithm” class within the System.Security.Cryptography namespace.
+The Rijndael Cryptology Class
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace InfoServices.Common.Helpers { public static class Cryptology { public static string EncryptStringAES(string plainText, string sharedSecret, byte[] salt) { ValidateInputs(plainText, sharedSecret, salt); using (var aesAlg = new RijndaelManaged()) { string outStr; try { using (var msEncrypt = new MemoryStream()) { aesAlg.Key = new Rfc2898DeriveBytes(sharedSecret, salt).GetBytes(aesAlg.KeySize / 8); msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int)); msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV), CryptoStreamMode.Write)) { using (var swEncrypt = new StreamWriter(csEncrypt)) swEncrypt.Write(plainText); } outStr = Convert.ToBase64String(msEncrypt.ToArray()); } } finally { if (aesAlg != null) aesAlg.Clear(); } return outStr; } } public static string DecryptStringAES(string cipherText, string sharedSecret, byte[] salt) { ValidateInputs(cipherText, sharedSecret, salt); using (var aesAlg = new RijndaelManaged()) { string plaintext; try { using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText))) { aesAlg.Key = new Rfc2898DeriveBytes(sharedSecret, salt).GetBytes(aesAlg.KeySize / 8); aesAlg.IV = ReadByteArray(msDecrypt); using (var csDecrypt = new CryptoStream(msDecrypt, aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV), CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) plaintext = srDecrypt.ReadToEnd(); } } } finally { if (aesAlg != null) aesAlg.Clear(); } return plaintext; } } public static string CreateNew64SaltStringFromKeyString(string saltKeyString) { var rng = new RNGCryptoServiceProvider(); byte[] buff = Encoding.ASCII.GetBytes(saltKeyString); rng.GetBytes(buff); return Convert.ToBase64String(buff); } public static byte[] CreateNewSaltByteArrayFromKeyString(string saltKeyString) { var rng = new RNGCryptoServiceProvider(); byte[] buff = Encoding.ASCII.GetBytes(saltKeyString); rng.GetBytes(buff); return buff; } public static byte[] CreateSaltByteArrayFromSalt64String(string salt64) { return Convert.FromBase64String(salt64); } public static string Create64SaltStringFromSaltByteArray(byte[] saltString64Byte) { return Convert.ToBase64String(saltString64Byte); } #region Helpers private static byte[] ReadByteArray(Stream stream) { var rawLength = new byte[sizeof(int)]; if (stream.Read(rawLength, 0, rawLength.Length) != rawLength.Length) throw new SystemException("Stream did not contain properly formatted byte array"); var buffer = new byte[BitConverter.ToInt32(rawLength, 0)]; if (stream.Read(buffer, 0, buffer.Length) != buffer.Length) throw new SystemException("Did not read byte array properly"); return buffer; } private static void ValidateInputs(string text, string sharedSecret, byte[] salt) { if (string.IsNullOrEmpty(text)) throw new ArgumentNullException("text"); if (string.IsNullOrEmpty(sharedSecret)) throw new ArgumentNullException("sharedSecret"); if (string.IsNullOrEmpty(salt[0].ToString())) throw new ArgumentNullException("salt"); } #endregion } } |
+Cryptology Class Tests
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
using InfoServices.Common.Helpers; // Namespace for the Cryptology Class using Microsoft.VisualStudio.TestTools.UnitTesting; namespace TCMS.Infrastructure.Tests.UnitTests.Infrastructure.Common { public static class CryptographyTests { [TestClass] public class GivenIWantToTestTheCryptographyClass { private static string _saltKeyString { get; set; } private static byte[] _saltNewByteArray { get; set; } private static string _stringPlainText { get; set; } private static string _sharedSecretKey { get; set; } public GivenIWantToTestTheCryptographyClass() { _saltKeyString = "b092655W101546"; // You can create your own but make sure the char length padding is the same _saltNewByteArray = Cryptology.CreateNewSaltByteArrayFromKeyString(_saltKeyString); _stringPlainText = "YourInitialTestString"; _sharedSecretKey = "YourSecretSharedKey"; // Any Text string without spaces } [TestMethod] public void WhenICallTheEncryptStringMethod_ThenIWillValidateTheEncryptStringResult() { //Arrange //Act var result = Cryptology.EncryptStringAES(_stringPlainText, _sharedSecretKey, _saltNewByteArray); //Assert Assert.IsNotNull(result); } [TestMethod] public void WhenICallTheDecryptStringMethod_ThenIWillValidateTheDecryptStringResult() { //Arrange string stringCipherText = Cryptology.EncryptStringAES(_stringPlainText, _sharedSecretKey, _saltNewByteArray); //Act var result = Cryptology.DecryptStringAES(stringCipherText, _sharedSecretKey, _saltNewByteArray); //Assert Assert.IsNotNull(stringCipherText); Assert.IsNotNull(result); Assert.AreEqual(_stringPlainText, result); } [TestMethod] public void WhenIConvertTheSaltNewByteArrayToString64AndBackToByteArray_ThenIWillValidateTheResult() { //Arrange var userId = "YourUserId"; var sharedSecretKey = "YourSharedSecretKey"; var saltNewByteArray = Cryptology.CreateNewSaltByteArrayFromKeyString(sharedSecretKey); string encryptedUserId = Cryptology.EncryptStringAES(userId, sharedSecretKey, saltNewByteArray); string string64Salt = Cryptology.Create64SaltStringFromSaltByteArray(saltNewByteArray); byte[] string64SaltByteArray = Cryptology.CreateSaltByteArrayFromSalt64String(string64Salt); //Act string decryptedUserId = Cryptology.DecryptStringAES(encryptedUserId, sharedSecretKey, string64SaltByteArray); //Assert Assert.IsNotNull(decryptedUserId); Assert.IsNotNull(decryptedUserId); Assert.AreEqual(userId, decryptedUserId); } [TestMethod] public void WhenIWantToValidateProcess_ThenIWillValidateTheResult() { // ** This is the test you use to create your Base 64 Salt Encription and Encripted User Id and Password //Arrange var userId = "YourUserId"; var userPassword = "YourPassword"; var sharedSecretKey = "YourSharedSecretKey"; var string64Salt = @"u/fEzVoW7SLYNB/liUJMlUSv"; // A Base64 Encoded Salt: You can use the Helper Methods to create your own var encryptedUserId = @"userIdCrypted: Created Below"; var encryptedUserPassword = @"passwordCrypted: Created Below"; byte[] string64SaltByteArray = Cryptology.CreateSaltByteArrayFromSalt64String(string64Salt); //var userIdCrypted = Cryptology.EncryptStringAES(userId, sharedSecretKey, string64SaltByteArray); //var passwordCrypted = Cryptology.EncryptStringAES(userPassword, sharedSecretKey, string64SaltByteArray); //Act string decryptedUserId = Cryptology.DecryptStringAES(encryptedUserId, sharedSecretKey, string64SaltByteArray); string decryptedUserPassword = Cryptology.DecryptStringAES(encryptedUserPassword, sharedSecretKey, string64SaltByteArray); //Assert Assert.IsNotNull(decryptedUserId); Assert.IsNotNull(decryptedUserPassword); Assert.AreEqual(userId, decryptedUserId); Assert.AreEqual(userPassword, decryptedUserPassword); } #region Salt Tests [TestMethod] public void WhenICallTheCreateNew64SaltStringFromKeyStringMethod_ThenIWillValidateTheMethodResult() { //Arrange //Act string result = Cryptology.CreateNew64SaltStringFromKeyString(_saltKeyString); //Assert Assert.IsNotNull(result); } [TestMethod] public void WhenICallTheCreateNewSaltByteArrayFromKeyStringMethod_ThenIWillValidateTheMethodResult() { //Arrange //Act byte[] result = Cryptology.CreateNewSaltByteArrayFromKeyString(_saltKeyString); //Assert Assert.IsNotNull(result); } [TestMethod] public void WhenICallTheCreateSaltByteArrayFromSalt64StringMethod_ThenIWillValidateTheMethodResult() { //Arrange var saltBase64 = Cryptology.CreateNew64SaltStringFromKeyString(_saltKeyString); //Act byte[] result = Cryptology.CreateSaltByteArrayFromSalt64String(saltBase64); //Assert Assert.IsNotNull(result); } [TestMethod] public void WhenICallTheCreate64SaltStringFromSaltByteArrayMethod_ThenIWillValidateTheMethodResult() { //Arrange byte[] saltString64Byte = Cryptology.CreateNewSaltByteArrayFromKeyString(_saltKeyString); string string64Salt = Cryptology.Create64SaltStringFromSaltByteArray(saltString64Byte); byte[] string64SaltByteArray = Cryptology.CreateSaltByteArrayFromSalt64String(string64Salt); //Act string result = Cryptology.Create64SaltStringFromSaltByteArray(saltString64Byte); //Assert Assert.IsNotNull(result); CollectionAssert.AreEquivalent(saltString64Byte, string64SaltByteArray); foreach (var item in saltString64Byte) { CollectionAssert.Contains(string64SaltByteArray, item); } } [TestMethod] public void WhenIWhatToValadateTheConversionProcess_ThenIWillValidateTheProcessResult() { //Arrange byte[] saltString64Byte = Cryptology.CreateNewSaltByteArrayFromKeyString(_saltKeyString); string string64Salt = Cryptology.Create64SaltStringFromSaltByteArray(saltString64Byte); //Act byte[] string64SaltByteArray = Cryptology.CreateSaltByteArrayFromSalt64String(string64Salt); //Assert CollectionAssert.AreEquivalent(saltString64Byte, string64SaltByteArray); foreach (var item in saltString64Byte) { CollectionAssert.Contains(string64SaltByteArray, item); } } #endregion } } } |
+Password Decryption Example Code
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
using System.Configuration; using System.Data.EntityClient; using System.Data.SqlClient; using InfoServices.Common.Helpers; using InfoServices.Infrastructure.Messages.DTOs; namespace InfoServices.Infrastructure.Messages.Helpers { public static class MessageHelpers { public static string CreateEntityConnection(SqlConnInfoDTO connInfo, string entityMetadata) { return CreateEntityConnString(CreateSqlConnString(connInfo), entityMetadata); } public static string GetConnString() { var encryptedUserId = System.Configuration.ConfigurationManager.AppSettings["EncryptedUser"]; var encryptedPassword = System.Configuration.ConfigurationManager.AppSettings["EncryptedPassword"]; var secretKey = System.Configuration.ConfigurationManager.AppSettings["SecretKey"]; var string64Salt = System.Configuration.ConfigurationManager.AppSettings["SaltKeyString"]; var string64SaltByteArray = Cryptology.CreateSaltByteArrayFromSalt64String(string64Salt); var applicationName = "EntityFramework"; var initialCatalog = "TCMS"; var serverName = ConfigurationManager.AppSettings["ServerName"]; var userId = Cryptology.DecryptStringAES(encryptedUserId, secretKey, string64SaltByteArray);; var userPassword = Cryptology.DecryptStringAES(encryptedPassword, secretKey, string64SaltByteArray); var entityMetadata = @"res://*/DAL.Messaging.csdl| res://*/DAL.Messaging.ssdl| res://*/DAL.Messaging.msl"; return CreateConnString(applicationName, initialCatalog, serverName, userId, userPassword, entityMetadata); } #region Helpers private static string CreateSqlConnString(SqlConnInfoDTO sqlConnInfo) { return new SqlConnectionStringBuilder { DataSource = sqlConnInfo.ServerName, InitialCatalog = sqlConnInfo.InitialCatalog, UserID = sqlConnInfo.UserId, Password = sqlConnInfo.UserPassword, MultipleActiveResultSets = sqlConnInfo.MultipleActiveResultSets, ApplicationName = sqlConnInfo.ApplicationName }.ToString(); } private static string CreateEntityConnString(string providerString, string entityMetadata) { return new EntityConnectionStringBuilder { Provider = "System.Data.SqlClient", ProviderConnectionString = providerString, Metadata = entityMetadata }.ConnectionString; } private static string CreateConnString(string appName, string initCatalog, string serverName, string userId, string userPassword, string entityMetadata) { SqlConnInfoDTO connInfo = new SqlConnInfoDTO { ApplicationName = appName, InitialCatalog = initCatalog, MultipleActiveResultSets = true, ServerName = serverName, UserId = userId, UserPassword = userPassword }; return CreateEntityConnection(connInfo, entityMetadata); } #endregion } } |
+Password Decryption App.Config
|
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="ServerName" value="PLACE SERVER NAME HERE" /> <add key="EncryptedUser" value="THE ENCRIPTED USER ID TEXT FROM THE UNIT TEST AS NOTED" /> <add key="EncryptedPassword" value="THE ENCRIPTED USER PASSWORD TEXT FROM THE UNIT TEST AS NOTED" /> <add key="SecretKey" value="ANY TEXT WITH NO SPACES AS THE SHARED KEY" /> <add key="SaltKeyString" value="u/fEzVoW7SLYNB/liUJMlUSv" /> <!--This is the SALT from the Test String--> </appSettings> </configuration> |
What is Rijndael?
Rijndael, pronounced: “Rain Doll“, is the Advanced Encryption Standard (AES) specification for the encryption of electronic data .
This standard was established by the U.S. National Institute of Standards and Technology (NIST) in 2001.
Rijndael is based on the Rijndael Cipher. It was developed by two Belgian Cryptographers, Joan Daemen and Vincent Rijmen.
Their Submitted Proposal was Evaluated against 15 other Ciphers
… By the NIST during the AES selection process
AES has been adopted by the U.S. government and is now used worldwide. It supersedes the Data Encryption Standard (DES). DES was published in 1977.
The algorithm described by AES is a symmetric-key algorithm. This means that the same key is used for both encrypting and decrypting the data.
The TCMS Cryptology Class
… Is an Info Services Domain Cross Cutting Concerns Resource
…… Its Use is Any Encryption Requirement, Not Just Config Files
The following two tabs change content below.
I am a Principal Architect at Liquid Hub in the Philadelphia area specializing in Agile Practices as a Certified Scrum Master (CSM). I use Test Driven Development (TDD) and Acceptance Test Driven Development (ATDD) with Behavior Driven Development (BDD) as my bridge to Agile User Stories Acceptance Criteria in a Domain Driven Design (DDD) implementing true RESTful services
Latest posts by Brad Huett (see all)
- DevOps: A Bridge to Your DevOps Culture - March 25, 2016
- Embracing Test Driven Development (TDD) - March 25, 2016
- DevOps: Delivering Agile Projects - March 25, 2016

