diff --git a/mRemoteNGTests/Security/AesCryptographyProviderTests.cs b/mRemoteNGTests/Security/AesCryptographyProviderTests.cs
new file mode 100644
index 00000000..e44a111e
--- /dev/null
+++ b/mRemoteNGTests/Security/AesCryptographyProviderTests.cs
@@ -0,0 +1,58 @@
+using System.Security;
+using mRemoteNG.Security;
+using NUnit.Framework;
+
+
+namespace mRemoteNGTests.Security
+{
+ public class AesCryptographyProviderTests
+ {
+ private AesCryptographyProvider _aesCryptographyProvider;
+ private SecureString _encryptionKey;
+
+ [SetUp]
+ public void Setup()
+ {
+ _aesCryptographyProvider = new AesCryptographyProvider();
+ _encryptionKey = "mypassword111111".ConvertToSecureString();
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ _aesCryptographyProvider = null;
+ }
+
+ [Test]
+ public void GetBlockSizeReturnsProperValueForAes()
+ {
+ Assert.That(_aesCryptographyProvider.BlockSize, Is.EqualTo(16));
+ }
+
+ [Test]
+ public void EncryptionOutputsBase64String()
+ {
+ var plainText = "MySecret!";
+ var cipherText = _aesCryptographyProvider.Encrypt(plainText, _encryptionKey);
+ Assert.That(cipherText.IsBase64String, Is.True);
+ }
+
+ [Test]
+ public void DecryptedTextIsEqualToOriginalPlainText()
+ {
+ var plainText = "MySecret!";
+ var cipherText = _aesCryptographyProvider.Encrypt(plainText, _encryptionKey);
+ var decryptedCipherText = _aesCryptographyProvider.Decrypt(cipherText, _encryptionKey);
+ Assert.That(decryptedCipherText, Is.EqualTo(plainText));
+ }
+
+ [Test]
+ public void EncryptingTheSameValueReturnsNewCipherTextEachTime()
+ {
+ var plainText = "MySecret!";
+ var cipherText1 = _aesCryptographyProvider.Encrypt(plainText, _encryptionKey);
+ var cipherText2 = _aesCryptographyProvider.Encrypt(plainText, _encryptionKey);
+ Assert.That(cipherText1, Is.Not.EqualTo(cipherText2));
+ }
+ }
+}
\ No newline at end of file
diff --git a/mRemoteNGTests/mRemoteNGTests.csproj b/mRemoteNGTests/mRemoteNGTests.csproj
index 4e7d7295..3a9fa4df 100644
--- a/mRemoteNGTests/mRemoteNGTests.csproj
+++ b/mRemoteNGTests/mRemoteNGTests.csproj
@@ -54,6 +54,10 @@
MinimumRecommendedRules.ruleset
+
+ ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll
+ True
+
False
..\mRemoteV1\References\log4net.dll
@@ -100,6 +104,7 @@
+
Form
diff --git a/mRemoteNGTests/packages.config b/mRemoteNGTests/packages.config
index 7692ea5d..1cd6ffc6 100644
--- a/mRemoteNGTests/packages.config
+++ b/mRemoteNGTests/packages.config
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/mRemoteV1/Security/AesCryptographyProvider.cs b/mRemoteV1/Security/AesCryptographyProvider.cs
new file mode 100644
index 00000000..75c25ee6
--- /dev/null
+++ b/mRemoteV1/Security/AesCryptographyProvider.cs
@@ -0,0 +1,33 @@
+using System.Security;
+using System.Text;
+using Org.BouncyCastle.Crypto.Engines;
+
+
+namespace mRemoteNG.Security
+{
+ public class AesCryptographyProvider : ICryptographyProvider
+ {
+ private AesEngine _aesEngine;
+ private readonly Encoding _encoding;
+
+ public int BlockSize => _aesEngine.GetBlockSize();
+
+ public AesCryptographyProvider()
+ {
+ _aesEngine = new AesEngine();
+ _encoding = Encoding.UTF8;
+ }
+
+ public string Encrypt(string plainText, SecureString encryptionKey)
+ {
+ var bcEngine = new BouncyCastleCryptographyEngine(_aesEngine, _encoding);
+ return bcEngine.Encrypt(plainText, encryptionKey);
+ }
+
+ public string Decrypt(string cipherText, SecureString decryptionKey)
+ {
+ var bcEngine = new BouncyCastleCryptographyEngine(_aesEngine, _encoding);
+ return bcEngine.Decrypt(cipherText, decryptionKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/mRemoteV1/Security/BouncyCastleCryptographyEngine.cs b/mRemoteV1/Security/BouncyCastleCryptographyEngine.cs
new file mode 100644
index 00000000..f4b6cef0
--- /dev/null
+++ b/mRemoteV1/Security/BouncyCastleCryptographyEngine.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Security;
+using System.Text;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Paddings;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace mRemoteNG.Security
+{
+ internal class BouncyCastleCryptographyEngine
+ {
+ private readonly Encoding _encoding;
+ private readonly IBlockCipher _blockCipher;
+ private IBlockCipherPadding _padding;
+ private const bool ActionEncrypt = true;
+ private const bool ActionDecrypt = false;
+
+ internal IBlockCipherPadding Padding
+ {
+ get { return _padding; }
+ set {
+ if (value != null)
+ _padding = value;
+ }
+ }
+
+ internal BouncyCastleCryptographyEngine(IBlockCipher cipherEngine, Encoding encoding)
+ {
+ _blockCipher = new CbcBlockCipher(cipherEngine);
+ _encoding = encoding;
+ }
+
+ internal string Encrypt(string plain, SecureString key)
+ {
+ var result = BouncyCastleCrypto(ActionEncrypt, plain, key);
+ return Convert.ToBase64String(result);
+ }
+
+ internal string Decrypt(string cipher, SecureString key)
+ {
+ var result = BouncyCastleCrypto(ActionDecrypt, cipher, key);
+ return _encoding.GetString(result);
+ }
+
+
+ private byte[] BouncyCastleCrypto(bool forEncrypt, string input, SecureString key)
+ {
+ try
+ {
+ var inputBytes = Encoding.UTF8.GetBytes(input);
+ var iv = BuildInitializationVector(); //for the sake of demo
+
+ //Set up
+ var cipher = new PaddedBufferedBlockCipher(_blockCipher);
+ var keyParam = new KeyParameter(Encoding.Default.GetBytes(key.ConvertToUnsecureString()));
+ var keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16);
+
+ // Encrypt/Decrypt
+ cipher.Init(forEncrypt, keyParamWithIv);
+ var outputBytes = new byte[cipher.GetOutputSize(inputBytes.Length)];
+ var length = cipher.ProcessBytes(inputBytes, outputBytes, 0);
+ cipher.DoFinal(outputBytes, length); //Do the final block
+ return outputBytes;
+ }
+ catch (CryptoException ex)
+ {
+ throw new CryptoException("", ex);
+ }
+ }
+
+ private byte[] BuildInitializationVector()
+ {
+ var numberOfBytes = _blockCipher.GetBlockSize();
+ var iv = new byte[numberOfBytes];
+ var randomNumberGenerator = new SecureRandom();
+ randomNumberGenerator.NextBytes(iv, 0, numberOfBytes);
+ return iv;
+ }
+ }
+}
\ No newline at end of file
diff --git a/mRemoteV1/Security/ICryptographyProvider.cs b/mRemoteV1/Security/ICryptographyProvider.cs
new file mode 100644
index 00000000..062cbdaf
--- /dev/null
+++ b/mRemoteV1/Security/ICryptographyProvider.cs
@@ -0,0 +1,11 @@
+using System.Security;
+
+namespace mRemoteNG.Security
+{
+ public interface ICryptographyProvider
+ {
+ string Encrypt(string plainText, SecureString encryptionKey);
+
+ string Decrypt(string cipherText, SecureString decryptionKey);
+ }
+}
\ No newline at end of file
diff --git a/mRemoteV1/Security/SecureStringExtensions.cs b/mRemoteV1/Security/SecureStringExtensions.cs
new file mode 100644
index 00000000..05188af6
--- /dev/null
+++ b/mRemoteV1/Security/SecureStringExtensions.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text.RegularExpressions;
+
+namespace mRemoteNG.Security
+{
+ public static class SecureStringExtensions
+ {
+ ///
+ /// Method to marshall a SecureString out of protected memory into a standard String object that is required by most other functions.
+ /// Code initially taken from Fabio Pintos
+ /// Source: https://blogs.msdn.microsoft.com/fpintos/2009/06/12/how-to-properly-convert-securestring-to-string/
+ ///
+ ///
+ ///
+ public static string ConvertToUnsecureString(this SecureString securePassword)
+ {
+ if (securePassword == null)
+ throw new ArgumentNullException(nameof(securePassword));
+
+ var unmanagedString = IntPtr.Zero;
+ try
+ {
+ unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
+ return Marshal.PtrToStringUni(unmanagedString);
+ }
+ finally
+ {
+ Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
+ }
+ }
+
+ public static string ConvertToEncryptedString(this SecureString secureString)
+ {
+ return "TEST ENCRYPTION";
+ }
+
+ public static SecureString ConvertFromEncryptedString(this SecureString secureString, string encryptedString)
+ {
+ var unencryptedString = encryptedString;
+ return unencryptedString.ConvertToSecureString();
+ }
+
+ public static SecureString ConvertToSecureString(this string unsecuredPassword)
+ {
+ if (unsecuredPassword == null)
+ throw new ArgumentNullException(nameof(unsecuredPassword));
+
+ var secureString = new SecureString();
+ foreach (var character in unsecuredPassword.ToCharArray())
+ secureString.AppendChar(character);
+ // ReSharper disable once RedundantAssignment
+ unsecuredPassword = null;
+ return secureString;
+ }
+
+ public static bool IsBase64String(this string s)
+ {
+ s = s.Trim();
+ return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
+ }
+ }
+}
\ No newline at end of file
diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj
index e15acffc..b029aa8c 100644
--- a/mRemoteV1/mRemoteV1.csproj
+++ b/mRemoteV1/mRemoteV1.csproj
@@ -46,6 +46,10 @@
+
+ ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll
+ True
+
..\packages\Geckofx45.45.0.19.0\lib\net40\Geckofx-Core.dll
True
@@ -163,6 +167,10 @@
+
+
+
+
diff --git a/mRemoteV1/packages.config b/mRemoteV1/packages.config
index cfe52582..739bcde6 100644
--- a/mRemoteV1/packages.config
+++ b/mRemoteV1/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file