Skip to content

Commit

Permalink
Added support for storing and encrypting passwords saucepleez#138
Browse files Browse the repository at this point in the history
  • Loading branch information
saucepleez committed Jul 30, 2019
1 parent 802014b commit f7e17de
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 3 deletions.
100 changes: 97 additions & 3 deletions taskt/Core/Automation/Commands/DatabaseDefineConnectionCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

namespace taskt.Core.Automation.Commands
{





[Serializable]
[Attributes.ClassAttributes.Group("Database Commands")]
[Attributes.ClassAttributes.Description("This command allows you to define a connection to an OLEDB data source")]
Expand All @@ -36,6 +36,14 @@ public class DatabaseDefineConnectionCommand : ScriptCommand
[Attributes.PropertyAttributes.PropertyUIHelper(Attributes.PropertyAttributes.PropertyUIHelper.UIAdditionalHelperType.ShowVariableHelper)]
public string v_ConnectionString { get; set; }

[XmlAttribute]
[Attributes.PropertyAttributes.PropertyDescription("Define Connection String Password")]
[Attributes.PropertyAttributes.InputSpecification("")]
[Attributes.PropertyAttributes.SampleUsage("")]
[Attributes.PropertyAttributes.Remarks("")]
[Attributes.PropertyAttributes.PropertyUIHelper(Attributes.PropertyAttributes.PropertyUIHelper.UIAdditionalHelperType.ShowVariableHelper)]
public string v_ConnectionStringPassword { get; set; }

[XmlAttribute]
[Attributes.PropertyAttributes.PropertyDescription("Test Connection Before Proceeding")]
[Attributes.PropertyAttributes.PropertyUISelectionOption("Yes")]
Expand All @@ -49,6 +57,10 @@ public class DatabaseDefineConnectionCommand : ScriptCommand
[XmlIgnore]
[NonSerialized]
private TextBox ConnectionString;

[XmlIgnore]
[NonSerialized]
private TextBox ConnectionStringPassword;
public DatabaseDefineConnectionCommand()
{
this.CommandName = "DatabaseDefineConnectionCommand";
Expand All @@ -65,6 +77,15 @@ public override void RunCommand(object sender)
var connection = v_ConnectionString.ConvertToUserVariable(sender);
var instance = v_InstanceName.ConvertToUserVariable(sender);
var testPreference = v_TestConnection.ConvertToUserVariable(sender);
var connectionPass = v_ConnectionStringPassword.ConvertToUserVariable(sender);

if (connectionPass.StartsWith("!"))
{
connectionPass = connectionPass.Substring(1);
connectionPass = EncryptionServices.DecryptString(connectionPass, "taskt-database-automation");
}

connection = connection.Replace("#pwd", connectionPass);

var oleDBConnection = new OleDbConnection(connection);

Expand Down Expand Up @@ -92,7 +113,7 @@ public override List<Control> Render(frmCommandEditor editor)
helperControl.CommandImage = UI.Images.GetUIImage("VariableCommand");
helperControl.CommandDisplay = "Build Connection String";
helperControl.Click += (sender, e) => Button_Click(sender, e);


ConnectionString = (TextBox)CommandControls.CreateDefaultInputFor("v_ConnectionString", this);

Expand All @@ -104,6 +125,48 @@ public override List<Control> Render(frmCommandEditor editor)
RenderedControls.AddRange(connectionHelpers);
RenderedControls.Add(ConnectionString);

ConnectionStringPassword = (TextBox)CommandControls.CreateDefaultInputFor("v_ConnectionStringPassword", this);

var connectionPassLabel = CommandControls.CreateDefaultLabelFor("v_ConnectionStringPassword", this);
var connectionPassHelpers = CommandControls.CreateUIHelpersFor("v_ConnectionStringPassword", this, new[] { ConnectionStringPassword }, editor);

RenderedControls.Add(connectionPassLabel);
RenderedControls.AddRange(connectionPassHelpers);

CommandItemControl passwordHelperControl = new CommandItemControl();
passwordHelperControl.Padding = new Padding(10, 0, 0, 0);
passwordHelperControl.ForeColor = Color.AliceBlue;
passwordHelperControl.Font = new Font("Segoe UI Semilight", 10);
passwordHelperControl.Name = "show_pass_helper";
passwordHelperControl.CommandImage = UI.Images.GetUIImage("VariableCommand");
passwordHelperControl.CommandDisplay = "Show Password";
passwordHelperControl.Click += (sender, e) => TogglePasswordChar(passwordHelperControl, e);

RenderedControls.Add(passwordHelperControl);


CommandItemControl encryptHelperControl = new CommandItemControl();
encryptHelperControl.Padding = new Padding(10, 0, 0, 0);
encryptHelperControl.ForeColor = Color.AliceBlue;
encryptHelperControl.Font = new Font("Segoe UI Semilight", 10);
encryptHelperControl.Name = "show_pass_helper";
encryptHelperControl.CommandImage = UI.Images.GetUIImage("VariableCommand");
encryptHelperControl.CommandDisplay = "Encrypt Password";
encryptHelperControl.Click += (sender, e) => EncryptPassword(passwordHelperControl, e);
RenderedControls.Add(encryptHelperControl);

var label = new Label();
label.AutoSize = true;
label.Font = new Font("Segoe UI", 10, FontStyle.Regular);
label.ForeColor = Color.White;
label.Text = "NOTE: If storing the password in the textbox below, please ensure the connection string above contains a database-specific placeholder with #pwd to be replaced at runtime. (;Password=#pwd)";
RenderedControls.Add(label);


RenderedControls.Add(ConnectionStringPassword);
ConnectionStringPassword.PasswordChar = '*';


RenderedControls.AddRange(CommandControls.CreateDefaultInputGroupFor("v_TestConnection", this, editor));

return RenderedControls;
Expand All @@ -114,6 +177,37 @@ private void Button_Click(object sender, EventArgs e)
{
ShowConnectionBuilder();
}
private void TogglePasswordChar(CommandItemControl sender, EventArgs e)
{
//if password is hidden
if (ConnectionStringPassword.PasswordChar == '*')
{
//show password plain text
sender.CommandDisplay = "Hide Password";
ConnectionStringPassword.PasswordChar = '\0';
}
else
{
//mask password with chars
sender.CommandDisplay = "Show Password";
ConnectionStringPassword.PasswordChar = '*';
}
}
private void EncryptPassword(CommandItemControl sender, EventArgs e)
{
if (string.IsNullOrEmpty(ConnectionStringPassword.Text))
{
return;
}

var acknowledgement = MessageBox.Show("WARNING! This function will encrypt the password locally but is not extremely secure as the client knows the secret! Consider using a password management service instead. The encrypted password will be stored with a leading exclamation ('!') whch the automation engine will detect and know to decrypt the value automatically at run-time. Do not encrypt the password multiple times or the decryption will be invalid! Would you like to proceed?", "Encryption Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

if (acknowledgement == DialogResult.Yes)
{
ConnectionStringPassword.Text = string.Concat($"!{EncryptionServices.EncryptString(ConnectionStringPassword.Text, "taskt-database-automation")}");
}

}

public void ShowConnectionBuilder()
{
Expand Down
67 changes: 67 additions & 0 deletions taskt/Core/EncryptionServices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace taskt.Core
{
public static class EncryptionServices
{
// 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 const string initVector = "kPcmAhmtL4ofXSMJ";
// This constant is used to determine the keysize of the encryption algorithm
private const int keysize = 256;
//Encrypt
public static string EncryptString(string plainText, string passPhrase)
{
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
//Decrypt
public static string DecryptString(string cipherText, string passPhrase)
{
byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
byte[] keyBytes = password.GetBytes(keysize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
public static string GenerateToken()
{
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] tokenData = new byte[32];
rng.GetBytes(tokenData);
return Convert.ToBase64String(tokenData);
}
}
}
}
2 changes: 2 additions & 0 deletions taskt/taskt.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.Web" />
<Reference Include="System.Windows" />
<Reference Include="System.Xml.Linq" />
Expand Down Expand Up @@ -274,6 +275,7 @@
<Compile Include="Core\Automation\Commands\WaitForFileToExistCommand.cs" />
<Compile Include="Core\Automation\Commands\WriteTextFileCommand.cs" />
<Compile Include="Core\DocumentationGeneration.cs" />
<Compile Include="Core\EncryptionServices.cs" />
<Compile Include="Core\GlobalAppInstances.cs" />
<Compile Include="Core\Client.cs" />
<Compile Include="Core\Common.cs" />
Expand Down

0 comments on commit f7e17de

Please sign in to comment.