Restore SEBPatch

This commit is contained in:
2025-06-01 11:44:20 +02:00
commit 8c656e3137
1297 changed files with 142172 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
/*
* 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<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𝈒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();
}
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.Linq;
using System.Security.Cryptography;
using System.Text;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
namespace SafeExamBrowser.Configuration.Cryptography
{
public class HashAlgorithm : IHashAlgorithm
{
public string GenerateHashFor(string password)
{
using (var algorithm = new SHA256Managed())
{
var bytes = Encoding.UTF8.GetBytes(password);
var hash = algorithm.ComputeHash(bytes);
var hashString = String.Join(String.Empty, hash.Select(b => b.ToString("x2")));
return hashString;
}
}
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.Security.Cryptography;
using System.Text;
using System.Threading;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.Integrity;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.Cryptography
{
public class KeyGenerator : IKeyGenerator
{
private readonly object @lock = new object();
private readonly ThreadLocal<SHA256Managed> algorithm;
private readonly AppConfig appConfig;
private readonly IIntegrityModule integrityModule;
private readonly ILogger logger;
private string browserExamKey;
public KeyGenerator(AppConfig appConfig, IIntegrityModule integrityModule, ILogger logger)
{
this.algorithm = new ThreadLocal<SHA256Managed>(() => new SHA256Managed());
this.appConfig = appConfig;
this.integrityModule = integrityModule;
this.logger = logger;
}
public string CalculateAppSignatureKey(string connectionToken, string salt)
{
if (integrityModule.TryCalculateAppSignatureKey(connectionToken, salt, out var appSignatureKey))
{
logger.Debug("Successfully calculated app signature key using integrity module.");
}
else
{
logger.Error("Failed to calculate app signature key using integrity module!");
}
return appSignatureKey;
}
public string CalculateBrowserExamKeyHash(string configurationKey, byte[] salt, string url)
{
var urlWithoutFragment = url.Split('#')[0];
var hash = algorithm.Value.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey(configurationKey, salt))));
var key = ToString(hash);
return key;
}
public string CalculateConfigurationKeyHash(string configurationKey, string url)
{
var urlWithoutFragment = url.Split('#')[0];
var hash = algorithm.Value.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + configurationKey));
var key = ToString(hash);
return key;
}
public void UseCustomBrowserExamKey(string browserExamKey)
{
if (browserExamKey != default)
{
this.browserExamKey = browserExamKey;
logger.Debug("Initialized custom browser exam key.");
}
}
private string ComputeBrowserExamKey(string configurationKey, byte[] salt)
{
lock (@lock)
{
if (browserExamKey == default)
{
logger.Debug("Initializing browser exam key...");
if (configurationKey == default)
{
configurationKey = "";
logger.Warn("The current configuration does not contain a value for the configuration key!");
}
if (salt == default || salt.Length == 0)
{
salt = new byte[0];
logger.Warn("The current configuration does not contain a salt value for the browser exam key!");
}
if (integrityModule.TryCalculateBrowserExamKey(configurationKey, ToString(salt), out browserExamKey))
{
logger.Debug("Successfully calculated browser exam key using integrity module.");
}
else
{
logger.Warn("Failed to calculate browser exam key using integrity module! Falling back to simplified calculation...");
using (var algorithm = new HMACSHA256(salt))
{
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + configurationKey));
var key = ToString(hash);
browserExamKey = key;
}
logger.Debug("Successfully calculated browser exam key using simplified calculation.");
}
}
}
return browserExamKey;
}
private string ToString(byte[] bytes)
{
return BitConverter.ToString(bytes).ToLower().Replace("-", string.Empty);
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* 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.IO;
using System.Linq;
using System.Security.Cryptography;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.Cryptography
{
public class PasswordEncryption : IPasswordEncryption
{
private const int BLOCK_SIZE = 16;
private const int HEADER_SIZE = 2;
private const int ITERATIONS = 10000;
private const int KEY_SIZE = 32;
private const int OPTIONS = 0x1;
private const int SALT_SIZE = 8;
private const int VERSION = 0x2;
private ILogger logger;
public PasswordEncryption(ILogger logger)
{
this.logger = logger;
}
public LoadStatus Decrypt(Stream data, string password, out Stream decrypted)
{
decrypted = default(Stream);
if (password == null)
{
return LoadStatus.PasswordNeeded;
}
var (version, options) = ParseHeader(data);
var (authenticationKey, encryptionKey) = GenerateKeysForDecryption(data, password);
var (originalHmac, computedHmac) = GenerateHmacForDecryption(authenticationKey, data);
if (!computedHmac.SequenceEqual(originalHmac))
{
return FailForInvalidHmac();
}
decrypted = Decrypt(data, encryptionKey, originalHmac.Length);
return LoadStatus.Success;
}
public SaveStatus Encrypt(Stream data, string password, out Stream encrypted)
{
var (authKey, authSalt, encrKey, encrSalt) = GenerateKeysForEncryption(password);
encrypted = Encrypt(data, encrKey, out var initVector);
encrypted = WriteEncryptionParameters(authKey, authSalt, encrSalt, initVector, encrypted);
return SaveStatus.Success;
}
private (int version, int options) ParseHeader(Stream data)
{
data.Seek(0, SeekOrigin.Begin);
logger.Debug("Parsing encryption header...");
var version = data.ReadByte();
var options = data.ReadByte();
if (version != VERSION || options != OPTIONS)
{
logger.Debug($"Unknown encryption header! Expected: [{VERSION},{OPTIONS},...] - Actual: [{version},{options},...]");
}
return (version, options);
}
private (byte[] authenticationKey, byte[] encryptionKey) GenerateKeysForDecryption(Stream data, string password)
{
var authenticationSalt = new byte[SALT_SIZE];
var encryptionSalt = new byte[SALT_SIZE];
logger.Debug("Generating keys for authentication and decryption...");
data.Seek(HEADER_SIZE, SeekOrigin.Begin);
data.Read(encryptionSalt, 0, SALT_SIZE);
data.Read(authenticationSalt, 0, SALT_SIZE);
using (var authenticationGenerator = new Rfc2898DeriveBytes(password, authenticationSalt, ITERATIONS))
using (var encryptionGenerator = new Rfc2898DeriveBytes(password, encryptionSalt, ITERATIONS))
{
var authenticationKey = authenticationGenerator.GetBytes(KEY_SIZE);
var encryptionKey = encryptionGenerator.GetBytes(KEY_SIZE);
return (authenticationKey, encryptionKey);
}
}
private (byte[] authKey, byte[] authSalt, byte[] encrKey, byte[] encrSalt) GenerateKeysForEncryption(string password)
{
logger.Debug("Generating keys for authentication and encryption...");
using (var authenticationGenerator = new Rfc2898DeriveBytes(password, SALT_SIZE, ITERATIONS))
using (var encryptionGenerator = new Rfc2898DeriveBytes(password, SALT_SIZE, ITERATIONS))
{
var authenticationSalt = authenticationGenerator.Salt;
var authenticationKey = authenticationGenerator.GetBytes(KEY_SIZE);
var encryptionSalt = encryptionGenerator.Salt;
var encryptionKey = encryptionGenerator.GetBytes(KEY_SIZE);
return (authenticationKey, authenticationSalt, encryptionKey, encryptionSalt);
}
}
private (byte[] originalHmac, byte[] computedHmac) GenerateHmacForDecryption(byte[] authenticationKey, Stream data)
{
logger.Debug("Generating HMACs for authentication...");
using (var algorithm = new HMACSHA256(authenticationKey))
{
var originalHmac = new byte[algorithm.HashSize / 8];
var hashStream = new SubStream(data, 0, data.Length - originalHmac.Length);
var computedHmac = algorithm.ComputeHash(hashStream);
data.Seek(-originalHmac.Length, SeekOrigin.End);
data.Read(originalHmac, 0, originalHmac.Length);
return (originalHmac, computedHmac);
}
}
private byte[] GenerateHmacForEncryption(byte[] authenticationKey, Stream data)
{
data.Seek(0, SeekOrigin.Begin);
logger.Debug("Generating HMAC for authentication...");
using (var algorithm = new HMACSHA256(authenticationKey))
{
return algorithm.ComputeHash(data);
}
}
private LoadStatus FailForInvalidHmac()
{
logger.Debug($"The authentication failed due to an invalid password or corrupted data!");
return LoadStatus.PasswordNeeded;
}
private Stream Decrypt(Stream data, byte[] encryptionKey, int hmacLength)
{
var initializationVector = new byte[BLOCK_SIZE];
data.Seek(HEADER_SIZE + 2 * SALT_SIZE, SeekOrigin.Begin);
data.Read(initializationVector, 0, BLOCK_SIZE);
var decryptedData = new MemoryStream();
var encryptedData = new SubStream(data, data.Position, data.Length - data.Position - hmacLength);
logger.Debug("Decrypting data...");
using (var algorithm = new AesManaged { KeySize = KEY_SIZE * 8, BlockSize = BLOCK_SIZE * 8, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
using (var decryptor = algorithm.CreateDecryptor(encryptionKey, initializationVector))
using (var cryptoStream = new CryptoStream(encryptedData, decryptor, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(decryptedData);
}
return decryptedData;
}
private Stream Encrypt(Stream data, byte[] encryptionKey, out byte[] initializationVector)
{
var encryptedData = new MemoryStream();
logger.Debug("Encrypting data...");
using (var algorithm = new AesManaged { KeySize = KEY_SIZE * 8, BlockSize = BLOCK_SIZE * 8, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
{
algorithm.GenerateIV();
data.Seek(0, SeekOrigin.Begin);
initializationVector = algorithm.IV;
using (var encryptor = algorithm.CreateEncryptor(encryptionKey, initializationVector))
using (var cryptoStream = new CryptoStream(data, encryptor, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(encryptedData);
}
return encryptedData;
}
}
private Stream WriteEncryptionParameters(byte[] authKey, byte[] authSalt, byte[] encrSalt, byte[] initVector, Stream encryptedData)
{
var data = new MemoryStream();
var header = new byte[] { VERSION, OPTIONS };
logger.Debug("Writing encryption parameters...");
data.Write(header, 0, header.Length);
data.Write(encrSalt, 0, encrSalt.Length);
data.Write(authSalt, 0, authSalt.Length);
data.Write(initVector, 0, initVector.Length);
encryptedData.Seek(0, SeekOrigin.Begin);
encryptedData.CopyTo(data);
var hmac = GenerateHmacForEncryption(authKey, data);
data.Seek(0, SeekOrigin.End);
data.Write(hmac, 0, hmac.Length);
return data;
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.Cryptography
{
public class PublicKeyEncryption : IPublicKeyEncryption
{
protected const int PUBLIC_KEY_HASH_SIZE = 20;
protected ICertificateStore store;
protected ILogger logger;
public PublicKeyEncryption(ICertificateStore store, ILogger logger)
{
this.logger = logger;
this.store = store;
}
public virtual LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate)
{
var publicKeyHash = ParsePublicKeyHash(data);
var found = store.TryGetCertificateWith(publicKeyHash, out certificate);
decryptedData = default(Stream);
if (!found)
{
return FailForMissingCertificate();
}
decryptedData = Decrypt(data, PUBLIC_KEY_HASH_SIZE, certificate);
return LoadStatus.Success;
}
public virtual SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData)
{
var publicKeyHash = GeneratePublicKeyHash(certificate);
encryptedData = Encrypt(data, certificate);
encryptedData = WriteEncryptionParameters(encryptedData, publicKeyHash);
return SaveStatus.Success;
}
protected LoadStatus FailForMissingCertificate()
{
logger.Error($"Could not find certificate which matches the given public key hash!");
return LoadStatus.InvalidData;
}
protected byte[] GeneratePublicKeyHash(X509Certificate2 certificate)
{
var publicKey = certificate.PublicKey.EncodedKeyValue.RawData;
using (var sha = new SHA1CryptoServiceProvider())
{
return sha.ComputeHash(publicKey);
}
}
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 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 decrypted = new MemoryStream();
var decryptedBuffer = new byte[blockSize];
var encryptedBuffer = new byte[blockSize];
var remainingBytes = data.Length - offset - (blockSize * blockCount);
data.Seek(offset, SeekOrigin.Begin);
logger.Debug("Decrypting data...");
using (algorithm)
{
for (var block = 0; block < blockCount; block++)
{
data.Read(encryptedBuffer, 0, encryptedBuffer.Length);
decryptedBuffer = algorithm.Decrypt(encryptedBuffer, false);
decrypted.Write(decryptedBuffer, 0, decryptedBuffer.Length);
}
if (remainingBytes > 0)
{
encryptedBuffer = new byte[remainingBytes];
data.Read(encryptedBuffer, 0, encryptedBuffer.Length);
decryptedBuffer = algorithm.Decrypt(encryptedBuffer, false);
decrypted.Write(decryptedBuffer, 0, decryptedBuffer.Length);
}
}
return decrypted;
}
protected Stream Encrypt(Stream data, X509Certificate2 certificate)
{
var algorithm = certificate.PublicKey.Key as RSACryptoServiceProvider;
var blockSize = (algorithm.KeySize / 8) - 32;
var blockCount = data.Length / blockSize;
var decryptedBuffer = new byte[blockSize];
var encrypted = new MemoryStream();
var encryptedBuffer = new byte[blockSize];
var remainingBytes = data.Length - (blockCount * blockSize);
data.Seek(0, SeekOrigin.Begin);
logger.Debug("Encrypting data...");
using (algorithm)
{
for (var block = 0; block < blockCount; block++)
{
data.Read(decryptedBuffer, 0, decryptedBuffer.Length);
encryptedBuffer = algorithm.Encrypt(decryptedBuffer, false);
encrypted.Write(encryptedBuffer, 0, encryptedBuffer.Length);
}
if (remainingBytes > 0)
{
decryptedBuffer = new byte[remainingBytes];
data.Read(decryptedBuffer, 0, decryptedBuffer.Length);
encryptedBuffer = algorithm.Encrypt(decryptedBuffer, false);
encrypted.Write(encryptedBuffer, 0, encryptedBuffer.Length);
}
}
return encrypted;
}
private Stream WriteEncryptionParameters(Stream encryptedData, byte[] keyHash)
{
var data = new MemoryStream();
logger.Debug("Writing encryption parameters...");
data.Write(keyHash, 0, keyHash.Length);
encryptedData.Seek(0, SeekOrigin.Begin);
encryptedData.CopyTo(data);
return data;
}
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Configuration.Cryptography
{
public class PublicKeySymmetricEncryption : PublicKeyEncryption
{
private const int ENCRYPTION_KEY_LENGTH = 32;
private const int KEY_LENGTH_SIZE = 4;
private PasswordEncryption passwordEncryption;
public PublicKeySymmetricEncryption(ICertificateStore store, ILogger logger, PasswordEncryption passwordEncryption) : base(store, logger)
{
this.passwordEncryption = passwordEncryption;
}
public override LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate)
{
var publicKeyHash = ParsePublicKeyHash(data);
var found = store.TryGetCertificateWith(publicKeyHash, out certificate);
decryptedData = default(Stream);
if (!found)
{
return FailForMissingCertificate();
}
var symmetricKey = ParseSymmetricKey(data, certificate);
var stream = new SubStream(data, data.Position, data.Length - data.Position);
var status = passwordEncryption.Decrypt(stream, symmetricKey, out decryptedData);
return status;
}
public override SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData)
{
var publicKeyHash = GeneratePublicKeyHash(certificate);
var symmetricKey = GenerateSymmetricKey();
var symmetricKeyString = Convert.ToBase64String(symmetricKey);
var status = passwordEncryption.Encrypt(data, symmetricKeyString, out encryptedData);
if (status != SaveStatus.Success)
{
return FailForUnsuccessfulPasswordEncryption(status);
}
encryptedData = WriteEncryptionParameters(encryptedData, certificate, publicKeyHash, symmetricKey);
return SaveStatus.Success;
}
private SaveStatus FailForUnsuccessfulPasswordEncryption(SaveStatus status)
{
logger.Error($"Password encryption has failed with status '{status}'!");
return SaveStatus.UnexpectedError;
}
private byte[] GenerateSymmetricKey()
{
var key = new byte[ENCRYPTION_KEY_LENGTH];
using (var generator = RandomNumberGenerator.Create())
{
generator.GetBytes(key);
}
return key;
}
private string ParseSymmetricKey(Stream data, X509Certificate2 certificate)
{
var keyLengthData = new byte[KEY_LENGTH_SIZE];
logger.Debug("Parsing symmetric key...");
data.Seek(PUBLIC_KEY_HASH_SIZE, SeekOrigin.Begin);
data.Read(keyLengthData, 0, keyLengthData.Length);
var encryptedKeyLength = BitConverter.ToInt32(keyLengthData, 0);
var encryptedKey = new byte[encryptedKeyLength];
data.Read(encryptedKey, 0, encryptedKey.Length);
var stream = new SubStream(data, PUBLIC_KEY_HASH_SIZE + KEY_LENGTH_SIZE, encryptedKeyLength);
var decryptedKey = Decrypt(stream, 0, certificate);
var symmetricKey = Convert.ToBase64String(decryptedKey.ToArray());
return symmetricKey;
}
private Stream WriteEncryptionParameters(Stream encryptedData, X509Certificate2 certificate, byte[] publicKeyHash, byte[] symmetricKey)
{
var data = new MemoryStream();
var symmetricKeyData = new MemoryStream(symmetricKey);
var encryptedKey = Encrypt(symmetricKeyData, certificate);
// IMPORTANT: The key length must be exactly 4 Bytes, thus the cast to integer!
var encryptedKeyLength = BitConverter.GetBytes((int) encryptedKey.Length);
logger.Debug("Writing encryption parameters...");
data.Write(publicKeyHash, 0, publicKeyHash.Length);
data.Write(encryptedKeyLength, 0, encryptedKeyLength.Length);
encryptedKey.Seek(0, SeekOrigin.Begin);
encryptedKey.CopyTo(data);
encryptedData.Seek(0, SeekOrigin.Begin);
encryptedData.CopyTo(data);
return data;
}
}
}