/* * Copyright (c) 2024 ETH Zürich, IT Services * * 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.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using SafeExamBrowser.Configuration.ConfigurationData; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Configuration.Cryptography { public class CertificateStore : ICertificateStore { private ILogger logger; private readonly X509Store[] stores = new[] { new X509Store(StoreLocation.CurrentUser), new X509Store(StoreLocation.LocalMachine), new X509Store(StoreName.TrustedPeople) }; public CertificateStore(ILogger logger) { this.logger = logger; } 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; } public void ExtractAndImportIdentities(IDictionary data) { const int IDENTITY_CERTIFICATE = 1; var hasCertificates = data.TryGetValue(Keys.Network.Certificates.EmbeddedCertificates, out var value); if (hasCertificates && value is IList> certificates) { var toRemove = new List>(); 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𝈒ah𝉇t𝈁a𝉈Hai1972", 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(); } } } }