mirror of
https://github.com/Ylianst/MeshCentralRouter
synced 2025-12-06 00:13:33 +00:00
Added code signing hostname lock.
This commit is contained in:
@@ -295,6 +295,7 @@
|
||||
</Compile>
|
||||
<Compile Include="src\WebSocketClient.cs" />
|
||||
<Compile Include="src\Win32Api.cs" />
|
||||
<Compile Include="src\WinCrypt.cs" />
|
||||
<EmbeddedResource Include="src\AddPortMapForm.resx">
|
||||
<DependentUpon>AddPortMapForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
|
||||
20
Program.cs
20
Program.cs
@@ -15,20 +15,40 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MeshCentralRouter
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
public static string LockToHostname = null;
|
||||
public static string LockToServerId = null;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[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.
|
||||
Uri signedUrl = WinCrypt.GetSignatureUrl(System.Reflection.Assembly.GetEntryAssembly().Location);
|
||||
if (signedUrl != null)
|
||||
{
|
||||
NameValueCollection urlArguments = HttpUtility.ParseQueryString(signedUrl.Query);
|
||||
if (urlArguments["serverid"] != null)
|
||||
{
|
||||
LockToServerId = urlArguments["serverid"];
|
||||
LockToHostname = signedUrl.Host;
|
||||
}
|
||||
}
|
||||
|
||||
LockToHostname = "central.mesh.meshcentral.com";
|
||||
LockToServerId = "D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D707403E396CF0EF6DC2B3A42F735135FD";
|
||||
|
||||
Uri authLoginUrl = null;
|
||||
|
||||
// Setup settings & visual style
|
||||
|
||||
27
Properties/Resources.Designer.cs
generated
27
Properties/Resources.Designer.cs
generated
@@ -536,6 +536,24 @@ namespace MeshCentralRouter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Locked to host: {0}..
|
||||
/// </summary>
|
||||
internal static string LockedToHost {
|
||||
get {
|
||||
return ResourceManager.GetString("LockedToHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Locked to server id: {0}..
|
||||
/// </summary>
|
||||
internal static string LockedToServerId {
|
||||
get {
|
||||
return ResourceManager.GetString("LockedToServerId", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to , {0} connections..
|
||||
/// </summary>
|
||||
@@ -836,6 +854,15 @@ namespace MeshCentralRouter.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This executable is locked to only connect to {0}..
|
||||
/// </summary>
|
||||
internal static string SignedExecutableServerLockError {
|
||||
get {
|
||||
return ResourceManager.GetString("SignedExecutableServerLockError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Slow.
|
||||
/// </summary>
|
||||
|
||||
@@ -457,4 +457,13 @@
|
||||
<data name="icon-monitor1" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\icon-monitor1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="LockedToHost" xml:space="preserve">
|
||||
<value>Locked to host: {0}.</value>
|
||||
</data>
|
||||
<data name="LockedToServerId" xml:space="preserve">
|
||||
<value>Locked to server id: {0}.</value>
|
||||
</data>
|
||||
<data name="SignedExecutableServerLockError" xml:space="preserve">
|
||||
<value>This executable is locked to only connect to {0}.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -9,6 +9,7 @@ MeshCentral Router is a Windows TCP/UDP port mapping tool. You first needs to ha
|
||||
A typical use of this tool is to perform a Remote Desktop RDP session with another computer over the Internet without the need to set a VPN.
|
||||
|
||||
## Video Tutorials
|
||||
|
||||
You can watch many tutorial videos on the [MeshCentral YouTube Channel](https://www.youtube.com/channel/UCJWz607A8EVlkilzcrb-GKg/videos). MeshCentral Router has it's own introduction video.
|
||||
|
||||
Introduction to MeshCentral Router.
|
||||
|
||||
@@ -269,6 +269,15 @@ namespace MeshCentralRouter
|
||||
// Set automatic port map values
|
||||
if (authLoginUrl != null)
|
||||
{
|
||||
// Check if we are locked to a server
|
||||
if ((Program.LockToHostname != null) && (Program.LockToHostname != authLoginUrl.Host))
|
||||
{
|
||||
forceExit = true;
|
||||
MessageBox.Show(string.Format(Properties.Resources.SignedExecutableServerLockError, Program.LockToHostname), Properties.Resources.MeshCentralRouter, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
Application.Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
string autoName = null;
|
||||
string autoNodeId = null;
|
||||
string autoRemoteIp = null;
|
||||
@@ -507,6 +516,30 @@ namespace MeshCentralRouter
|
||||
|
||||
private void nextButton1_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Check if we are locked to a server
|
||||
if (Program.LockToHostname != null)
|
||||
{
|
||||
bool ok = true;
|
||||
if (authLoginUrl != null)
|
||||
{
|
||||
ok = (Program.LockToHostname == authLoginUrl.Host);
|
||||
}
|
||||
else
|
||||
{
|
||||
string host = serverNameComboBox.Text;
|
||||
int i = host.IndexOf("?key=");
|
||||
if (i >= 0) { host = host.Substring(0, i); }
|
||||
i = host.IndexOf(":");
|
||||
if (i >= 0) { host = host.Substring(0, i); }
|
||||
ok = (Program.LockToHostname == host);
|
||||
}
|
||||
if (ok == false)
|
||||
{
|
||||
MessageBox.Show(string.Format(Properties.Resources.SignedExecutableServerLockError, Program.LockToHostname), Properties.Resources.MeshCentralRouter, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to login
|
||||
addButton.Enabled = false;
|
||||
addRelayButton.Enabled = false;
|
||||
@@ -1064,6 +1097,13 @@ namespace MeshCentralRouter
|
||||
|
||||
Uri authLoginUrl2 = new Uri(args);
|
||||
|
||||
// Check if we are locked to a server
|
||||
if ((Program.LockToHostname != null) && (Program.LockToHostname != authLoginUrl2.Host))
|
||||
{
|
||||
MessageBox.Show(string.Format(Properties.Resources.SignedExecutableServerLockError, Program.LockToHostname), Properties.Resources.MeshCentralRouter, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set automatic port map values
|
||||
if (authLoginUrl2 != null)
|
||||
{
|
||||
|
||||
382
src/WinCrypt.cs
Normal file
382
src/WinCrypt.cs
Normal file
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.Pkcs;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace MeshCentralRouter
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user