changed interface for loading credentials from a repo to get around issues with providing a key

This commit is contained in:
David Sparer
2017-04-06 09:16:14 -06:00
parent 224d2987db
commit e9d41fd01b
15 changed files with 44 additions and 132 deletions

View File

@@ -34,7 +34,7 @@ namespace mRemoteNG.Specs.Utilities
new StaticSerializerKeyProviderDecorator<IEnumerable<ICredentialRecord>, string>(encryptor, encryptor) { Key = EncryptionKey }
), new CredentialRecordLoader(
dataProvider,
new StaticDeserializerKeyProviderDecorator<string, IEnumerable<ICredentialRecord>>(decryptor, decryptor) { Key = EncryptionKey }
decryptor
)
);
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Security;
using mRemoteNG.Config;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers;
@@ -12,29 +13,30 @@ namespace mRemoteNGTests.Config
{
private CredentialRecordLoader _credentialRecordLoader;
private IDataProvider<string> _dataProvider;
private IDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
[SetUp]
public void Setup()
{
_dataProvider = Substitute.For<IDataProvider<string>>();
_deserializer = Substitute.For<IDeserializer<string, IEnumerable<ICredentialRecord>>>();
_deserializer = Substitute.For<ISecureDeserializer<string, IEnumerable<ICredentialRecord>>>();
_credentialRecordLoader = new CredentialRecordLoader(_dataProvider, _deserializer);
}
[Test]
public void LoadsFromDataProvider()
{
_credentialRecordLoader.Load();
_credentialRecordLoader.Load(new SecureString());
_dataProvider.Received(1).Load();
}
[Test]
public void DeserializesDataFromDataProvider()
{
var key = new SecureString();
_dataProvider.Load().Returns("mydata");
_credentialRecordLoader.Load();
_deserializer.Received(1).Deserialize("mydata");
_credentialRecordLoader.Load(key);
_deserializer.Received(1).Deserialize("mydata", key);
}
}
}

View File

@@ -17,14 +17,14 @@ namespace mRemoteNGTests.Config.Serializers.CredentialSerializers
public void Setup()
{
var baseDeserializer = new XmlCredentialRecordDeserializer();
_sut = new XmlCredentialPasswordDecryptorDecorator(baseDeserializer) {Key = _decryptionKey};
_sut = new XmlCredentialPasswordDecryptorDecorator(baseDeserializer);
}
[Test]
public void OutputedCredentialHasDecryptedPassword()
{
var xml = GenerateXml();
var output = _sut.Deserialize(xml);
var output = _sut.Deserialize(xml, _decryptionKey);
Assert.That(output.First().Password.ConvertToUnsecureString(), Is.EqualTo(_unencryptedPassword));
}

View File

@@ -1,41 +0,0 @@
using System.Security;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using NSubstitute;
using NUnit.Framework;
namespace mRemoteNGTests.Credential
{
public class StaticDeserializerKeyProviderDecoratorTests
{
private StaticDeserializerKeyProviderDecorator<object, string> _deserializerKeyProvider;
private IDeserializer<object, string> _baseDeserializer;
private IHasKey _objThatNeedsKey;
private readonly SecureString _key = "someKey1".ConvertToSecureString();
[SetUp]
public void Setup()
{
_objThatNeedsKey = Substitute.For<IHasKey>();
_baseDeserializer = Substitute.For<IDeserializer<object, string>>();
_deserializerKeyProvider = new StaticDeserializerKeyProviderDecorator<object, string>(_objThatNeedsKey, _baseDeserializer) { Key = _key };
}
[Test]
public void CallsBaseSerializer()
{
var creds = new[] { Substitute.For<ICredentialRecord>() };
_deserializerKeyProvider.Deserialize(creds);
_baseDeserializer.Received().Deserialize(creds);
}
[Test]
public void PassesOnItsKey()
{
var creds = new[] { Substitute.For<ICredentialRecord>() };
_deserializerKeyProvider.Deserialize(creds);
Assert.That(_objThatNeedsKey.Key, Is.EqualTo(_deserializerKeyProvider.Key));
}
}
}

View File

