#define TRACE using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Numerics; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using ArchestrAServices.Common; using Invensys.Compression; namespace ArchestrAServices.ASBContract; public class SysAuthConnectionBase { private static RandomNumberGenerator mRANDOM = RandomNumberGenerator.Create(); private static readonly byte[] PASSWORDSALT = Encoding.ASCII.GetBytes("ArchestrAService"); protected static string ASBAuthenticationVersion = "V2"; protected static readonly object MessageNumberLock = new object(); protected SysAuthParameters m_SolutionParameters; public Guid connectionID = Guid.Empty; protected byte[] m_PrivateKey; protected byte[] m_LocalPublicKey; protected byte[] m_RemotePublicKey; protected bool m_Authenticated; protected ulong m_NextMessageNumber = 1uL; private ulong m_HighestMessageNumberReceived; private List m_OutOfSyncMessageNumbers = new List(); private byte[] SolutionPassphrase { get { byte[] result = null; if (m_SolutionParameters.ASBSolutionValid) { if (m_SolutionParameters.DH_passphrase != null && m_SolutionParameters.DH_passphrase.Length > 0) { result = Encoding.UTF8.GetBytes(m_SolutionParameters.DH_passphrase); } if (m_SolutionParameters.DH_certificate != null && m_SolutionParameters.DH_certificate.Length != 0) { result = new X509Certificate(m_SolutionParameters.DH_certificate).GetPublicKey(); } } return result; } } private byte[] CryptoKey { get { List list = new List(); list.AddRange(CalculateConnectionKey(m_RemotePublicKey, m_PrivateKey)); byte[] solutionPassphrase = SolutionPassphrase; if (solutionPassphrase != null) { list.AddRange(solutionPassphrase); } return list.ToArray(); } } public ConnectionValidator m_ConnectionValidator { get; protected set; } public bool SecureSessionEstablished { get { return m_Authenticated; } protected set { m_Authenticated = value; } } public string ReasonSecureSessionNotEstablished { get; protected set; } public string DH_passphrase { get { return m_SolutionParameters.DH_passphrase; } set { m_SolutionParameters.DH_passphrase = value; } } public string DH_hashAlgorithm => m_SolutionParameters.hashAlgorithm; public string DH_asbSolutionName => m_SolutionParameters.ASBSolutionName; public SigningMethod SignatureMethod { get; set; } public SysAuthConnectionBase(string asbSolutionName = null) { m_SolutionParameters = new SysAuthParameters(asbSolutionName); ReasonSecureSessionNotEstablished = "Constructed"; Reset(); } protected void Reset() { SecureSessionEstablished = false; ReasonSecureSessionNotEstablished = "Reset"; SignatureMethod = SigningMethod.Baktun; connectionID = Guid.NewGuid(); m_PrivateKey = GetPrivateKey(m_SolutionParameters.KeySize); m_LocalPublicKey = CalculatePublicKey(m_PrivateKey); m_Authenticated = false; } private byte[] GetPrivateKey(int length) { byte[] array = null; if (length > 0) { BigInteger bigInteger = m_SolutionParameters.DH_p - new BigInteger(1); BigInteger bigInteger2 = new BigInteger(0); while (bigInteger2 >= bigInteger || bigInteger2 <= 0L) { array = new byte[length / 8]; mRANDOM.GetBytes(array); bigInteger2 = new BigInteger(array); } } return array; } protected byte[] CalculatePublicKey(byte[] privateKey) { BigInteger exponent = new BigInteger(privateKey); BigInteger dH_g = m_SolutionParameters.DH_g; BigInteger dH_p = m_SolutionParameters.DH_p; return BigInteger.ModPow(dH_g, exponent, dH_p).ToByteArray(); } protected byte[] CalculateConnectionKey(byte[] remotePublicKey, byte[] localPrivateKey) { BigInteger value = new BigInteger(remotePublicKey); BigInteger exponent = new BigInteger(localPrivateKey); BigInteger dH_p = m_SolutionParameters.DH_p; return BigInteger.ModPow(value, exponent, dH_p).ToByteArray(); } private HMAC NewSolutionHmac(bool ForceHMAC = false) { HMAC result; switch (DH_hashAlgorithm.ToLower()) { case "md5": SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Solution HMAC is MD5"); result = new HMACMD5(CryptoKey); break; case "sha1": SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Solution HMAC is SHA1"); result = new HMACSHA1(CryptoKey); break; case "sha512": SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Solution HMAC is SHA512"); result = new HMACSHA512(CryptoKey); break; default: SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Solution HMAC is NONE"); result = null; if (ForceHMAC) { result = new HMACSHA1(CryptoKey); } break; } return result; } public bool ValidRequest(ConnectedRequest request, bool ForceHmac = false) { bool flag = false; if (request != null && request.ConnectionValidator != null) { ConnectionValidator connectionValidator = request.ConnectionValidator; byte[] messageAuthenticationCode = connectionValidator.MessageAuthenticationCode; byte[] signatureInitializationVector = connectionValidator.SignatureInitializationVector; byte[] array = null; using (HMAC hMAC = NewSolutionHmac(ForceHmac)) { if (hMAC != null) { connectionValidator.MessageAuthenticationCode = new byte[0]; connectionValidator.SignatureInitializationVector = new byte[0]; byte[] bytes = Encoding.UTF8.GetBytes(request.ToXml()); connectionValidator.MessageAuthenticationCode = messageAuthenticationCode; connectionValidator.SignatureInitializationVector = signatureInitializationVector; if (SignatureMethod == SigningMethod.Baktun) { array = EncypherBaktun(hMAC.ComputeHash(bytes), signatureInitializationVector); } else { byte[] array2 = hMAC.ComputeHash(bytes); byte[] array3 = DecypherApollo(messageAuthenticationCode, signatureInitializationVector); bool flag2 = false; if (array2 != null && array3 != null && array2.Length == array3.Length) { flag2 = true; for (int i = 0; i < array2.Length; i++) { if (array2[i] != array3[i]) { flag2 = false; break; } } } array = ((!flag2) ? new byte[0] : messageAuthenticationCode); } } } if (array != null) { if (messageAuthenticationCode != null && array.Length == messageAuthenticationCode.Length) { flag = true; for (int j = 0; j < messageAuthenticationCode.Length; j++) { if (messageAuthenticationCode[j] != array[j]) { if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished)) { ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message hmac correct length {0} but differs at byte {1}, cannot validate", new object[2] { messageAuthenticationCode.Length, j }); } flag = false; break; } } } else if (messageAuthenticationCode == null) { if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished)) { ReasonSecureSessionNotEstablished = "ValidRequest: Received message has null hmac, cannot validate"; } } else if (string.IsNullOrEmpty(ReasonSecureSessionNotEstablished)) { ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message hmac wrong length, cannot validate (received {0}, computed {1})", new object[2] { messageAuthenticationCode.Length, array.Length }); } } else { flag = true; } if (flag) { lock (this) { if (connectionValidator.MessageNumber <= m_HighestMessageNumberReceived) { ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message out of sequence, cannot validate (current {0}, highest {1})", new object[2] { connectionValidator.MessageNumber, m_HighestMessageNumberReceived }); flag = false; } else if (m_OutOfSyncMessageNumbers.Contains(connectionValidator.MessageNumber)) { ReasonSecureSessionNotEstablished = string.Format(CultureInfo.CurrentCulture, "ValidRequest: Received message received late, cannot validate (current {0})", new object[1] { connectionValidator.MessageNumber }); flag = false; } else if (connectionValidator.MessageNumber == m_HighestMessageNumberReceived + 1) { m_HighestMessageNumberReceived = connectionValidator.MessageNumber; } else { m_OutOfSyncMessageNumbers.Add(connectionValidator.MessageNumber); } m_OutOfSyncMessageNumbers.Sort(); foreach (ulong outOfSyncMessageNumber in m_OutOfSyncMessageNumbers) { if (outOfSyncMessageNumber == m_HighestMessageNumberReceived + 1) { m_HighestMessageNumberReceived = outOfSyncMessageNumber; } } List list = new List(); foreach (ulong outOfSyncMessageNumber2 in m_OutOfSyncMessageNumbers) { if (outOfSyncMessageNumber2 <= m_HighestMessageNumberReceived) { list.Add(outOfSyncMessageNumber2); } } foreach (ulong item in list) { m_OutOfSyncMessageNumbers.Remove(item); } } } } else { ReasonSecureSessionNotEstablished = "ValidRequest: Either the message or its ConnectionValidator field is null, cannot validate"; } return flag; } public bool ValidResponse(ConnectedResponse response, bool ForceHmac = false) { if (response != null && response.Result.Success) { return ValidRequest(response, ForceHmac); } return false; } protected byte[] ReCalculateAuthenticationData(byte[] leftPart, byte[] rightPart, byte[] initializationVector) { List list = new List(); if (leftPart != null) { list.AddRange(leftPart); } if (rightPart != null) { list.AddRange(rightPart); } return ReEncypher(list.ToArray(), initializationVector); } protected byte[] CalculateAuthenticationData(byte[] leftPart, byte[] rightPart, out byte[] initializationVector) { List list = new List(); initializationVector = null; if (leftPart != null) { list.AddRange(leftPart); } if (rightPart != null) { list.AddRange(rightPart); } return Encypher(list.ToArray(), out initializationVector); } public ConnectionValidator MakeConnectionValidator() { lock (MessageNumberLock) { return new ConnectionValidator { ConnectionId = connectionID, MessageNumber = m_NextMessageNumber++, MessageAuthenticationCode = new byte[0], SignatureInitializationVector = new byte[0] }; } } public void Sign(ConnectedRequest request, bool ForceHmac = false) { if (request == null) { return; } lock (MessageNumberLock) { ConnectionValidator connectionValidator = new ConnectionValidator(); connectionValidator.ConnectionId = connectionID; connectionValidator.MessageNumber = m_NextMessageNumber++; connectionValidator.MessageAuthenticationCode = new byte[0]; connectionValidator.SignatureInitializationVector = new byte[0]; request.ConnectionValidator = connectionValidator; using HMAC hMAC = NewSolutionHmac(ForceHmac); if (hMAC != null) { byte[] bytes = Encoding.UTF8.GetBytes(request.ToXml()); connectionValidator.MessageAuthenticationCode = Encypher(hMAC.ComputeHash(bytes), out var initializationVector); connectionValidator.SignatureInitializationVector = initializationVector; } } } public byte[] EncypherBaktun(byte[] clearData, byte[] initializationVector) { byte[] result = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using AADeflateStream aADeflateStream = new AADeflateStream(stream, CompressionMode.Compress); aADeflateStream.Write(clearData, 0, clearData.Length); aADeflateStream.Close(); } result = memoryStream.ToArray(); } return result; } public byte[] EncypherBaktun(byte[] clearData, out byte[] initializationVector) { byte[] result = null; initializationVector = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); initializationVector = aesManaged.IV; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using AADeflateStream aADeflateStream = new AADeflateStream(stream, CompressionMode.Compress); aADeflateStream.Write(clearData, 0, clearData.Length); aADeflateStream.Close(); } result = memoryStream.ToArray(); } return result; } public string EncypherBaktun(string clearText, out byte[] initializationVector) { string result = string.Empty; initializationVector = null; if (!string.IsNullOrEmpty(clearText)) { byte[] bytes = Encoding.UTF8.GetBytes(clearText); result = EncypherBaktun(bytes, out initializationVector).ToBase64(); } return result; } public byte[] ReEncypherBaktun(byte[] clearData, byte[] initializationVector) { byte[] result = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using AADeflateStream aADeflateStream = new AADeflateStream(stream, CompressionMode.Compress); aADeflateStream.Write(clearData, 0, clearData.Length); aADeflateStream.Close(); } result = memoryStream.ToArray(); } return result; } public string ReEncypherBaktun(string clearText, byte[] initializationVector) { string result = string.Empty; if (!string.IsNullOrEmpty(clearText)) { byte[] bytes = Encoding.UTF8.GetBytes(clearText); result = ReEncypherBaktun(bytes, initializationVector).ToBase64(); } return result; } public byte[] DecypherBaktun(byte[] cypherData, byte[] initializationVector) { byte[] result = null; if (cypherData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cypherData); using CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read); using AADeflateStream aADeflateStream = new AADeflateStream(stream2, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(); aADeflateStream.CopyTo(memoryStream); result = memoryStream.ToArray(); } return result; } public string DecypherBaktun(string cypherText, byte[] initializationVector) { string result = string.Empty; if (!string.IsNullOrEmpty(cypherText)) { byte[] cypherData = cypherText.FromBase64ToByteArray(); byte[] bytes = DecypherBaktun(cypherData, initializationVector); result = Encoding.UTF8.GetString(bytes); } return result; } public byte[] EncypherApollo(byte[] clearData, byte[] initializationVector) { byte[] result = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { cryptoStream.Write(clearData, 0, clearData.Length); cryptoStream.Close(); } result = memoryStream.ToArray(); } return result; } public byte[] EncypherApollo(byte[] clearData, out byte[] initializationVector) { byte[] result = null; initializationVector = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); initializationVector = aesManaged.IV; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { cryptoStream.Write(clearData, 0, clearData.Length); cryptoStream.Close(); } result = memoryStream.ToArray(); } return result; } public string EncypherApollo(string clearText, out byte[] initializationVector) { string result = string.Empty; initializationVector = null; if (!string.IsNullOrEmpty(clearText)) { byte[] bytes = Encoding.UTF8.GetBytes(clearText); result = EncypherApollo(bytes, out initializationVector).ToBase64(); } return result; } public byte[] ReEncypherApollo(byte[] clearData, byte[] initializationVector) { byte[] result = null; if (clearData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { cryptoStream.Write(clearData, 0, clearData.Length); cryptoStream.Close(); } result = memoryStream.ToArray(); } return result; } public string ReEncypherApollo(string clearText, byte[] initializationVector) { string result = string.Empty; if (!string.IsNullOrEmpty(clearText)) { byte[] bytes = Encoding.UTF8.GetBytes(clearText); result = ReEncypherApollo(bytes, initializationVector).ToBase64(); } return result; } public byte[] DecypherApollo(byte[] cypherData, byte[] initializationVector) { byte[] result = null; if (cypherData != null) { using AesManaged aesManaged = new AesManaged(); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(CryptoKey.ToBase64(), PASSWORDSALT); aesManaged.Key = rfc2898DeriveBytes.GetBytes(16); aesManaged.IV = initializationVector; using ICryptoTransform transform = aesManaged.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cypherData); using CryptoStream cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Read); using MemoryStream memoryStream = new MemoryStream(); cryptoStream.CopyTo(memoryStream); result = memoryStream.ToArray(); } return result; } public string DecypherApollo(string cypherText, byte[] initializationVector) { string result = string.Empty; if (!string.IsNullOrEmpty(cypherText)) { byte[] cypherData = cypherText.FromBase64ToByteArray(); byte[] bytes = DecypherApollo(cypherData, initializationVector); result = Encoding.UTF8.GetString(bytes); } return result; } public byte[] Encypher(byte[] clearData, byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return EncypherBaktun(clearData, initializationVector); } return EncypherApollo(clearData, initializationVector); } public byte[] Encypher(byte[] clearData, out byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return EncypherBaktun(clearData, out initializationVector); } return EncypherApollo(clearData, out initializationVector); } public string Encypher(string clearText, out byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return EncypherBaktun(clearText, out initializationVector); } return EncypherApollo(clearText, out initializationVector); } public byte[] ReEncypher(byte[] clearData, byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return ReEncypherBaktun(clearData, initializationVector); } return ReEncypherApollo(clearData, initializationVector); } public string ReEncypher(string clearText, byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return ReEncypherBaktun(clearText, initializationVector); } return ReEncypherApollo(clearText, initializationVector); } public byte[] Decypher(byte[] cypherData, byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return DecypherBaktun(cypherData, initializationVector); } return DecypherApollo(cypherData, initializationVector); } public string Decypher(string cypherText, byte[] initializationVector) { if (SignatureMethod == SigningMethod.Baktun) { return DecypherBaktun(cypherText, initializationVector); } return DecypherApollo(cypherText, initializationVector); } }