SEBWIN-296: Implemented unit tests for configuration repository.
This commit is contained in:
parent
f8cb521a1e
commit
5016c14ff3
7 changed files with 333 additions and 90 deletions
|
@ -7,11 +7,16 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Configuration.ConfigurationData;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
||||
using SafeExamBrowser.Contracts.Configuration.DataFormats;
|
||||
using SafeExamBrowser.Contracts.Configuration.DataResources;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.UnitTests
|
||||
|
@ -20,19 +25,252 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
|||
public class ConfigurationRepositoryTests
|
||||
{
|
||||
private ConfigurationRepository sut;
|
||||
private Mock<IDataParser> binaryParser;
|
||||
private Mock<IDataSerializer> binarySerializer;
|
||||
private Mock<ICertificateStore> certificateStore;
|
||||
private Mock<IResourceLoader> fileLoader;
|
||||
private Mock<IResourceSaver> fileSaver;
|
||||
private Mock<IHashAlgorithm> hashAlgorithm;
|
||||
private Mock<IModuleLogger> logger;
|
||||
private Mock<IResourceLoader> networkLoader;
|
||||
private Mock<IDataParser> xmlParser;
|
||||
private Mock<IDataSerializer> xmlSerializer;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var executablePath = Assembly.GetExecutingAssembly().Location;
|
||||
var hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||
var logger = new Mock<IModuleLogger>();
|
||||
|
||||
sut = new ConfigurationRepository(hashAlgorithm.Object, logger.Object, executablePath, string.Empty, string.Empty, string.Empty);
|
||||
binaryParser = new Mock<IDataParser>();
|
||||
binarySerializer = new Mock<IDataSerializer>();
|
||||
certificateStore = new Mock<ICertificateStore>();
|
||||
fileLoader = new Mock<IResourceLoader>();
|
||||
fileSaver = new Mock<IResourceSaver>();
|
||||
hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||
logger = new Mock<IModuleLogger>();
|
||||
networkLoader = new Mock<IResourceLoader>();
|
||||
xmlParser = new Mock<IDataParser>();
|
||||
xmlSerializer = new Mock<IDataSerializer>();
|
||||
|
||||
fileLoader.Setup(f => f.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
|
||||
fileSaver.Setup(f => f.CanSave(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
|
||||
networkLoader.Setup(n => n.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.Scheme.Equals("http") || u.Scheme.Equals("seb"));
|
||||
|
||||
sut = new ConfigurationRepository(certificateStore.Object, hashAlgorithm.Object, logger.Object, executablePath, string.Empty, string.Empty, string.Empty);
|
||||
sut.InitializeAppConfig();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyInitializeSessionConfiguration()
|
||||
public void ConfigureClient_MustWorkAsExpected()
|
||||
{
|
||||
var stream = new MemoryStream() as Stream;
|
||||
var password = new PasswordParameters { Password = "test123" };
|
||||
var parseResult = new ParseResult
|
||||
{
|
||||
Format = FormatType.Binary,
|
||||
RawData = new Dictionary<string, object>(),
|
||||
Status = LoadStatus.Success
|
||||
};
|
||||
var serializeResult = new SerializeResult
|
||||
{
|
||||
Data = new MemoryStream(),
|
||||
Status = SaveStatus.Success
|
||||
};
|
||||
|
||||
RegisterModules();
|
||||
|
||||
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
|
||||
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<PasswordParameters>())).Returns(serializeResult);
|
||||
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
|
||||
|
||||
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||
|
||||
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
|
||||
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Once);
|
||||
binarySerializer.Verify(b => b.TrySerialize(
|
||||
It.IsAny<Dictionary<string, object>>(),
|
||||
It.Is<PasswordParameters>(p => p.IsHash == true && p.Password == string.Empty)), Times.Once);
|
||||
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Once);
|
||||
|
||||
Assert.AreEqual(SaveStatus.Success, status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigureClient_MustKeepSameEncryptionAccordingToConfiguration()
|
||||
{
|
||||
var stream = new MemoryStream() as Stream;
|
||||
var password = new PasswordParameters { Password = "test123" };
|
||||
var parseResult = new ParseResult
|
||||
{
|
||||
Encryption = new PublicKeyParameters
|
||||
{
|
||||
InnerEncryption = password,
|
||||
SymmetricEncryption = true
|
||||
},
|
||||
Format = FormatType.Binary,
|
||||
RawData = new Dictionary<string, object> { { Keys.ConfigurationFile.KeepClientConfigEncryption, true } },
|
||||
Status = LoadStatus.Success
|
||||
};
|
||||
var serializeResult = new SerializeResult
|
||||
{
|
||||
Data = new MemoryStream(),
|
||||
Status = SaveStatus.Success
|
||||
};
|
||||
|
||||
RegisterModules();
|
||||
|
||||
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
|
||||
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>())).Returns(serializeResult);
|
||||
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
|
||||
|
||||
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||
|
||||
binarySerializer.Verify(b => b.TrySerialize(
|
||||
It.IsAny<Dictionary<string, object>>(),
|
||||
It.Is<PublicKeyParameters>(p => p.InnerEncryption == password && p.SymmetricEncryption)), Times.Once);
|
||||
|
||||
Assert.AreEqual(SaveStatus.Success, status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigureClient_MustAbortProcessOnError()
|
||||
{
|
||||
var stream = new MemoryStream() as Stream;
|
||||
var password = new PasswordParameters { Password = "test123" };
|
||||
var parseResult = new ParseResult
|
||||
{
|
||||
Format = FormatType.Binary,
|
||||
RawData = new Dictionary<string, object>(),
|
||||
Status = LoadStatus.Success
|
||||
};
|
||||
var serializeResult = new SerializeResult
|
||||
{
|
||||
Data = new MemoryStream(),
|
||||
Status = SaveStatus.Success
|
||||
};
|
||||
|
||||
RegisterModules();
|
||||
|
||||
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
|
||||
|
||||
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||
|
||||
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
|
||||
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Never);
|
||||
binarySerializer.Verify(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>()), Times.Never);
|
||||
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Never);
|
||||
|
||||
Assert.AreEqual(SaveStatus.UnexpectedError, status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryLoad_MustWorkAsExpected()
|
||||
{
|
||||
var stream = new MemoryStream() as Stream;
|
||||
var parseResult = new ParseResult { RawData = new Dictionary<string, object>(), Status = LoadStatus.Success };
|
||||
|
||||
RegisterModules();
|
||||
|
||||
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||
|
||||
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||
fileLoader.Verify(f => f.TryLoad(It.IsAny<Uri>(), out stream), Times.Never);
|
||||
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||
binaryParser.Verify(b => b.CanParse(It.IsAny<Stream>()), Times.Once);
|
||||
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
|
||||
xmlParser.Verify(x => x.CanParse(It.IsAny<Stream>()), Times.AtMostOnce);
|
||||
xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
|
||||
|
||||
Assert.AreEqual(LoadStatus.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryLoad_MustReportPasswordNeed()
|
||||
{
|
||||
var stream = new MemoryStream() as Stream;
|
||||
var parseResult = new ParseResult { Status = LoadStatus.PasswordNeeded };
|
||||
|
||||
RegisterModules();
|
||||
|
||||
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||
|
||||
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
Assert.AreEqual(LoadStatus.PasswordNeeded, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryLoad_MustNotFailToIfNoLoaderRegistered()
|
||||
{
|
||||
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||
|
||||
sut.Register(fileLoader.Object);
|
||||
sut.Register(networkLoader.Object);
|
||||
|
||||
result = sut.TryLoadSettings(new Uri("ftp://www.blubb.org"), out _);
|
||||
|
||||
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||
|
||||
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryLoad_MustNotFailIfNoParserRegistered()
|
||||
{
|
||||
var data = default(Stream);
|
||||
|
||||
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
|
||||
sut.Register(networkLoader.Object);
|
||||
|
||||
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out data), Times.Once);
|
||||
|
||||
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TryLoad_MustNotFailInCaseOfUnexpectedError()
|
||||
{
|
||||
var data = default(Stream);
|
||||
|
||||
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Throws<Exception>();
|
||||
sut.Register(networkLoader.Object);
|
||||
|
||||
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
Assert.AreEqual(LoadStatus.UnexpectedError, result);
|
||||
|
||||
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
|
||||
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
|
||||
sut.Register(binaryParser.Object);
|
||||
|
||||
result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||
|
||||
Assert.AreEqual(LoadStatus.UnexpectedError, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustInitializeSessionConfiguration()
|
||||
{
|
||||
var appConfig = sut.InitializeAppConfig();
|
||||
var configuration = sut.InitializeSessionConfiguration();
|
||||
|
@ -44,7 +282,7 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
|||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyUpdateAppConfig()
|
||||
public void MustUpdateAppConfig()
|
||||
{
|
||||
var appConfig = sut.InitializeAppConfig();
|
||||
var clientAddress = appConfig.ClientAddress;
|
||||
|
@ -64,7 +302,7 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
|||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustCorrectlyUpdateSessionConfiguration()
|
||||
public void MustUpdateSessionConfiguration()
|
||||
{
|
||||
var appConfig = sut.InitializeAppConfig();
|
||||
var firstSession = sut.InitializeSessionConfiguration();
|
||||
|
@ -76,5 +314,16 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
|||
Assert.AreNotEqual(secondSession.Id, thirdSession.Id);
|
||||
Assert.AreNotEqual(secondSession.StartupToken, thirdSession.StartupToken);
|
||||
}
|
||||
|
||||
private void RegisterModules()
|
||||
{
|
||||
sut.Register(binaryParser.Object);
|
||||
sut.Register(binarySerializer.Object);
|
||||
sut.Register(fileLoader.Object);
|
||||
sut.Register(fileSaver.Object);
|
||||
sut.Register(networkLoader.Object);
|
||||
sut.Register(xmlParser.Object);
|
||||
sut.Register(xmlSerializer.Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||
{
|
||||
internal class CertificateImporter
|
||||
{
|
||||
private ILogger logger;
|
||||
|
||||
internal CertificateImporter(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
internal void ExtractAndImportIdentities(IDictionary<string, object> data)
|
||||
{
|
||||
const int IDENTITY_CERTIFICATE = 1;
|
||||
var hasCertificates = data.TryGetValue(Keys.Network.Certificates.EmbeddedCertificates, out var value);
|
||||
|
||||
if (hasCertificates && value is IList<IDictionary<string, object>> certificates)
|
||||
{
|
||||
var toRemove = new List<IDictionary<string, object>>();
|
||||
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
var hasData = certificate.TryGetValue(Keys.Network.Certificates.CertificateData, out var dataValue);
|
||||
var hasType = certificate.TryGetValue(Keys.Network.Certificates.CertificateType, out var typeValue);
|
||||
var isIdentity = typeValue is int type && type == IDENTITY_CERTIFICATE;
|
||||
|
||||
if (hasData && hasType && isIdentity && dataValue is byte[] certificateData)
|
||||
{
|
||||
ImportIdentityCertificate(certificateData, new X509Store(StoreLocation.CurrentUser));
|
||||
ImportIdentityCertificate(certificateData, new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine));
|
||||
|
||||
toRemove.Add(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.ForEach(c => certificates.Remove(c));
|
||||
}
|
||||
}
|
||||
|
||||
internal void ImportIdentityCertificate(byte[] certificateData, X509Store store)
|
||||
{
|
||||
try
|
||||
{
|
||||
var certificate = new X509Certificate2();
|
||||
|
||||
certificate.Import(certificateData, "Di𝈭l𝈖Ch𝈒aht𝈁aHai1972", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(certificate);
|
||||
|
||||
logger.Info($"Successfully imported identity certificate into {store.Location}.{store.Name}.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to import identity certificate into {store.Location}.{store.Name}!", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ namespace SafeExamBrowser.Configuration
|
|||
{
|
||||
public class ConfigurationRepository : IConfigurationRepository
|
||||
{
|
||||
private CertificateImporter certificateImporter;
|
||||
private ICertificateStore certificateStore;
|
||||
private IList<IDataParser> dataParsers;
|
||||
private IList<IDataSerializer> dataSerializers;
|
||||
private DataMapper dataMapper;
|
||||
|
@ -33,6 +33,7 @@ namespace SafeExamBrowser.Configuration
|
|||
private IList<IResourceSaver> resourceSavers;
|
||||
|
||||
public ConfigurationRepository(
|
||||
ICertificateStore certificateStore,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
IModuleLogger logger,
|
||||
string executablePath,
|
||||
|
@ -40,10 +41,10 @@ namespace SafeExamBrowser.Configuration
|
|||
string programTitle,
|
||||
string programVersion)
|
||||
{
|
||||
this.certificateStore = certificateStore;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.logger = logger;
|
||||
|
||||
certificateImporter = new CertificateImporter(logger.CloneFor(nameof(CertificateImporter)));
|
||||
dataParsers = new List<IDataParser>();
|
||||
dataSerializers = new List<IDataSerializer>();
|
||||
dataMapper = new DataMapper();
|
||||
|
@ -64,7 +65,7 @@ namespace SafeExamBrowser.Configuration
|
|||
{
|
||||
TryParseData(data, out var encryption, out var format, out var rawData, password);
|
||||
|
||||
certificateImporter.ExtractAndImportIdentities(rawData);
|
||||
certificateStore.ExtractAndImportIdentities(rawData);
|
||||
encryption = DetermineEncryptionForClientConfiguration(rawData, encryption);
|
||||
|
||||
var status = TrySerializeData(rawData, format, out var serialized, encryption);
|
||||
|
|
|
@ -6,15 +6,21 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using SafeExamBrowser.Configuration.ConfigurationData;
|
||||
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.Cryptography
|
||||
{
|
||||
public class CertificateStore : ICertificateStore
|
||||
{
|
||||
private ILogger logger;
|
||||
|
||||
private readonly X509Store[] stores = new[]
|
||||
{
|
||||
new X509Store(StoreLocation.CurrentUser),
|
||||
|
@ -22,6 +28,11 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
new X509Store(StoreName.TrustedPeople)
|
||||
};
|
||||
|
||||
public CertificateStore(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate)
|
||||
{
|
||||
certificate = default(X509Certificate2);
|
||||
|
@ -56,5 +67,56 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ExtractAndImportIdentities(IDictionary<string, object> data)
|
||||
{
|
||||
const int IDENTITY_CERTIFICATE = 1;
|
||||
var hasCertificates = data.TryGetValue(Keys.Network.Certificates.EmbeddedCertificates, out var value);
|
||||
|
||||
if (hasCertificates && value is IList<IDictionary<string, object>> certificates)
|
||||
{
|
||||
var toRemove = new List<IDictionary<string, object>>();
|
||||
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
var hasData = certificate.TryGetValue(Keys.Network.Certificates.CertificateData, out var dataValue);
|
||||
var hasType = certificate.TryGetValue(Keys.Network.Certificates.CertificateType, out var typeValue);
|
||||
var isIdentity = typeValue is int type && type == IDENTITY_CERTIFICATE;
|
||||
|
||||
if (hasData && hasType && isIdentity && dataValue is byte[] certificateData)
|
||||
{
|
||||
ImportIdentityCertificate(certificateData, new X509Store(StoreLocation.CurrentUser));
|
||||
ImportIdentityCertificate(certificateData, new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine));
|
||||
|
||||
toRemove.Add(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.ForEach(c => certificates.Remove(c));
|
||||
}
|
||||
}
|
||||
|
||||
private void ImportIdentityCertificate(byte[] certificateData, X509Store store)
|
||||
{
|
||||
try
|
||||
{
|
||||
var certificate = new X509Certificate2();
|
||||
|
||||
certificate.Import(certificateData, "Di𝈭l𝈖Ch𝈒aht𝈁aHai1972", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(certificate);
|
||||
|
||||
logger.Info($"Successfully imported identity certificate into {store.Location}.{store.Name}.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to import identity certificate into {store.Location}.{store.Name}!", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ConfigurationData\CertificateImporter.cs" />
|
||||
<Compile Include="ConfigurationData\Keys.cs" />
|
||||
<Compile Include="ConfigurationData\DataValues.cs" />
|
||||
<Compile Include="Cryptography\CertificateStore.cs" />
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to load certificates installed on the computer.
|
||||
/// Provides functionality related to certificates installed on the computer.
|
||||
/// </summary>
|
||||
public interface ICertificateStore
|
||||
{
|
||||
|
@ -20,5 +21,10 @@ namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
|||
/// Returns <c>true</c> if the certificate was found, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all identity certificates from the given configuration data and installs them on the computer.
|
||||
/// </summary>
|
||||
void ExtractAndImportIdentities(IDictionary<string, object> data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,15 +116,17 @@ namespace SafeExamBrowser.Runtime
|
|||
var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright;
|
||||
var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||
var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||
|
||||
var certificateStore = new CertificateStore(ModuleLogger(nameof(CertificateStore)));
|
||||
var compressor = new GZipCompressor(ModuleLogger(nameof(GZipCompressor)));
|
||||
var passwordEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption)));
|
||||
var publicKeyEncryption = new PublicKeyEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeyEncryption)));
|
||||
var symmetricEncryption = new PublicKeySymmetricEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption);
|
||||
var publicKeyEncryption = new PublicKeyEncryption(certificateStore, ModuleLogger(nameof(PublicKeyEncryption)));
|
||||
var symmetricEncryption = new PublicKeySymmetricEncryption(certificateStore, ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption);
|
||||
var repositoryLogger = ModuleLogger(nameof(ConfigurationRepository));
|
||||
var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser)));
|
||||
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
|
||||
|
||||
configuration = new ConfigurationRepository(new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion);
|
||||
configuration = new ConfigurationRepository(certificateStore, new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion);
|
||||
appConfig = configuration.InitializeAppConfig();
|
||||
|
||||
configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser));
|
||||
|
|
Loading…
Reference in a new issue