2019-02-19 10:22:32 +01:00
|
|
|
|
/*
|
2020-01-06 15:24:46 +01:00
|
|
|
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
2019-02-19 10:22:32 +01:00
|
|
|
|
*
|
|
|
|
|
* 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/.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-02-22 10:02:34 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2019-02-19 10:22:32 +01:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
2019-02-22 10:02:34 +01:00
|
|
|
|
using SafeExamBrowser.Configuration.ConfigurationData;
|
2019-08-30 09:55:26 +02:00
|
|
|
|
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
|
|
|
|
using SafeExamBrowser.Logging.Contracts;
|
2019-02-19 10:22:32 +01:00
|
|
|
|
|
|
|
|
|
namespace SafeExamBrowser.Configuration.Cryptography
|
|
|
|
|
{
|
2019-02-19 15:54:11 +01:00
|
|
|
|
public class CertificateStore : ICertificateStore
|
2019-02-19 10:22:32 +01:00
|
|
|
|
{
|
2019-02-22 10:02:34 +01:00
|
|
|
|
private ILogger logger;
|
|
|
|
|
|
2019-02-19 10:22:32 +01:00
|
|
|
|
private readonly X509Store[] stores = new[]
|
|
|
|
|
{
|
|
|
|
|
new X509Store(StoreLocation.CurrentUser),
|
|
|
|
|
new X509Store(StoreLocation.LocalMachine),
|
|
|
|
|
new X509Store(StoreName.TrustedPeople)
|
|
|
|
|
};
|
|
|
|
|
|
2019-02-22 10:02:34 +01:00
|
|
|
|
public CertificateStore(ILogger logger)
|
|
|
|
|
{
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-19 10:22:32 +01:00
|
|
|
|
public bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate)
|
|
|
|
|
{
|
|
|
|
|
certificate = default(X509Certificate2);
|
|
|
|
|
|
|
|
|
|
using (var algorithm = new SHA1CryptoServiceProvider())
|
|
|
|
|
{
|
|
|
|
|
foreach (var store in stores)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
store.Open(OpenFlags.ReadOnly);
|
|
|
|
|
|
|
|
|
|
foreach (var current in store.Certificates)
|
|
|
|
|
{
|
|
|
|
|
var publicKey = current.PublicKey.EncodedKeyValue.RawData;
|
|
|
|
|
var publicKeyHash = algorithm.ComputeHash(publicKey);
|
|
|
|
|
|
|
|
|
|
if (publicKeyHash.SequenceEqual(keyHash))
|
|
|
|
|
{
|
|
|
|
|
certificate = current;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
store.Close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-02-22 10:02:34 +01:00
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-02-19 10:22:32 +01:00
|
|
|
|
}
|
|
|
|
|
}
|