using DSInternals.Common;
using DSInternals.Common.Interop;
using Microsoft.Win32;
using System;
using System.Globalization;
namespace DSInternals.DataStore
{
public static class BootKeyRetriever
{
public const int BootKeyLength = 16;
private const string CurrentControlSetKey = "Select";
private const string CurrentControlSetValue = "Current";
private const string SystemKey = "SYSTEM";
private const string LsaKeyFormat = @"ControlSet{0:D3}\Control\Lsa\";
private const int DefaultControlSetId = 1;
private static readonly string[] BootKeySubKeyNames = { "JD", "Skew1", "GBG", "Data" };
private static readonly int[] KeyPermutation = { 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 };
///
/// Gets the boot key from an offline SYSTEM registry hive.
///
/// SYSTEM hive file path.
/// Boot Key
public static byte[] GetBootKey(string hiveFilePath)
{
Validator.AssertNotNullOrWhiteSpace(hiveFilePath, "hiveFilePath");
Validator.AssertFileExists(hiveFilePath);
using (var hive = new RegistryHiveFileMapping(hiveFilePath))
{
using (var system = hive.RootKey)
{
return GetBootKey(system);
}
}
}
///
/// Gets the boot key of the local computer.
///
/// Boot Key
public static byte[] GetBootKey()
{
using (var hklm = Registry.LocalMachine)
{
using(var system = hklm.OpenSubKey(SystemKey))
{
return GetBootKey(system);
}
}
}
private static byte[] GetBootKey(RegistryKey systemKey)
{
int currentControlSetId = GetCurrentControlSetId(systemKey);
string lsaKeyName = String.Format(LsaKeyFormat, currentControlSetId);
byte[] bootKey = new byte[BootKeyLength];
using(var lsa = systemKey.OpenSubKey(lsaKeyName))
{
for(int i = 0; i < BootKeySubKeyNames.Length; i++)
{
using (var bootKeyPartKey = lsa.OpenSubKey(BootKeySubKeyNames[i]))
{
byte[] bootKeyPart = bootKeyPartKey.GetClass().HexToBinary();
// Append this part to the boot key:
Array.Copy((Array)bootKeyPart, 0, (Array)bootKey, i * bootKeyPart.Length, bootKeyPart.Length);
}
}
}
return DecodeKey(bootKey);
}
private static int GetCurrentControlSetId(RegistryKey systemKey)
{
using (var ccsKey = systemKey.OpenSubKey(CurrentControlSetKey))
{
return (int)ccsKey.GetValue(CurrentControlSetValue, DefaultControlSetId);
}
}
private static byte[] DecodeKey(byte[] bootKey)
{
// Apply permutation to BootKey
byte[] decodedBootKey = new byte[BootKeyLength];
for (int i = 0; i < BootKeyLength; i++)
{
decodedBootKey[i] = bootKey[KeyPermutation[i]];
}
return decodedBootKey;
}
}
}