diff --git a/MeshCentralAssistant.csproj b/MeshCentralAssistant.csproj
index 2e9741f..995bc3c 100644
--- a/MeshCentralAssistant.csproj
+++ b/MeshCentralAssistant.csproj
@@ -70,6 +70,7 @@
+
@@ -162,6 +163,7 @@
SessionsForm.cs
+
Form
diff --git a/Program.cs b/Program.cs
index f3b2710..d9b04db 100644
--- a/Program.cs
+++ b/Program.cs
@@ -25,6 +25,18 @@ static class Program
{
[ThreadStatic]
public static readonly bool IsMainThread = true;
+ public static Uri signedUrl = null;
+
+ public class CurrentAppContext : ApplicationContext
+ {
+ private static CurrentAppContext _currContext;
+
+ public CurrentAppContext() { if (_currContext == null) { _currContext = this; } }
+
+ public CurrentAppContext(Form AppMainForm) : this() { this.MainForm = AppMainForm; }
+
+ public CurrentAppContext CurrentContext { get { return _currContext; } }
+ }
///
/// The main entry point for the application.
@@ -32,6 +44,9 @@ static class Program
[STAThread]
static void Main(string[] args)
{
+ // If this application is signed, get the URL of the signature, this will be used to lock this application to a server.
+ signedUrl = WinCrypt.GetSignatureUrl(System.Reflection.Assembly.GetEntryAssembly().Location);
+
if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
string update = null;
@@ -106,4 +121,5 @@ public static void UnhandledExceptionEventSink(object sender, UnhandledException
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
}
+
}
diff --git a/WinCrypt.cs b/WinCrypt.cs
new file mode 100644
index 0000000..80f0c51
--- /dev/null
+++ b/WinCrypt.cs
@@ -0,0 +1,382 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.Pkcs;
+using System.Security.Cryptography;
+
+namespace MeshAssistant
+{
+ static class WinCrypt
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct BLOB
+ {
+ public int cbData;
+ public IntPtr pbData;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPT_ALGORITHM_IDENTIFIER
+ {
+ public String pszObjId;
+ BLOB Parameters;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CERT_ID
+ {
+ public int dwIdChoice;
+ public BLOB IssuerSerialNumberOrKeyIdOrHashId;
+ }
+
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ public struct SIGNER_SUBJECT_INFO
+ {
+ /// DWORD->unsigned int
+ public uint cbSize;
+
+ /// DWORD*
+ public System.IntPtr pdwIndex;
+
+ /// DWORD->unsigned int
+ public uint dwSubjectChoice;
+
+ /// SubjectChoiceUnion
+ public SubjectChoiceUnion Union1;
+ }
+
+ [StructLayoutAttribute(LayoutKind.Explicit)]
+ public struct SubjectChoiceUnion
+ {
+
+ /// SIGNER_FILE_INFO*
+ [FieldOffsetAttribute(0)]
+ public System.IntPtr pSignerFileInfo;
+
+ /// SIGNER_BLOB_INFO*
+ [FieldOffsetAttribute(0)]
+ public System.IntPtr pSignerBlobInfo;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CERT_NAME_BLOB
+ {
+ public uint cbData;
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
+ public byte[] pbData;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPT_INTEGER_BLOB
+ {
+ public UInt32 cbData;
+ public IntPtr pbData;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPT_ATTR_BLOB
+ {
+ public uint cbData;
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
+ public byte[] pbData;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPT_ATTRIBUTE
+ {
+ [MarshalAs(UnmanagedType.LPStr)]
+ public string pszObjId;
+ public uint cValue;
+ [MarshalAs(UnmanagedType.LPStruct)]
+ public CRYPT_ATTR_BLOB rgValue;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CMSG_SIGNER_INFO
+ {
+ public int dwVersion;
+ private CERT_NAME_BLOB Issuer;
+ CRYPT_INTEGER_BLOB SerialNumber;
+ CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
+ CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
+ BLOB EncryptedHash;
+ CRYPT_ATTRIBUTE[] AuthAttrs;
+ CRYPT_ATTRIBUTE[] UnauthAttrs;
+ }
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptQueryObject(
+ int dwObjectType,
+ IntPtr pvObject,
+ int dwExpectedContentTypeFlags,
+ int dwExpectedFormatTypeFlags,
+ int dwFlags,
+ out int pdwMsgAndCertEncodingType,
+ out int pdwContentType,
+ out int pdwFormatType,
+ ref IntPtr phCertStore,
+ ref IntPtr phMsg,
+ ref IntPtr ppvContext);
+
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptMsgGetParam(
+ IntPtr hCryptMsg,
+ int dwParamType,
+ int dwIndex,
+ IntPtr pvData,
+ ref int pcbData
+ );
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptMsgGetParam(
+ IntPtr hCryptMsg,
+ int dwParamType,
+ int dwIndex,
+ [In, Out] byte[] vData,
+ ref int pcbData
+ );
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CryptDecodeObject(
+ uint CertEncodingType,
+ UIntPtr lpszStructType,
+ byte[] pbEncoded,
+ uint cbEncoded,
+ uint flags,
+ [In, Out] byte[] pvStructInfo,
+ ref uint cbStructInfo);
+
+
+ public const int CRYPT_ASN_ENCODING = 0x00000001;
+ public const int CRYPT_NDR_ENCODING = 0x00000002;
+ public const int X509_ASN_ENCODING = 0x00000001;
+ public const int X509_NDR_ENCODING = 0x00000002;
+ public const int PKCS_7_ASN_ENCODING = 0x00010000;
+ public const int PKCS_7_NDR_ENCODING = 0x00020000;
+
+ public static UIntPtr PKCS7_SIGNER_INFO = new UIntPtr(500);
+ public static UIntPtr CMS_SIGNER_INFO = new UIntPtr(501);
+
+ public static string szOID_RSA_signingTime = "1.2.840.113549.1.9.5";
+ public static string szOID_RSA_counterSign = "1.2.840.113549.1.9.6";
+
+ //+-------------------------------------------------------------------------
+ // Get parameter types and their corresponding data structure definitions.
+ //--------------------------------------------------------------------------
+ public const int CMSG_TYPE_PARAM = 1;
+ public const int CMSG_CONTENT_PARAM = 2;
+ public const int CMSG_BARE_CONTENT_PARAM = 3;
+ public const int CMSG_INNER_CONTENT_TYPE_PARAM = 4;
+ public const int CMSG_SIGNER_COUNT_PARAM = 5;
+ public const int CMSG_SIGNER_INFO_PARAM = 6;
+ public const int CMSG_SIGNER_CERT_INFO_PARAM = 7;
+ public const int CMSG_SIGNER_HASH_ALGORITHM_PARAM = 8;
+ public const int CMSG_SIGNER_AUTH_ATTR_PARAM = 9;
+ public const int CMSG_SIGNER_UNAUTH_ATTR_PARAM = 10;
+ public const int CMSG_CERT_COUNT_PARAM = 11;
+ public const int CMSG_CERT_PARAM = 12;
+ public const int CMSG_CRL_COUNT_PARAM = 13;
+ public const int CMSG_CRL_PARAM = 14;
+ public const int CMSG_ENVELOPE_ALGORITHM_PARAM = 15;
+ public const int CMSG_RECIPIENT_COUNT_PARAM = 17;
+ public const int CMSG_RECIPIENT_INDEX_PARAM = 18;
+ public const int CMSG_RECIPIENT_INFO_PARAM = 19;
+ public const int CMSG_HASH_ALGORITHM_PARAM = 20;
+ public const int CMSG_HASH_DATA_PARAM = 21;
+ public const int CMSG_COMPUTED_HASH_PARAM = 22;
+ public const int CMSG_ENCRYPT_PARAM = 26;
+ public const int CMSG_ENCRYPTED_DIGEST = 27;
+ public const int CMSG_ENCODED_SIGNER = 28;
+ public const int CMSG_ENCODED_MESSAGE = 29;
+ public const int CMSG_VERSION_PARAM = 30;
+ public const int CMSG_ATTR_CERT_COUNT_PARAM = 31;
+ public const int CMSG_ATTR_CERT_PARAM = 32;
+ public const int CMSG_CMS_RECIPIENT_COUNT_PARAM = 33;
+ public const int CMSG_CMS_RECIPIENT_INDEX_PARAM = 34;
+ public const int CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM = 35;
+ public const int CMSG_CMS_RECIPIENT_INFO_PARAM = 36;
+ public const int CMSG_UNPROTECTED_ATTR_PARAM = 37;
+ public const int CMSG_SIGNER_CERT_ID_PARAM = 38;
+ public const int CMSG_CMS_SIGNER_INFO_PARAM = 39;
+
+
+ //-------------------------------------------------------------------------
+ //dwObjectType for CryptQueryObject
+ //-------------------------------------------------------------------------
+ public const int CERT_QUERY_OBJECT_FILE = 0x00000001;
+ public const int CERT_QUERY_OBJECT_BLOB = 0x00000002;
+
+ //-------------------------------------------------------------------------
+ //dwContentType for CryptQueryObject
+ //-------------------------------------------------------------------------
+ //encoded single certificate
+ public const int CERT_QUERY_CONTENT_CERT = 1;
+ //encoded single CTL
+ public const int CERT_QUERY_CONTENT_CTL = 2;
+ //encoded single CRL
+ public const int CERT_QUERY_CONTENT_CRL = 3;
+ //serialized store
+ public const int CERT_QUERY_CONTENT_SERIALIZED_STORE = 4;
+ //serialized single certificate
+ public const int CERT_QUERY_CONTENT_SERIALIZED_CERT = 5;
+ //serialized single CTL
+ public const int CERT_QUERY_CONTENT_SERIALIZED_CTL = 6;
+ //serialized single CRL
+ public const int CERT_QUERY_CONTENT_SERIALIZED_CRL = 7;
+ //a PKCS#7 signed message
+ public const int CERT_QUERY_CONTENT_PKCS7_SIGNED = 8;
+ //a PKCS#7 message, such as enveloped message. But it is not a signed message,
+ public const int CERT_QUERY_CONTENT_PKCS7_UNSIGNED = 9;
+ //a PKCS7 signed message embedded in a file
+ public const int CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
+ //an encoded PKCS#10
+ public const int CERT_QUERY_CONTENT_PKCS10 = 11;
+ //an encoded PKX BLOB
+ public const int CERT_QUERY_CONTENT_PFX = 12;
+ //an encoded CertificatePair (contains forward and/or reverse cross certs)
+ public const int CERT_QUERY_CONTENT_CERT_PAIR = 13;
+
+ //-------------------------------------------------------------------------
+ //dwExpectedConentTypeFlags for CryptQueryObject
+ //-------------------------------------------------------------------------
+ //encoded single certificate
+ public const int CERT_QUERY_CONTENT_FLAG_CERT = (1 << CERT_QUERY_CONTENT_CERT);
+
+ //encoded single CTL
+ public const int CERT_QUERY_CONTENT_FLAG_CTL = (1 << CERT_QUERY_CONTENT_CTL);
+
+ //encoded single CRL
+ public const int CERT_QUERY_CONTENT_FLAG_CRL = (1 << CERT_QUERY_CONTENT_CRL);
+
+ //serialized store
+ public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE = (1 << CERT_QUERY_CONTENT_SERIALIZED_STORE);
+
+ //serialized single certificate
+ public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT = (1 << CERT_QUERY_CONTENT_SERIALIZED_CERT);
+
+ //serialized single CTL
+ public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CTL);
+
+ //serialized single CRL
+ public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CRL);
+
+ //an encoded PKCS#7 signed message
+ public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED);
+
+ //an encoded PKCS#7 message. But it is not a signed message
+ public const int CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_UNSIGNED);
+
+ //the content includes an embedded PKCS7 signed message
+ public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED);
+
+ //an encoded PKCS#10
+ public const int CERT_QUERY_CONTENT_FLAG_PKCS10 = (1 << CERT_QUERY_CONTENT_PKCS10);
+
+ //an encoded PFX BLOB
+ public const int CERT_QUERY_CONTENT_FLAG_PFX = (1 << CERT_QUERY_CONTENT_PFX);
+
+ //an encoded CertificatePair (contains forward and/or reverse cross certs)
+ public const int CERT_QUERY_CONTENT_FLAG_CERT_PAIR = (1 << CERT_QUERY_CONTENT_CERT_PAIR);
+
+ //content can be any type
+ public const int CERT_QUERY_CONTENT_FLAG_ALL =
+ CERT_QUERY_CONTENT_FLAG_CERT |
+ CERT_QUERY_CONTENT_FLAG_CTL |
+ CERT_QUERY_CONTENT_FLAG_CRL |
+ CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
+ CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
+ CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
+ CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
+ CERT_QUERY_CONTENT_FLAG_PKCS10 |
+ CERT_QUERY_CONTENT_FLAG_PFX |
+ CERT_QUERY_CONTENT_FLAG_CERT_PAIR;
+
+ //-------------------------------------------------------------------------
+ //dwFormatType for CryptQueryObject
+ //-------------------------------------------------------------------------
+ //the content is in binary format
+ public const int CERT_QUERY_FORMAT_BINARY = 1;
+
+ //the content is base64 encoded
+ public const int CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
+
+ //the content is ascii hex encoded with "{ASN}" prefix
+ public const int CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
+
+ //-------------------------------------------------------------------------
+ //dwExpectedFormatTypeFlags for CryptQueryObject
+ //-------------------------------------------------------------------------
+ //the content is in binary format
+ public const int CERT_QUERY_FORMAT_FLAG_BINARY = (1 << CERT_QUERY_FORMAT_BINARY);
+
+ //the content is base64 encoded
+ public const int CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = (1 << CERT_QUERY_FORMAT_BASE64_ENCODED);
+
+ //the content is ascii hex encoded with "{ASN}" prefix
+ public const int CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED);
+
+ //the content can be of any format
+ public const int CERT_QUERY_FORMAT_FLAG_ALL =
+ CERT_QUERY_FORMAT_FLAG_BINARY |
+ CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED |
+ CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;
+
+
+ public static Uri GetSignatureUrl(string filename)
+ {
+ try
+ {
+ int encodingType;
+ int contentType;
+ int formatType;
+ IntPtr certStore = IntPtr.Zero;
+ IntPtr cryptMsg = IntPtr.Zero;
+ IntPtr context = IntPtr.Zero;
+
+ if (!WinCrypt.CryptQueryObject(WinCrypt.CERT_QUERY_OBJECT_FILE, Marshal.StringToHGlobalUni(filename), WinCrypt.CERT_QUERY_CONTENT_FLAG_ALL, WinCrypt.CERT_QUERY_FORMAT_FLAG_ALL, 0, out encodingType, out contentType, out formatType, ref certStore, ref cryptMsg, ref context)) { return null; }
+
+ // Get size of the encoded message.
+ int cbData = 0;
+ if (!WinCrypt.CryptMsgGetParam(cryptMsg, WinCrypt.CMSG_ENCODED_MESSAGE, 0, IntPtr.Zero, ref cbData)) { return null; }
+ var vData = new byte[cbData];
+
+ // Get the encoded message.
+ if (!WinCrypt.CryptMsgGetParam(cryptMsg, WinCrypt.CMSG_ENCODED_MESSAGE, 0, vData, ref cbData)) { return null; }
+
+ var signedCms = new SignedCms();
+ signedCms.Decode(vData);
+
+ foreach (var signerInfo in signedCms.SignerInfos)
+ {
+ foreach (CryptographicAttributeObject signedAttribute in signerInfo.SignedAttributes)
+ {
+ if (signedAttribute.Oid.Value == "1.3.6.1.4.1.311.2.1.12")
+ {
+ foreach (AsnEncodedData x in signedAttribute.Values)
+ {
+ string z = x.Format(true);
+ int i = z.IndexOf("68 74 74 70 73 3a 2f 2f"); // "https://"
+ if (i >= 0) { return new Uri(System.Text.UTF8Encoding.UTF8.GetString(FromHex(z.Substring(i)))); }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception) { }
+ return null;
+ }
+
+ private static byte[] FromHex(string hex)
+ {
+ hex = hex.Replace(" ", "");
+ byte[] raw = new byte[hex.Length / 2];
+ for (int i = 0; i < raw.Length; i++) { raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); }
+ return raw;
+ }
+
+ }
+}
\ No newline at end of file