@@ -15,21 +15,21 @@ namespace mRemoteNGTests.IntegrationTests
public class XmlCredentialSerializerLifeCycleTests
{
private ISerializer<IEnumerable<ICredentialRecord>, string> _serializer;
private IDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private readonly Guid _id = Guid.NewGuid();
private const string Title = "mycredential1";
private const string Username = "user1";
private const string Domain = "domain1";
private readonly SecureString _password = "myPassword1!".ConvertToSecureString();
private readonly SecureString _key = "myPassword1!".ConvertToSecureString();
[SetUp]
public void Setup()
{
var keyProvider = Substitute.For<IKeyProvider>();
keyProvider.GetKey().Returns(_password);
keyProvider.GetKey().Returns(_key);
var cryptoProvider = new CryptoProviderFactory(BlockCipherEngines.AES, BlockCipherModes.CCM).Build();
_serializer = new XmlCredentialPasswordEncryptorDecorator(cryptoProvider, new XmlCredentialRecordSerializer()) { Key = _password };
_deserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer()) { Key = _password };
_serializer = new XmlCredentialPasswordEncryptorDecorator(cryptoProvider, new XmlCredentialRecordSerializer()) { Key = _key };
_deserializer = new XmlCredentialPasswordDecryptorDecorator(new XmlCredentialRecordDeserializer());
}
[Test]
@@ -37,7 +37,7 @@ namespace mRemoteNGTests.IntegrationTests
{
var credentials = new[] { new CredentialRecord(), new CredentialRecord() };
var serializedCredentials = _serializer.Serialize(credentials);
var deserializedCredentials = _deserializer.Deserialize(serializedCredentials);
var deserializedCredentials = _deserializer.Deserialize(serializedCredentials, _key);
Assert.That(deserializedCredentials.Count(), Is.EqualTo(2));
}
@@ -73,17 +73,17 @@ namespace mRemoteNGTests.IntegrationTests
public void PasswordConsistentAfterSerialization()
{
var sut = SerializeThenDeserializeCredential();
Assert.That(sut.Password.ConvertToUnsecureString(), Is.EqualTo(_password.ConvertToUnsecureString()));
Assert.That(sut.Password.ConvertToUnsecureString(), Is.EqualTo(_key.ConvertToUnsecureString()));
}
private ICredentialRecord SerializeThenDeserializeCredential()
{
var credentials = new[]
{
new CredentialRecord(_id) {Title = Title, Username = Username, Domain = Domain, Password = _password}
new CredentialRecord(_id) {Title = Title, Username = Username, Domain = Domain, Password = _key}
};
var serializedCredentials = _serializer.Serialize(credentials);
var deserializedCredentials = _deserializer.Deserialize(serializedCredentials);
var deserializedCredentials = _deserializer.Deserialize(serializedCredentials, _key);
return deserializedCredentials.First();
}
}

View File

@@ -137,7 +137,6 @@
<Compile Include="Credential\CredentialDomainUserComparerTests.cs" />
<Compile Include="Credential\CredentialRepositoryListTests.cs" />
<Compile Include="Credential\CredentialRecordTests.cs" />
<Compile Include="Credential\StaticDeserializerKeyProviderDecoratorTests.cs" />
<Compile Include="Credential\StaticSerializerKeyProviderDecoratorTests.cs" />
<Compile Include="IntegrationTests\XmlCredentialSerializerLifeCycleTests.cs" />
<Compile Include="IntegrationTests\XmlSerializationLifeCycleTests.cs" />

View File

@@ -23,7 +23,7 @@ namespace mRemoteNG.App.Initialization
private readonly string _credentialRepoListPath = Path.Combine(SettingsFileInfo.SettingsPath, "credentialRepositories.xml");
private readonly ICredentialRepositoryList _credentialRepositoryList;
private readonly ISerializer<IEnumerable<ICredentialRecord>, string> _credRepoSerializer;
private readonly IDeserializer<string, IEnumerable<ICredentialRecord>> _credRepoDeserializer;
private readonly ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _credRepoDeserializer;
private readonly string _credentialFilePath;
public CredsAndConsSetup(ICredentialRepositoryList credentialRepositoryList, string credentialFilePath)

