mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-23 01:28:36 +08:00
295 lines
9.1 KiB
C#
295 lines
9.1 KiB
C#
#if !PORTABLE
|
|
using System;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Cryptography;
|
|
using System.Reflection;
|
|
using System.ComponentModel;
|
|
// ReSharper disable UnusedMember.Local
|
|
// ReSharper disable NotAccessedField.Local
|
|
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
|
#pragma warning disable 414
|
|
#pragma warning disable 169
|
|
|
|
|
|
namespace mRemoteNG.Tools
|
|
{
|
|
public class Authenticode
|
|
{
|
|
#region Public Methods
|
|
public Authenticode(string filePath)
|
|
{
|
|
FilePath = filePath;
|
|
}
|
|
|
|
public StatusValue Verify()
|
|
{
|
|
var trustFileInfoPointer = default(IntPtr);
|
|
var trustDataPointer = default(IntPtr);
|
|
try
|
|
{
|
|
var fileInfo = new FileInfo(FilePath);
|
|
if (!fileInfo.Exists)
|
|
{
|
|
Status = StatusValue.FileNotExist;
|
|
return Status;
|
|
}
|
|
if (fileInfo.Length == 0)
|
|
{
|
|
Status = StatusValue.FileEmpty;
|
|
return Status;
|
|
}
|
|
|
|
if (RequireThumbprintMatch)
|
|
{
|
|
if (string.IsNullOrEmpty(ThumbprintToMatch))
|
|
{
|
|
Status = StatusValue.NoThumbprintToMatch;
|
|
return Status;
|
|
}
|
|
|
|
var certificate2 = new X509Certificate2(X509Certificate.CreateFromSignedFile(FilePath));
|
|
_thumbprint = certificate2.Thumbprint;
|
|
if (_thumbprint != ThumbprintToMatch)
|
|
{
|
|
Status = StatusValue.ThumbprintNotMatch;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
var trustFileInfo = new NativeMethods.WINTRUST_FILE_INFO {pcwszFilePath = FilePath};
|
|
trustFileInfoPointer = Marshal.AllocCoTaskMem(Marshal.SizeOf(trustFileInfo));
|
|
Marshal.StructureToPtr(trustFileInfo, trustFileInfoPointer, false);
|
|
|
|
var trustData = new NativeMethods.WINTRUST_DATA
|
|
{
|
|
dwUIChoice = (uint) Display,
|
|
fdwRevocationChecks = NativeMethods.WTD_REVOKE_WHOLECHAIN,
|
|
dwUnionChoice = NativeMethods.WTD_CHOICE_FILE,
|
|
pFile = trustFileInfoPointer,
|
|
dwStateAction = NativeMethods.WTD_STATEACTION_IGNORE,
|
|
dwProvFlags = NativeMethods.WTD_DISABLE_MD2_MD4,
|
|
dwUIContext = (uint) DisplayContext
|
|
};
|
|
trustDataPointer = Marshal.AllocCoTaskMem(Marshal.SizeOf(trustData));
|
|
Marshal.StructureToPtr(trustData, trustDataPointer, false);
|
|
|
|
var windowHandle = DisplayParentForm?.Handle ?? IntPtr.Zero;
|
|
|
|
_trustProviderErrorCode = NativeMethods.WinVerifyTrust(windowHandle, NativeMethods.WINTRUST_ACTION_GENERIC_VERIFY_V2, trustDataPointer);
|
|
// ReSharper disable once SwitchStatementMissingSomeCases
|
|
switch (_trustProviderErrorCode)
|
|
{
|
|
case NativeMethods.TRUST_E_NOSIGNATURE:
|
|
Status = StatusValue.NoSignature;
|
|
break;
|
|
case NativeMethods.TRUST_E_SUBJECT_NOT_TRUSTED:
|
|
break;
|
|
|
|
}
|
|
if (_trustProviderErrorCode != 0)
|
|
{
|
|
Status = StatusValue.TrustProviderError;
|
|
return Status;
|
|
}
|
|
|
|
Status = StatusValue.Verified;
|
|
return Status;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
if (ex is CryptographicException)
|
|
{
|
|
var hResultProperty = ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
if (hResultProperty != null)
|
|
{
|
|
var hResult = Convert.ToInt32(hResultProperty.GetValue(ex, null));
|
|
if (hResult == NativeMethods.CRYPT_E_NO_MATCH)
|
|
{
|
|
Status = StatusValue.NoSignature;
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
// other exception, or hResultProperty is null or is not CRYPT_E_NO_MATCH
|
|
Status = StatusValue.UnhandledException;
|
|
Exception = ex;
|
|
return Status;
|
|
}
|
|
finally
|
|
{
|
|
if (trustDataPointer != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeCoTaskMem(trustDataPointer);
|
|
}
|
|
if (trustFileInfoPointer != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeCoTaskMem(trustFileInfoPointer);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
private DisplayValue Display { get; set; } = DisplayValue.None;
|
|
|
|
private DisplayContextValue DisplayContext {get; set;}
|
|
private Form DisplayParentForm {get; set;}
|
|
internal Exception Exception {get; private set;}
|
|
private string FilePath {get; set;}
|
|
internal bool RequireThumbprintMatch { get; set;}
|
|
|
|
internal StatusValue Status { get; private set; }
|
|
|
|
public string GetStatusMessage()
|
|
{
|
|
// ReSharper disable once SwitchStatementMissingSomeCases
|
|
switch (Status)
|
|
{
|
|
case StatusValue.Verified:
|
|
return "The file was verified successfully.";
|
|
case StatusValue.FileNotExist:
|
|
return "The specified file does not exist.";
|
|
case StatusValue.FileEmpty:
|
|
return "The specified file is empty.";
|
|
case StatusValue.NoSignature:
|
|
return "The specified file is not digitally signed.";
|
|
case StatusValue.NoThumbprintToMatch:
|
|
return "A thumbprint match is required but no thumbprint to match against was specified.";
|
|
case StatusValue.ThumbprintNotMatch:
|
|
/* (char)0x2260 == the "not equal to" symbol (which I cannot print in here without changing the encoding of the file)
|
|
* Fancy...
|
|
*
|
|
* "<>" is fiarly cryptic for non-programers
|
|
* So is "!="
|
|
* "=/=" gets the job done, no?
|
|
* What about plain old English (or localized value): X is not equal to Y?
|
|
* :P
|
|
*/
|
|
return $"The thumbprint does not match. {_thumbprint} {(char) 0x2260} {ThumbprintToMatch}.";
|
|
case StatusValue.TrustProviderError:
|
|
var ex = new Win32Exception(_trustProviderErrorCode);
|
|
return $"The trust provider returned an error. {ex.Message}";
|
|
case StatusValue.UnhandledException:
|
|
return $"An unhandled exception occurred. {Exception.Message}";
|
|
default:
|
|
return "The status is unknown.";
|
|
}
|
|
}
|
|
|
|
private string _thumbprint;
|
|
|
|
internal string ThumbprintToMatch { get; set;}
|
|
|
|
private int _trustProviderErrorCode;
|
|
|
|
#endregion
|
|
|
|
#region Public Enums
|
|
|
|
private enum DisplayValue : uint
|
|
{
|
|
Unknown = 0,
|
|
All = NativeMethods.WTD_UI_ALL,
|
|
None = NativeMethods.WTD_UI_NONE,
|
|
NoBad = NativeMethods.WTD_UI_NOBAD,
|
|
NoGood = NativeMethods.WTD_UI_NOGOOD
|
|
}
|
|
|
|
private enum DisplayContextValue : uint
|
|
{
|
|
Execute = NativeMethods.WTD_UICONTEXT_EXECUTE,
|
|
Install = NativeMethods.WTD_UICONTEXT_INSTALL
|
|
}
|
|
|
|
public enum StatusValue
|
|
{
|
|
Unknown = 0,
|
|
Verified,
|
|
FileNotExist,
|
|
FileEmpty,
|
|
NoSignature,
|
|
NoThumbprintToMatch,
|
|
ThumbprintNotMatch,
|
|
TrustProviderError,
|
|
UnhandledException
|
|
}
|
|
#endregion
|
|
|
|
#region Protected Classes
|
|
|
|
private static class NativeMethods
|
|
{
|
|
// ReSharper disable InconsistentNaming
|
|
[DllImport("wintrust.dll", CharSet = CharSet.Auto, SetLastError = false)]
|
|
public static extern int WinVerifyTrust([In()]IntPtr hWnd, [In(), MarshalAs(UnmanagedType.LPStruct)]Guid pgActionOID, [In()]IntPtr pWVTData);
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
public class WINTRUST_DATA
|
|
{
|
|
public WINTRUST_DATA()
|
|
{
|
|
cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA));
|
|
}
|
|
|
|
private uint cbStruct;
|
|
public IntPtr pPolicyCallbackData;
|
|
public IntPtr pSIPClientData;
|
|
public uint dwUIChoice;
|
|
public uint fdwRevocationChecks;
|
|
public uint dwUnionChoice;
|
|
public IntPtr pFile;
|
|
public uint dwStateAction;
|
|
public IntPtr hWVTStateData;
|
|
public IntPtr pwszURLReference;
|
|
public uint dwProvFlags;
|
|
public uint dwUIContext;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
public class WINTRUST_FILE_INFO
|
|
{
|
|
public WINTRUST_FILE_INFO()
|
|
{
|
|
cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
|
|
}
|
|
|
|
private uint cbStruct;
|
|
[MarshalAs(UnmanagedType.LPTStr)]public string pcwszFilePath;
|
|
public IntPtr hFile;
|
|
public IntPtr pgKnownSubject;
|
|
}
|
|
|
|
public const int CRYPT_E_NO_MATCH = unchecked ((int) 0x80092009);
|
|
|
|
public const int TRUST_E_SUBJECT_NOT_TRUSTED = unchecked ((int) 0x800B0004);
|
|
public const int TRUST_E_NOSIGNATURE = unchecked ((int) 0x800B0100);
|
|
|
|
public static readonly Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
|
|
|
|
public const uint WTD_CHOICE_FILE = 1;
|
|
public const uint WTD_DISABLE_MD2_MD4 = 0x2000;
|
|
public const uint WTD_REVOKE_WHOLECHAIN = 1;
|
|
|
|
public const uint WTD_STATEACTION_IGNORE = 0x0;
|
|
public const uint WTD_STATEACTION_VERIFY = 0x1;
|
|
public const uint WTD_STATEACTION_CLOSE = 0x2;
|
|
|
|
public const uint WTD_UI_ALL = 1;
|
|
public const uint WTD_UI_NONE = 2;
|
|
public const uint WTD_UI_NOBAD = 3;
|
|
public const uint WTD_UI_NOGOOD = 4;
|
|
|
|
public const uint WTD_UICONTEXT_EXECUTE = 0;
|
|
public const uint WTD_UICONTEXT_INSTALL = 1;
|
|
// ReSharper restore InconsistentNaming
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
#endif |