diff --git a/mRemoteNG.Specs/Utilities/XmlCredentialRepoBuilder.cs b/mRemoteNG.Specs/Utilities/XmlCredentialRepoBuilder.cs index 4fccd05a..27d1f62a 100644 --- a/mRemoteNG.Specs/Utilities/XmlCredentialRepoBuilder.cs +++ b/mRemoteNG.Specs/Utilities/XmlCredentialRepoBuilder.cs @@ -31,7 +31,7 @@ namespace mRemoteNG.Specs.Utilities new CredentialRepositoryConfig(), new CredentialRecordSaver( dataProvider, - new StaticSerializerKeyProviderDecorator, string>(encryptor, encryptor) { Key = EncryptionKey } + encryptor ), new CredentialRecordLoader( dataProvider, decryptor diff --git a/mRemoteNGTests/Config/Serializers/CredentialSerializers/XmlCredentialPasswordEncryptorDecoratorTests.cs b/mRemoteNGTests/Config/Serializers/CredentialSerializers/XmlCredentialPasswordEncryptorDecoratorTests.cs index c7328f2f..379e7759 100644 --- a/mRemoteNGTests/Config/Serializers/CredentialSerializers/XmlCredentialPasswordEncryptorDecoratorTests.cs +++ b/mRemoteNGTests/Config/Serializers/CredentialSerializers/XmlCredentialPasswordEncryptorDecoratorTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security; using System.Xml.Linq; using mRemoteNG.Config.Serializers; using mRemoteNG.Config.Serializers.CredentialSerializer; @@ -17,6 +18,7 @@ namespace mRemoteNGTests.Config.Serializers.CredentialSerializers private const BlockCipherEngines CipherEngine = BlockCipherEngines.Twofish; private const BlockCipherModes CipherMode = BlockCipherModes.EAX; private const int KdfIterations = 2000; + private SecureString _key = "myKey1".ConvertToSecureString(); [SetUp] public void Setup() @@ -30,22 +32,14 @@ namespace mRemoteNGTests.Config.Serializers.CredentialSerializers [Test] public void CantPassNullCredentialList() { - Assert.Throws(() => _sut.Serialize(null)); - } - - [Test] - public void CanSetEncryptionKey() - { - const string newKey = "blah"; - _sut.Key = newKey.ConvertToSecureString(); - Assert.That(_sut.Key.ConvertToUnsecureString(), Is.EqualTo(newKey)); + Assert.Throws(() => _sut.Serialize(null, new SecureString())); } [Test] public void EncryptsPasswordAttributesInXml() { var credList = Substitute.For>(); - var output = _sut.Serialize(credList); + var output = _sut.Serialize(credList, _key); var outputAsXdoc = XDocument.Parse(output); var firstElementPassword = outputAsXdoc.Root?.Descendants().First().FirstAttribute.Value; Assert.That(firstElementPassword, Is.EqualTo("encrypted")); @@ -58,7 +52,7 @@ namespace mRemoteNGTests.Config.Serializers.CredentialSerializers public void SetsRootNodeEncryptionAttributes(string attributeName, object expectedValue) { var credList = Substitute.For>(); - var output = _sut.Serialize(credList); + var output = _sut.Serialize(credList, _key); var outputAsXdoc = XDocument.Parse(output); var authField = outputAsXdoc.Root?.Attribute(attributeName)?.Value; Assert.That(authField, Is.EqualTo(expectedValue.ToString())); diff --git a/mRemoteNGTests/IntegrationTests/XmlCredentialSerializerLifeCycleTests.cs b/mRemoteNGTests/IntegrationTests/XmlCredentialSerializerLifeCycleTests.cs index eb694258..2fffc446 100644 --- a/mRemoteNGTests/IntegrationTests/XmlCredentialSerializerLifeCycleTests.cs +++ b/mRemoteNGTests/IntegrationTests/XmlCredentialSerializerLifeCycleTests.cs @@ -14,7 +14,7 @@ namespace mRemoteNGTests.IntegrationTests { public class XmlCredentialSerializerLifeCycleTests { - private ISerializer, string> _serializer; + private ISecureSerializer, string> _serializer; private ISecureDeserializer> _deserializer; private readonly Guid _id = Guid.NewGuid(); private const string Title = "mycredential1"; @@ -28,7 +28,7 @@ namespace mRemoteNGTests.IntegrationTests var keyProvider = Substitute.For(); keyProvider.GetKey().Returns(_key); var cryptoProvider = new CryptoProviderFactory(BlockCipherEngines.AES, BlockCipherModes.CCM).Build(); - _serializer = new XmlCredentialPasswordEncryptorDecorator(cryptoProvider, new XmlCredentialRecordSerializer()) { Key = _key }; + _serializer = new XmlCredentialPasswordEncryptorDecorator(cryptoProvider, new XmlCredentialRecordSerializer()); _deserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()); } @@ -36,7 +36,7 @@ namespace mRemoteNGTests.IntegrationTests public void WeCanSerializeAndDeserializeXmlCredentials() { var credentials = new[] { new CredentialRecord(), new CredentialRecord() }; - var serializedCredentials = _serializer.Serialize(credentials); + var serializedCredentials = _serializer.Serialize(credentials, _key); var deserializedCredentials = _deserializer.Deserialize(serializedCredentials, _key); Assert.That(deserializedCredentials.Count(), Is.EqualTo(2)); } @@ -82,7 +82,7 @@ namespace mRemoteNGTests.IntegrationTests { new CredentialRecord(_id) {Title = Title, Username = Username, Domain = Domain, Password = _key} }; - var serializedCredentials = _serializer.Serialize(credentials); + var serializedCredentials = _serializer.Serialize(credentials, _key); var deserializedCredentials = _deserializer.Deserialize(serializedCredentials, _key); return deserializedCredentials.First(); } diff --git a/mRemoteV1/App/Initialization/CredsAndConsSetup.cs b/mRemoteV1/App/Initialization/CredsAndConsSetup.cs index 53cac7d0..bfe0cd51 100644 --- a/mRemoteV1/App/Initialization/CredsAndConsSetup.cs +++ b/mRemoteV1/App/Initialization/CredsAndConsSetup.cs @@ -22,7 +22,7 @@ namespace mRemoteNG.App.Initialization { private readonly string _credentialRepoListPath = Path.Combine(SettingsFileInfo.SettingsPath, "credentialRepositories.xml"); private readonly ICredentialRepositoryList _credentialRepositoryList; - private readonly ISerializer, string> _credRepoSerializer; + private readonly ISecureSerializer, string> _credRepoSerializer; private readonly ISecureDeserializer> _credRepoDeserializer; private readonly string _credentialFilePath; @@ -79,7 +79,7 @@ namespace mRemoteNG.App.Initialization var xmlRepoFactory = new XmlCredentialRepositoryFactory(_credRepoSerializer, _credRepoDeserializer); var newCredentialRepository = xmlRepoFactory.Build( - new CredentialRepositoryConfig { Source = _credentialFilePath } + new CredentialRepositoryConfig { Source = _credentialFilePath, Title = "Converted Credentials", TypeName = "Xml"} ); foreach (var credential in harvestedCredentials) @@ -107,7 +107,11 @@ namespace mRemoteNG.App.Initialization Runtime.CredentialProviderCatalog.AddProvider(repository); } Runtime.CredentialProviderCatalog.RepositoriesUpdated += (sender, args) => credRepoListSaver.Save(Runtime.CredentialProviderCatalog.CredentialProviders); - Runtime.CredentialProviderCatalog.CredentialsUpdated += (sender, args) => (sender as ICredentialRepository)?.SaveCredentials(); + Runtime.CredentialProviderCatalog.CredentialsUpdated += (sender, args) => + { + var repo = (sender as ICredentialRepository); + repo?.SaveCredentials(repo.Config.Key); + }; } private void LoadDefaultConnectionCredentials() diff --git a/mRemoteV1/Config/CredentialRecordSaver.cs b/mRemoteV1/Config/CredentialRecordSaver.cs index e758c225..9a9b52d7 100644 --- a/mRemoteV1/Config/CredentialRecordSaver.cs +++ b/mRemoteV1/Config/CredentialRecordSaver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Security; using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Serializers; using mRemoteNG.Credential; @@ -10,9 +11,9 @@ namespace mRemoteNG.Config public class CredentialRecordSaver { private readonly IDataProvider _dataProvider; - private readonly ISerializer, string> _serializer; + private readonly ISecureSerializer, string> _serializer; - public CredentialRecordSaver(IDataProvider dataProvider, ISerializer, string> serializer) + public CredentialRecordSaver(IDataProvider dataProvider, ISecureSerializer, string> serializer) { if (dataProvider == null) throw new ArgumentNullException(nameof(dataProvider)); @@ -23,9 +24,9 @@ namespace mRemoteNG.Config _serializer = serializer; } - public void Save(IEnumerable credentialRecords) + public void Save(IEnumerable credentialRecords, SecureString key) { - var serializedCredentials = _serializer.Serialize(credentialRecords); + var serializedCredentials = _serializer.Serialize(credentialRecords, key); _dataProvider.Save(serializedCredentials); } } diff --git a/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs b/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs index 40642eb6..b0f4f660 100644 --- a/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs +++ b/mRemoteV1/Config/Serializers/CredentialProviderSerializer/CredentialRepositoryListDeserializer.cs @@ -9,10 +9,10 @@ namespace mRemoteNG.Config.Serializers.CredentialProviderSerializer { public class CredentialRepositoryListDeserializer { - private readonly ISerializer, string> _serializer; + private readonly ISecureSerializer, string> _serializer; private readonly ISecureDeserializer> _deserializer; - public CredentialRepositoryListDeserializer(ISerializer, string> serializer, ISecureDeserializer> deserializer) + public CredentialRepositoryListDeserializer(ISecureSerializer, string> serializer, ISecureDeserializer> deserializer) { if (serializer == null) throw new ArgumentNullException(nameof(serializer)); diff --git a/mRemoteV1/Config/Serializers/CredentialSerializer/XmlCredentialPasswordEncryptorDecorator.cs b/mRemoteV1/Config/Serializers/CredentialSerializer/XmlCredentialPasswordEncryptorDecorator.cs index c0527f55..8fc38ed8 100644 --- a/mRemoteV1/Config/Serializers/CredentialSerializer/XmlCredentialPasswordEncryptorDecorator.cs +++ b/mRemoteV1/Config/Serializers/CredentialSerializer/XmlCredentialPasswordEncryptorDecorator.cs @@ -7,21 +7,10 @@ using mRemoteNG.Security; namespace mRemoteNG.Config.Serializers.CredentialSerializer { - public class XmlCredentialPasswordEncryptorDecorator : ISerializer, string>, IHasKey + public class XmlCredentialPasswordEncryptorDecorator : ISecureSerializer, string> { private readonly ISerializer, string> _baseSerializer; private readonly ICryptographyProvider _cryptographyProvider; - private SecureString _encryptionKey = new SecureString(); - - public SecureString Key - { - get { return _encryptionKey; } - set - { - if (value == null) return; - _encryptionKey = value; - } - } public XmlCredentialPasswordEncryptorDecorator(ICryptographyProvider cryptographyProvider, ISerializer, string> baseSerializer) { @@ -35,13 +24,13 @@ namespace mRemoteNG.Config.Serializers.CredentialSerializer } - public string Serialize(IEnumerable credentialRecords) + public string Serialize(IEnumerable credentialRecords, SecureString key) { if (credentialRecords == null) throw new ArgumentNullException(nameof(credentialRecords)); var baseReturn = _baseSerializer.Serialize(credentialRecords); - var encryptedReturn = EncryptPasswordAttributes(baseReturn, _encryptionKey); + var encryptedReturn = EncryptPasswordAttributes(baseReturn, key); return encryptedReturn; } diff --git a/mRemoteV1/Config/Serializers/ISecureSerializer.cs b/mRemoteV1/Config/Serializers/ISecureSerializer.cs new file mode 100644 index 00000000..1cbbaea5 --- /dev/null +++ b/mRemoteV1/Config/Serializers/ISecureSerializer.cs @@ -0,0 +1,9 @@ +using System.Security; + +namespace mRemoteNG.Config.Serializers +{ + public interface ISecureSerializer + { + TOut Serialize(TIn model, SecureString key); + } +} \ No newline at end of file diff --git a/mRemoteV1/Credential/ICredentialRepository.cs b/mRemoteV1/Credential/ICredentialRepository.cs index 0e4bcf09..df55f8fb 100644 --- a/mRemoteV1/Credential/ICredentialRepository.cs +++ b/mRemoteV1/Credential/ICredentialRepository.cs @@ -13,7 +13,7 @@ namespace mRemoteNG.Credential IList CredentialRecords { get; } bool IsLoaded { get; } void LoadCredentials(SecureString key); - void SaveCredentials(); + void SaveCredentials(SecureString key); void UnloadCredentials(); event EventHandler RepositoryConfigUpdated; event EventHandler> CredentialsUpdated; diff --git a/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs b/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs index f977271f..d95f694c 100644 --- a/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs +++ b/mRemoteV1/Credential/Repositories/XmlCredentialRepository.cs @@ -57,10 +57,10 @@ namespace mRemoteNG.Credential.Repositories CredentialRecords.Clear(); } - public void SaveCredentials() + public void SaveCredentials(SecureString key) { - if (!IsLoaded) return; - _credentialRecordSaver.Save(CredentialRecords); + //if (!IsLoaded) return; + _credentialRecordSaver.Save(CredentialRecords, key); } public event EventHandler RepositoryConfigUpdated; diff --git a/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs b/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs index c952a2fa..0cebc69d 100644 --- a/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs +++ b/mRemoteV1/Credential/Repositories/XmlCredentialRepositoryFactory.cs @@ -9,10 +9,10 @@ namespace mRemoteNG.Credential.Repositories { public class XmlCredentialRepositoryFactory { - private readonly ISerializer, string> _serializer; + private readonly ISecureSerializer, string> _serializer; private readonly ISecureDeserializer> _deserializer; - public XmlCredentialRepositoryFactory(ISerializer, string> serializer, ISecureDeserializer> deserializer) + public XmlCredentialRepositoryFactory(ISecureSerializer, string> serializer, ISecureDeserializer> deserializer) { if (serializer == null) throw new ArgumentNullException(nameof(serializer)); diff --git a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs index 937011d8..a0047acd 100644 --- a/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs +++ b/mRemoteV1/UI/Forms/CredentialManagerPages/CredentialRepositoryEditorPages/XmlCredentialRepositoryEditorPage.cs @@ -73,6 +73,7 @@ namespace mRemoteNG.UI.Forms.CredentialManagerPages.CredentialRepositoryEditorPa new CredentialRecordLoader(credRepoDataProvider, credRepoDeserializer) ); _repositoryList.AddProvider(newCredentialRepository); + newCredentialRepository.SaveCredentials(_repositoryConfig.Key); } RaiseNextPageEvent(); } diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index e141e4b6..525ae0a8 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -146,6 +146,7 @@ +