143 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018 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.IO;
 | |
| using System.Linq;
 | |
| using System.Security.Cryptography;
 | |
| using System.Security.Cryptography.X509Certificates;
 | |
| using SafeExamBrowser.Contracts.Configuration;
 | |
| using SafeExamBrowser.Contracts.Logging;
 | |
| 
 | |
| namespace SafeExamBrowser.Configuration.Cryptography
 | |
| {
 | |
| 	internal class PublicKeyHashEncryption
 | |
| 	{
 | |
| 		protected const int PUBLIC_KEY_HASH_SIZE = 20;
 | |
| 
 | |
| 		protected ILogger logger;
 | |
| 
 | |
| 		internal PublicKeyHashEncryption(ILogger logger)
 | |
| 		{
 | |
| 			this.logger = logger;
 | |
| 		}
 | |
| 
 | |
| 		internal virtual LoadStatus Decrypt(Stream data, out Stream decrypted, out X509Certificate2 certificate)
 | |
| 		{
 | |
| 			var keyHash = ParsePublicKeyHash(data);
 | |
| 			var found = TryGetCertificateWith(keyHash, out certificate);
 | |
| 
 | |
| 			decrypted = default(Stream);
 | |
| 
 | |
| 			if (!found)
 | |
| 			{
 | |
| 				return FailForMissingCertificate();
 | |
| 			}
 | |
| 
 | |
| 			decrypted = Decrypt(data, PUBLIC_KEY_HASH_SIZE, certificate);
 | |
| 
 | |
| 			return LoadStatus.Success;
 | |
| 		}
 | |
| 
 | |
| 		internal virtual SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encrypted)
 | |
| 		{
 | |
| 			// TODO: Don't forget to write encryption parameters!
 | |
| 
 | |
| 			throw new NotImplementedException();
 | |
| 		}
 | |
| 
 | |
| 		protected byte[] ParsePublicKeyHash(Stream data)
 | |
| 		{
 | |
| 			var keyHash = new byte[PUBLIC_KEY_HASH_SIZE];
 | |
| 
 | |
| 			logger.Debug("Parsing public key hash...");
 | |
| 
 | |
| 			data.Seek(0, SeekOrigin.Begin);
 | |
| 			data.Read(keyHash, 0, keyHash.Length);
 | |
| 
 | |
| 			return keyHash;
 | |
| 		}
 | |
| 
 | |
| 		protected bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate)
 | |
| 		{
 | |
| 			var storesToSearch = new[]
 | |
| 			{
 | |
| 				new X509Store(StoreLocation.CurrentUser),
 | |
| 				new X509Store(StoreLocation.LocalMachine),
 | |
| 				new X509Store(StoreName.TrustedPeople)
 | |
| 			};
 | |
| 
 | |
| 			certificate = default(X509Certificate2);
 | |
| 			logger.Debug("Searching certificate for decryption...");
 | |
| 
 | |
| 			using (var algorithm = new SHA1CryptoServiceProvider())
 | |
| 			{
 | |
| 				foreach (var store in storesToSearch)
 | |
| 				{
 | |
| 					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;
 | |
| 							store.Close();
 | |
| 
 | |
| 							return true;
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					store.Close();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		protected LoadStatus FailForMissingCertificate()
 | |
| 		{
 | |
| 			logger.Error($"Could not find certificate which matches the given public key hash!");
 | |
| 
 | |
| 			return LoadStatus.InvalidData;
 | |
| 		}
 | |
| 
 | |
| 		protected MemoryStream Decrypt(Stream data, long offset, X509Certificate2 certificate)
 | |
| 		{
 | |
| 			var algorithm = certificate.PrivateKey as RSACryptoServiceProvider;
 | |
| 			var blockSize = algorithm.KeySize / 8;
 | |
| 			var blockCount = (data.Length - offset) / blockSize;
 | |
| 			var remainingBytes = data.Length - offset - (blockSize * blockCount);
 | |
| 			var encrypted = new byte[blockSize];
 | |
| 			var decrypted = default(byte[]);
 | |
| 			var decryptedData = new MemoryStream();
 | |
| 
 | |
| 			data.Seek(offset, SeekOrigin.Begin);
 | |
| 			logger.Debug("Decrypting data...");
 | |
| 
 | |
| 			for (int i = 0; i < blockCount; i++)
 | |
| 			{
 | |
| 				data.Read(encrypted, 0, encrypted.Length);
 | |
| 				decrypted = algorithm.Decrypt(encrypted, false);
 | |
| 				decryptedData.Write(decrypted, 0, decrypted.Length);
 | |
| 			}
 | |
| 
 | |
| 			if (remainingBytes > 0)
 | |
| 			{
 | |
| 				encrypted = new byte[remainingBytes];
 | |
| 				data.Read(encrypted, 0, encrypted.Length);
 | |
| 				decrypted = algorithm.Decrypt(encrypted, false);
 | |
| 				decryptedData.Write(decrypted, 0, decrypted.Length);
 | |
| 			}
 | |
| 
 | |
| 			return decryptedData;
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
