Initial commit

This commit is contained in:
Rowlander
2021-10-01 11:09:20 +02:00
commit fe54fceb1e
1301 changed files with 853973 additions and 0 deletions
@@ -0,0 +1,290 @@
using System;
using System.IO;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace Xylem.Common.Cryptology.Security
{
/// <summary>
/// Cryptology information container
/// </summary>
public static class CryptInformation
{
/// <summary>
/// Separator for PC name from hardware information
/// </summary>
public const String SeparatorPcFromHwId = @"\";
/// <summary>
/// Get domain from local machine
/// </summary>
/// <returns> domain </returns>
/// <remarks date="2021-Jan-30" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetSysDomain()
{
// Collect key information from HW and user
var userInformation = GetDomainAndUserLoginName();
var userInformationFields = userInformation.Split(Convert.ToChar("\\"));
return userInformationFields[0];
}
/// <summary>
/// Get user login name from local machine
/// </summary>
/// <returns> user login name </returns>
/// <remarks date="2021-Jan-30" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Convert to upper to get rid of the case sensitive user login name.
/// </remarks>
public static String GetSysUserLoginName()
{
// Collect key information from HW and user
var userInformation = GetDomainAndUserLoginName();
var userInformationFields = userInformation.Split(Convert.ToChar("\\"));
return userInformationFields[1].ToUpper();
}
/// <summary>
/// User login name including the domain e.g. "SPXMLU\bauer_marc"
/// </summary>
/// <remarks date="2021-Jan-21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Private function as user name is case sensitive.
/// </remarks>
private static String GetDomainAndUserLoginName()
{
return System.Security.Principal.WindowsIdentity.GetCurrent().Name;
}
/// <summary>
/// Getting local PC name which is useful for inter-program communication for operators to
/// select the right PC for update operation.
/// </summary>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetSysPcName()
{
return Environment.MachineName;
}
/// <summary>
/// Getting the PC name out of the system information string which is useful for inter-program
/// communication for operators to select the right PC for update operation.
/// </summary>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetPcNameFromHwId(String hardwareId)
{
if (!hardwareId.Contains(SeparatorPcFromHwId)) return "?";
var hwIdFields = hardwareId.Split(Convert.ToChar(SeparatorPcFromHwId));
return hwIdFields[0];
}
/// <summary>
/// User password hash replaced by random code if user is not registered or valid date is expired
/// </summary>
/// <remarks date="2021-Mar-19" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-22" author="Thomas Wiedebusch">
/// - Replace "-" with blank in random code.
/// </remarks>
public static String GetSysUserPasswordHash(UserInformation sysUser)
{
var randomCode = BitConverter.ToString(Cryptology.BuildRandomValue(32)).Replace("-", "");
// Take the validation date for to decide for new random code.
if (sysUser == null || string.IsNullOrEmpty(sysUser.PasswordHash) ||
DateTimeOffset.Compare(sysUser.ValidDate, DateTimeOffset.Now) < 1) return randomCode;
// if the registration valid date is not outdated, the sys user password shall remain as is
return sysUser.PasswordHash;
}
/// <summary>
/// Check user input for "full name".
/// </summary>
/// <param name="name">full user name for check</param>
/// <returns>true if user name matches the:
/// - Start with capital letter,
/// - one or more lowercase letters,
/// - white space and at least
/// - one trailing word,
/// - double names separated with - or ' are allowed as well as special characters
/// like umlaut in German or Spain...
/// </returns>
/// <remarks date="2021-Feb-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-13" author="Thomas Wiedebusch">
/// - Check name for null or empty.
/// </remarks>
public static Boolean CheckUserFullNameFormat(String name)
{
return !string.IsNullOrEmpty(name) && Regex.Match(name, @"\b\p{Lu}[-'\w]+(\s[-'\w]+)+$").Success;
}
/// <summary>
/// Check if the decoding is possible by comparison of the reg user with the sys user.
/// All information needs to be checked including the proper assigned FullName and the pwd hash.
/// </summary>
/// <param name="regUser"></param>
/// <returns>true if all information is set for decoding</returns>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-20" author="Thomas Wiedebusch">
/// - GetSysUserPasswordHash.
/// </remarks>
public static Boolean IsDecodingPossible(UserInformation regUser)
{
if (regUser == null || string.IsNullOrEmpty(regUser.FullName)) return false;
return regUser.Domain == GetSysDomain() &&
regUser.LogInName == GetSysUserLoginName() &&
regUser.HardwareId == GetSysHardwareId() &&
regUser.PasswordHash == GetSysUserPasswordHash(regUser);
}
/// <summary>
/// Read the sys user from Win10 system.
/// </summary>
/// <param name="sysUser"></param>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mar-19" author="Thomas Wiedebusch">
/// - Take the sys user as input.
/// </remarks>
public static void GetSysUser(UserInformation sysUser)
{
sysUser.Domain = GetSysDomain();
sysUser.LogInName = GetSysUserLoginName();
sysUser.HardwareId = GetSysHardwareId();
sysUser.PasswordHash = GetSysUserPasswordHash(sysUser);
}
/// <summary>
/// Write the user registration from the registration file on the local HDD.
/// </summary>
/// <param name="pathFile"></param>
/// <param name="dbFullRegUser"></param>
/// <returns>true if file exists and user has at least the FullName being set</returns>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static Boolean WriteUserRegistration(String pathFile, UserInformation dbFullRegUser)
{
// Register user to local HDD if the DB user is not identical
if (string.IsNullOrEmpty(pathFile)) return false;
try
{
// Store user registration from ..[User]/AppData/Roaming/Genesis.
var serializedData = JsonConvert.SerializeObject(dbFullRegUser);
var asciiStream = Encoding.UTF8.GetBytes(serializedData);
File.WriteAllBytes(pathFile, asciiStream);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Read the user registration from the registration file on the local HDD.
/// </summary>
/// <param name="pathFile"></param>
/// <param name="regUser"></param>
/// <returns>true if file exists and user has at least the FullName being set</returns>
/// <remarks date="2021-Mar-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Jun-01" author="Thomas Wiedebusch">
/// - Convert to upper to get rid of the case sensitive user login name.
/// </remarks>
public static Boolean ReadUserRegistration(String pathFile, out UserInformation regUser)
{
regUser = null;
try
{
if (!File.Exists(pathFile)) return false;
regUser = new UserInformation();
// load user registration from ..[User]/AppData/Roaming/Genesis
var asciiStream = File.ReadAllBytes(pathFile);
var serializedData = Encoding.UTF8.GetString(asciiStream, 0, asciiStream.Length);
regUser = JsonConvert.DeserializeObject<UserInformation>(serializedData);
regUser.LogInName = regUser.LogInName.ToUpper();
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Bios version of user PC
/// </summary>
/// <remarks date="2021-Jan-20" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String GetBiosVersion()
{
var bios = new ManagementObjectSearcher("SELECT Version FROM Win32_BIOS");
var biosCollection = bios.Get();
var strBios = new StringBuilder();
foreach (var obj in biosCollection)
{
strBios.Append(obj["Version"]);
}
return strBios.ToString();
}
/// <summary>
/// Hardware identification of user PC based on UUID and processor ID
/// </summary>
/// <remarks date="2021-Jan-20" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Apr-08" author="Thomas Wiedebusch">
/// - Add PC-Name to the HW Id to inform the FwUpdateBuilder operator about the PC.
/// </remarks>
public static String GetSysHardwareId()
{
var strHwId = new StringBuilder();
var pcName = GetSysPcName();
strHwId.Append(pcName);
strHwId.Append(SeparatorPcFromHwId);
var computerSystem = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct");
var computerSystemCollection = computerSystem.Get();
foreach (var obj in computerSystemCollection)
{
strHwId.Append(obj["UUID"]);
}
strHwId.Append("-");
var processorInfo = new ManagementObjectSearcher("SELECT ProcessorID From Win32_processor");
var processorInfoCollection = processorInfo.Get();
foreach (var obj in processorInfoCollection)
{
strHwId.Append(obj["ProcessorID"]);
}
return strHwId.ToString();
}
}
}
+152
View File
@@ -0,0 +1,152 @@
using System;
using System.IO;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace Xylem.Common.Cryptology.Security
{
/// <summary>
/// Class for cryptology.
/// </summary>
public static class Cryptology
{
private const Int32 MacSize = 16;
private const Int32 MacBitSize = MacSize * 8;
/// <summary>
/// AES-GCM Decryption using bouncy Castle nuget package
/// NOTE - add nonce + salt as aad into GCM encryption/authentication
/// </summary>
/// <param name="saltSize"></param>
/// <param name="nonceSize"></param>
/// <param name="keySize"></param>
/// <param name="primaryKey"> byte array of key components</param>
/// <param name="encryptedData"> encrypted cipher byte array = nonce | salt | cipherText | MAC </param>
/// <returns> encrypted cipher byte array </returns>
/// <remarks date="2021-Jan-21" author="Thomas Wiedebusch">
/// - Initial based on code snipped from Ray Savarda.
/// </remarks>
public static Byte[] AesGcmDecryption(Int32 saltSize, Int32 nonceSize, Int32 keySize, Byte[] primaryKey,
Byte[] encryptedData)
{
// Extract nonce and salt from encryptedData
var nonce = new Byte[nonceSize];
Array.Copy(encryptedData, nonce, nonceSize);
var salt = new Byte[saltSize];
Array.Copy(encryptedData, nonceSize, salt, 0, saltSize);
var aes256Key = BuildAes256Key(primaryKey, salt, keySize);
//extract aad for GCM call
var aad = new Byte[nonceSize + saltSize];
Array.Copy(encryptedData, 0, aad, 0, aad.Length);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(aes256Key), MacBitSize, nonce, aad);
cipher.Init(false, parameters);
var gcmCipherText = new Byte[encryptedData.Length - aad.Length];
Array.Copy(encryptedData, aad.Length, gcmCipherText, 0, encryptedData.Length - aad.Length);
var rawData = new Byte[cipher.GetOutputSize(gcmCipherText.Length)];
//Note aad handled by AadParameters call
var len = cipher.ProcessBytes(gcmCipherText, 0, gcmCipherText.Length, rawData, 0);
//TODO THW Need to add exception handlers - if MAC failure, get exception!
/*var gcmResultCount =*/ cipher.DoFinal(rawData, len);
return rawData;
}
/// <summary>
/// AES-GCM Encryption using bouncy Castle nuget package
/// NOTE - add nonce + salt as aad into GCM encryption/authentication
/// </summary>
/// <param name="saltSize"></param>
/// <param name="nonceSize"></param>
/// <param name="keySize"></param>
/// <param name="primaryKey"> byte array of key components</param>
/// <param name="rawData"> raw data stream for encryption - payload </param>
/// <returns> encrypted cipher byte array = nonce | salt | cipherText | MAC </returns>
/// <remarks date="2021-Jan-21" author="Thomas Wiedebusch">
/// - Initial based on code snipped from Ray Savarda.
/// </remarks>
public static Byte[] AesGcmEncryption(Int32 saltSize, Int32 nonceSize, Int32 keySize, Byte[] primaryKey,
Byte[] rawData)
{
// Initial salt generation
var salt = BuildRandomValue(saltSize);
var nonce = BuildRandomValue(nonceSize);
var aes256Key = BuildAes256Key(primaryKey, salt, keySize);
// put together the aad so it can be included in GCM call
var aad = new Byte[nonceSize + saltSize];
Array.Copy(nonce, 0, aad, 0, nonce.Length);
Array.Copy(salt, 0, aad, nonce.Length, salt.Length);
var cipher = new GcmBlockCipher(new AesEngine());
//include AAD for MAC protection!
var parameters = new AeadParameters(new KeyParameter(aes256Key), MacBitSize, nonce, aad);
cipher.Init(true, parameters);
var cipherText = new Byte[cipher.GetOutputSize(rawData.Length)];
var len = cipher.ProcessBytes(rawData, 0, rawData.Length, cipherText, 0);
cipher.DoFinal(cipherText, len); //Note Cipher text includes MAC at end!
//var noMac = new Byte[cipherText.Length - MacSize];
//Array.Copy(cipherText, noMac, cipherText.Length - MacSize);
//var onlyMac = new Byte[MacSize];
//Array.ConstrainedCopy(cipherText, cipherText.Length - MacSize, onlyMac, 0, onlyMac.Length);
//Assemble output memory stream = nonce + salt + cipherText + MAC
var outMs = new MemoryStream();
using (var binaryWriter = new BinaryWriter(outMs))
{
//Prepend Nonce
binaryWriter.Write(nonce);
//add salt
binaryWriter.Write(salt);
//Write Cipher Text
binaryWriter.Write(cipherText);
}
return outMs.ToArray();
}
/// <summary>
/// Build random value
/// </summary>
/// <param name="size"></param>
/// <returns> byte array of random value </returns>
/// <remarks date="2021-Jan-21" author="Thomas Wiedebusch">
/// - Initial based on code snipped from Ray Savarda.
/// </remarks>
public static Byte[] BuildRandomValue(Int32 size)
{
var random = new Byte[size];
var rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(random);
return random;
}
/// <summary>
/// Build AES256 key
/// </summary>
/// <param name="primaryKey">primary information for key generation</param>
/// <param name="salt"></param>
/// <param name="aes256KeySize"></param>
/// <returns> AES256 key </returns>
/// <remarks date="2021-Jan-21" author="Thomas Wiedebusch">
/// - Initial based on code snipped from Ray Savarda.
/// </remarks>
public static Byte[] BuildAes256Key(Byte[] primaryKey, Byte[] salt, Int32 aes256KeySize)
{
const Int32 iterations = 10000; //(typically 2,000 - 10,000)
var aes256Key = new Rfc2898DeriveBytes(primaryKey, salt, iterations, HashAlgorithmName.SHA256);
return aes256Key.GetBytes(aes256KeySize);
}
}
}
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A25A96FD-604B-43BE-80CD-A953152C2175}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Cryptology.Security</RootNamespace>
<AssemblyName>Xylem.Common.Cryptology.Security</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\..\..\ServiceFwUpdate\packages\BouncyCastle.Crypto.dll.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\ServiceFwUpdate\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Cryptology.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="CryptInformation.cs" />
<Compile Include="UserInformation.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xylem.Common.Cryptology.Security")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a25a96fd-604b-43be-80cd-a953152c2175")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,66 @@
using System;
namespace Xylem.Common.Cryptology.Security
{
/// <summary>
/// Collection of user information for DB.
/// </summary>
public class UserInformation
{
/// <summary>
/// Ctor with user Id.
/// </summary>
public UserInformation(Int32 id)
{
Id = id;
}
/// <summary>
/// Ctor.
/// </summary>
public UserInformation()
{
}
/// <summary>
/// Used to keep the selection information on e.g. data tables stuck at the user.
/// </summary>
public Boolean IsSelected;
/// <summary>
/// Identification number of user used for DB assignment of hash and HwId.
/// </summary>
public Int32 Id;
/// <summary>
/// Readable name of user.
/// </summary>
public String FullName;
/// <summary>
/// Domain where user is registered.
/// </summary>
public String Domain;
/// <summary>
/// Login name of user.
/// </summary>
public String LogInName;
/// <summary>
/// User password hash.
/// </summary>
public String PasswordHash;
/// <summary>
/// Hardware identification.
/// </summary>
public String HardwareId;
/// <summary>
/// Date of user registration.
/// </summary>
public DateTimeOffset RegisterDate;
/// <summary>
/// Validation data.
/// </summary>
public DateTimeOffset ValidDate;
/// <summary>
/// All new users must be manually validated to active.
/// </summary>
public Boolean AccountActive;
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle.Crypto.dll" version="1.8.1" targetFramework="net472" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
</packages>