View File

@@ -11,9 +11,9 @@ namespace mRemoteNG.Config
public class CredentialRecordLoader
{
private readonly IDataProvider<string> _dataProvider;
private readonly IDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private readonly ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
public CredentialRecordLoader(IDataProvider<string> dataProvider, IDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
public CredentialRecordLoader(IDataProvider<string> dataProvider, ISecureDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
{
if (dataProvider == null)
throw new ArgumentNullException(nameof(dataProvider));
@@ -24,10 +24,10 @@ namespace mRemoteNG.Config
_deserializer = deserializer;
}
public IEnumerable<ICredentialRecord> Load()
public IEnumerable<ICredentialRecord> Load(SecureString key)
{
var serializedCredentials = _dataProvider.Load();
return _deserializer.Deserialize(serializedCredentials);
return _deserializer.Deserialize(serializedCredentials, key);
}
}
}

View File

@@ -10,9 +10,9 @@ namespace mRemoteNG.Config.Serializers.CredentialProviderSerializer
public class CredentialRepositoryListDeserializer
{
private readonly ISerializer<IEnumerable<ICredentialRecord>, string> _serializer;
private readonly IDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private readonly ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
public CredentialRepositoryListDeserializer(ISerializer<IEnumerable<ICredentialRecord>, string> serializer, IDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
public CredentialRepositoryListDeserializer(ISerializer<IEnumerable<ICredentialRecord>, string> serializer, ISecureDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
{
if (serializer == null)
throw new ArgumentNullException(nameof(serializer));

View File

@@ -3,25 +3,13 @@ using System.Collections.Generic;
using System.Security;
using System.Xml.Linq;
using mRemoteNG.Credential;
using mRemoteNG.Security;
using mRemoteNG.Security.Factories;
namespace mRemoteNG.Config.Serializers.CredentialSerializer
{
public class XmlCredentialPasswordDecryptorDecorator : IDeserializer<string, IEnumerable<ICredentialRecord>>, IHasKey
public class XmlCredentialPasswordDecryptorDecorator : ISecureDeserializer<string, IEnumerable<ICredentialRecord>>
{
private readonly IDeserializer<string, IEnumerable<ICredentialRecord>> _baseDeserializer;
private SecureString _decryptionKey = new SecureString();
public SecureString Key
{
get { return _decryptionKey; }
set
{
if (value == null) return;
_decryptionKey = value;
}
}
public XmlCredentialPasswordDecryptorDecorator(IDeserializer<string, IEnumerable<ICredentialRecord>> baseDeserializer)
{
@@ -31,13 +19,13 @@ namespace mRemoteNG.Config.Serializers.CredentialSerializer
_baseDeserializer = baseDeserializer;
}
public IEnumerable<ICredentialRecord> Deserialize(string xml)
public IEnumerable<ICredentialRecord> Deserialize(string xml, SecureString key)
{
var decryptedXml = DecryptPasswords(xml);
var decryptedXml = DecryptPasswords(xml, key);
return _baseDeserializer.Deserialize(decryptedXml);
}
private string DecryptPasswords(string xml)
private string DecryptPasswords(string xml, SecureString key)
{
var xdoc = XDocument.Parse(xml);
var cryptoProvider = new CryptoProviderFactoryFromXml(xdoc.Root).Build();
@@ -45,7 +33,7 @@ namespace mRemoteNG.Config.Serializers.CredentialSerializer
{
var passwordAttribute = credentialElement.Attribute("Password");
if (passwordAttribute == null) continue;
var decryptedPassword = cryptoProvider.Decrypt(passwordAttribute.Value, _decryptionKey);
var decryptedPassword = cryptoProvider.Decrypt(passwordAttribute.Value, key);
passwordAttribute.SetValue(decryptedPassword);
}
return xdoc.ToString();

View File

@@ -0,0 +1,9 @@
using System.Security;
namespace mRemoteNG.Config.Serializers
{
public interface ISecureDeserializer<in TIn, out TOut>
{
TOut Deserialize(TIn serializedData, SecureString key);
}
}

View File

@@ -10,9 +10,9 @@ namespace mRemoteNG.Credential.Repositories
public class CredentialRepositoryFactory
{
private readonly ISerializer<IEnumerable<ICredentialRecord>, string> _serializer;
private readonly IDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
private readonly ISecureDeserializer<string, IEnumerable<ICredentialRecord>> _deserializer;
public CredentialRepositoryFactory(ISerializer<IEnumerable<ICredentialRecord>, string> serializer, IDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
public CredentialRepositoryFactory(ISerializer<IEnumerable<ICredentialRecord>, string> serializer, ISecureDeserializer<string, IEnumerable<ICredentialRecord>> deserializer)
{
if (serializer == null)
throw new ArgumentNullException(nameof(serializer));

View File

@@ -3,11 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using mRemoteNG.Config;
using mRemoteNG.Config.DataProviders;
using mRemoteNG.Config.Serializers.CredentialSerializer;
using mRemoteNG.Security;
using mRemoteNG.Tools.CustomCollections;
using mRemoteNG.UI.Forms;
namespace mRemoteNG.Credential.Repositories
{
@@ -39,7 +35,7 @@ namespace mRemoteNG.Credential.Repositories
public void LoadCredentials()
{
var credentials = _credentialRecordLoader.Load();
var credentials = _credentialRecordLoader.Load(Config.Key);
foreach (var newCredential in credentials)
{
if (ThisIsADuplicateCredentialRecord(newCredential)) continue;

View File

@@ -1,41 +0,0 @@
using System;
using System.Security;
using mRemoteNG.Config.Serializers;
using mRemoteNG.Security;
namespace mRemoteNG.Credential
{
public class StaticDeserializerKeyProviderDecorator<TIn, TOut> : IDeserializer<TIn, TOut>, IHasKey
{
private readonly IDeserializer<TIn, TOut> _baseDeserializer;
private readonly IHasKey _objThatNeedsKey;
private SecureString _key = new SecureString();
public SecureString Key
{
get { return _key; }
set
{
if (value == null) return;
_key = value;
}
}
public StaticDeserializerKeyProviderDecorator(IHasKey objThatNeedsKey, IDeserializer<TIn, TOut> baseDeserializer)
{
if (objThatNeedsKey == null)
throw new ArgumentNullException(nameof(objThatNeedsKey));
if (baseDeserializer == null)
throw new ArgumentNullException(nameof(baseDeserializer));
_objThatNeedsKey = objThatNeedsKey;
_baseDeserializer = baseDeserializer;
}
public TOut Deserialize(TIn serializedData)
{
_objThatNeedsKey.Key = Key;
return _baseDeserializer.Deserialize(serializedData);
}
}
}

View File

@@ -145,6 +145,7 @@
<Compile Include="Config\CredentialRepositoryListLoader.cs" />
<Compile Include="Config\Serializers\CredentialSerializer\XmlCredentialPasswordDecryptorDecorator.cs" />
<Compile Include="Config\Serializers\CredentialSerializer\XmlCredentialPasswordEncryptorDecorator.cs" />
<Compile Include="Config\Serializers\ISecureDeserializer.cs" />
<Compile Include="Config\Serializers\MiscSerializers\ActiveDirectoryDeserializer.cs" />
<Compile Include="Config\Serializers\CredentialProviderSerializer\CredentialRepositoryListSerializer.cs" />
<Compile Include="Config\Serializers\MiscSerializers\CsvConnectionsSerializerMremotengFormat.cs" />
@@ -215,7 +216,6 @@
<Compile Include="Credential\CredentialChangedEventArgs.cs" />
<Compile Include="Credential\CredentialDeletionMsgBoxConfirmer.cs" />
<Compile Include="Credential\CredentialDomainUserComparer.cs" />
<Compile Include="Credential\StaticDeserializerKeyProviderDecorator.cs" />
<Compile Include="Credential\StaticSerializerKeyProviderDecorator.cs" />
<Compile Include="Security\Factories\CryptoProviderFactoryFromSettings.cs" />
<Compile Include="Security\Factories\LegacyInsecureCryptoProviderFactory.cs" />