Using X509Certificate constructor with IntPtr generates MethodAccessException - c#

My Silverlight app needs to access X509Store through native methods like this :
public class CapiNative
{
public const string MY = "MY";
public const uint PKCS_7_ASN_ENCODING = 0x00010000;
public const uint X509_ASN_ENCODING = 0x00000001;
public const uint CERT_FIND_SUBJECT_STR = 0x00080007;
public const int ACCESS_DENIED = 5;
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertOpenSystemStore(
IntPtr hCryptProv,
string storename);
[DllImport("crypt32.dll", SetLastError = true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
uint dwCertEncodingType,
uint dwFindFlags,
uint dwFindType,
[In, MarshalAs(UnmanagedType.LPWStr)]String pszFindString,
IntPtr pPrevCertCntxt);
internal static void CertEnumCertificatesInStore(IntPtr storeHandle, IntPtr certHandle)
{
throw new NotImplementedException();
}
}
public IntPtr FindCert(ref string err)
{
IntPtr storeHandle = CapiNative.CertOpenSystemStore(
IntPtr.Zero,
CapiNative.MY);
if (Marshal.GetLastWin32Error() == CapiNative.ACCESS_DENIED)
{
err = "Access Denied to the X509Store";
return IntPtr.Zero;
}
try
{
IntPtr certHandle = CapiNative.CertFindCertificateInStore(
storeHandle,
CapiNative.PKCS_7_ASN_ENCODING | CapiNative.X509_ASN_ENCODING,
0,
CapiNative.CERT_FIND_SUBJECT_STR,
_subject,
IntPtr.Zero);
X509Certificate foundcert = new X509Certificate(certHandle);
Console.WriteLine("\nFound certificate with SubjectName string \"{0}\"",_subject);
Console.WriteLine("SubjectName:\t{0}", foundcert.Issuer);
Console.WriteLine("Serial No:\t{0}", foundcert.GetSerialNumberString());
Console.WriteLine("HashString:\t{0}" , foundcert.GetCertHashString());
return certHandle;
}
catch (Exception e)
{
err = "Error getting certificate " + e.Message;
return IntPtr.Zero;
}
}
When I'm using the constructor X509Certificate(IntPtr) I get a MethodAccessException, I guess I can't use this method in Silverlight.
I also tried to use this technique :
https://stackoverflow.com/a/17340419/969881
Like this :
public X509Certificate IntPtrToObject(IntPtr ptrToUnwrap, ref string err)
{
if (ptrToUnwrap == IntPtr.Zero)
{
return null;
}
try
{
X509Certificate x509Cert = new X509Certificate();
System.Runtime.InteropServices.Marshal.PtrToStructure(ptrToUnwrap, x509Cert);
return x509;
}
catch (Exception e)
{
err = e.Message;
return null;
}
}
But I get the following error message :
Object contains non-primitive or non-blittable data. The structure
must be specified blittable or have information provided. Parameter
name: structure
Is it possible to parse a X509Certificate with the P/Invoke methods ?

Unfortunately, I'm not a Silverlight expert, and don't know what functionality is stripped there, however you can use workaround:
1) define CERT_CONTEXT structure as follows
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_CONTEXT {
public Int32 dwCertEncodingType;
public IntPtr pbCertEncoded;
public Int32 cbCertEncoded;
public IntPtr pCertInfo;
public IntPtr hCertStore;
}
and when acquire cert handle from CertFindCertificateInStore function (ensure if this is not equals to IntPtr.zero.
2) Then use Marshal.PtrToStructure method to copy pointer to a structure:
CapiNative.CERT_CONTEXT certcontext = (CapiNative.CERT_CONTEXT)Marshal.PtrToStructure(certHandle, typeof(CapiNative.CERT_CONTEXT));
3) then you can transfortm pCertInfo member of CERT_CONTEXT structure to CERT_INFO structure, but I would try to grab raw bytes from this structure:
byte[] rawData = new byte[certContext.cbCertEncoded];
Marshal.Copy(certContext.pbCertEncoded, rawData, 0, rawData.Length);
X509Certificate2 cert = new X509Certificate2(rawData);
BTW, do not forget to close certificate store after completing the certificate access to avoid memory leaks. Store is closed by using a CertCloseStore function.
BTW2, the code part from your original posting:
X509Certificate x509Cert = new X509Certificate();
System.Runtime.InteropServices.Marshal.PtrToStructure(ptrToUnwrap, x509Cert);
return x509;
is incorrect, because PtrToStructure expects a C-style structure with layout information, and its members must be of blittable types. You can't pass managed classes/structures to unmanaged code/memory.

Is there any specific reason why not to use X509Store class in .NET?

Related

Getting bad IntPtr from P/Invoked code

First, I apologize if my title isn't technically accurate. I'm not sure exactly what's happening and that describes it about as well as anything.
I am attempting to decode an SSL certificate for a specific use in a program. I p/invoked all of the necessary CryptoAPI functions and structs and in debug mode, everything is working as expected. However, when the program is run in release mode as a service (that's the purpose of this program), I get an access violation when attempting to decode the extensions. In order to make extensions easier to decode, I have created a generic class that can be used to represent any extension. This class contains an object of type TStruct that represents the underlying structure that the extension is based on. It has an EncryptedValue field of type byte[] that will encrypt/decrypt the extension data and place it in the structure as appropriate. The base class is as follows:
public abstract class ExtensionBase<TStruct> : IExtensionBase where TStruct : new()
{
//The underlying struct
protected TStruct objectData = new TStruct();
//The identifier of the struct, ie: szOID_BASIC_CONSTRAINTS
public string Identifier { get; protected set; }
//An enum representing the structure type, ie: X509_BASIC_CONSTRAINTS
public CertStructType StructureType { get; protected set; }
//Determines if the extension is critical
public bool IsCritical { get; protected set; }
//Overridden in any child class to determine if that extension actually contains
//data that should be encoded
public abstract bool HasData { get; }
//Encrypts/decrypts the data from/to the underlying structure
public virtual byte[] EncryptedValue
{
get
{
uint encodedSize = 0;
//PinnedHandle is a class that I wrote to wrap a GCHandle.
//It has an implicit cast to IntPtr that returns GCHandle.AddrOfPinnedObject
//The finalizer of the class releases the GCHandle if it is a valid handle
IntPtr dataPtr = new PinnedHandle(objectData);
byte[] retVal = null;
if (StructureType != CertStructType.None)
{
if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn,
(uint)StructureType,
dataPtr,
0,
IntPtr.Zero,
null,
ref encodedSize))
throw new Win32Exception();
retVal = new byte[encodedSize];
if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn,
(uint)StructureType,
dataPtr,
0,
IntPtr.Zero,
retVal,
ref encodedSize))
throw new Win32Exception();
}
else
{
if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn,
Identifier,
dataPtr,
0,
IntPtr.Zero,
null,
ref encodedSize))
throw new Win32Exception();
retVal = new byte[encodedSize];
if (!Crypt32.CryptEncodeObjectEx((uint)CertEncoding.X509Asn,
Identifier,
dataPtr,
0,
IntPtr.Zero,
retVal,
ref encodedSize))
throw new Win32Exception();
}
return retVal;
}
set
{
uint decodedSize = 0;
IntPtr decodedData = IntPtr.Zero;
if(StructureType != CertStructType.None)
decodedData = Crypt32.CryptDecodeObjectEx(StructureType, value);
else
decodedData = Crypt32.CryptDecodeObjectEx(Identifier, value);
TStruct data = (TStruct)Marshal.PtrToStructure(decodedData, typeof(TStruct));
objectData = data;
Marshal.FreeHGlobal(decodedData);
}
}
public ExtensionBase(string id)
{
Identifier = id;
StructureType = CertStructType.None;
}
public ExtensionBase(string id, CertStructType structType)
{
Identifier = id;
StructureType = structType;
}
}
One of the child classes that is giving me problems is the CertKeyUsage class which uses a CRYPT_BIT_BLOB struct to represent its data:
public class CertKeyUsage : ExtensionBase<CertKeyUsageFlags, CRYPT_BIT_BLOB>
{
public override bool HasData
{
get { return Value != CertKeyUsageFlags.None; }
}
public override unsafe byte[] EncryptedValue
{
get
{
CertKeyUsageFlags flags = Value;
objectData.cbData = 2;
objectData.cUnusedBits = 0;
objectData.pbData = new IntPtr(&flags);
return base.EncryptedValue;
}
set
{
try
{
//The following code was taken directly from Microsoft's implementation
//of X509Certificate
base.EncryptedValue = value;
if (objectData.cbData > 4)
objectData.cbData = 4;
byte[] keyUsage = new byte[4];
//This if statement returns true, and the following Marshal.Copy statement
//is where the program crashes with the Access Violation. As it is an unmanaged
//exception, the try/catch block doesn't do anything and the app dies
if (objectData.pbData != IntPtr.Zero)
Marshal.Copy(objectData.pbData, keyUsage, 0, (int) objectData.cbData);
Value = (CertKeyUsageFlags) BitConverter.ToUInt32(keyUsage, 0);
}
catch
{
}
}
}
public CertKeyUsage()
: base(CertOid.szOID_KEY_USAGE, CertStructType.X509KeyUsage)
{
IsCritical = true;
}
}
What about this code would be different from debug to release that would cause an access violation at the spot noted above? What could I do differently to get it working properly. This particular extension isn't critical and I could just skip over it, but after commenting out the code above that is causing the exception, another of the extensions will crash. I can't comment out all extensions, and the fact that it's moving to a different location tells me that there is some underlying problem with my code that I'm missing. Any help or suggestions would be greatly appreciated.
These are the p/invoked functions I'm calling:
[DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool CryptEncodeObjectEx(uint certEncodingType,
[MarshalAs(UnmanagedType.LPStr)]
string structType,
IntPtr structInfo,
uint flags,
IntPtr encodePara,
byte[] encodedData,
[In, Out] ref uint encodedSize);
[DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool CryptEncodeObjectEx(uint certEncodingType,
uint structType,
IntPtr structInfo,
uint flags,
IntPtr encodePara,
byte[] encodedData,
[In, Out] ref uint encodedSize);
[DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool CryptDecodeObjectEx(uint certEncodingType,
[MarshalAs(UnmanagedType.LPStr)]
string structType,
byte[] encodedData,
uint encodedDataSize,
uint flags,
IntPtr encodePara,
IntPtr decodedData,
[In, Out] ref uint decodedDataSize);
[DllImport("crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool CryptDecodeObjectEx(uint certEncodingType,
uint structType,
byte[] encodedData,
uint encodedDataSize,
uint flags,
IntPtr encodePara,
IntPtr decodedData,
[In, Out] ref uint decodedDataSize);
And the function that wraps CryptDecodeObjectEx:
public static IntPtr CryptDecodeObjectEx(string structType, byte[] encodedData)
{
uint encodedSize = (uint)encodedData.Length;
uint decodedSize = 0;
IntPtr decodedData = IntPtr.Zero;
if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn,
structType,
encodedData,
encodedSize,
0,
IntPtr.Zero,
decodedData,
ref decodedSize))
throw new Win32Exception();
decodedData = Marshal.AllocHGlobal((int)decodedSize);
if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn,
structType,
encodedData,
encodedSize,
0,
IntPtr.Zero,
decodedData,
ref decodedSize))
throw new Win32Exception();
return decodedData;
}
public static IntPtr CryptDecodeObjectEx(uint structType, byte[] encodedData)
{
uint encodedSize = (uint)encodedData.Length;
uint decodedSize = 0;
IntPtr decodedData = IntPtr.Zero;
if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn,
structType,
encodedData,
encodedSize,
0,
IntPtr.Zero,
decodedData,
ref decodedSize))
throw new Win32Exception();
decodedData = Marshal.AllocHGlobal((int)decodedSize);
if (!CryptDecodeObjectEx((uint)CertEncoding.X509Asn,
structType,
encodedData,
encodedSize,
0,
IntPtr.Zero,
decodedData,
ref decodedSize))
throw new Win32Exception();
return decodedData;
}
On a whim, I decided to compile the program as X86 rather than AnyCPU and everything works as expected. This leads me to believe that the problem is actually in this section of code:
public static IntPtr Next<T>(this IntPtr val)
{
T retVal = (T)Marshal.PtrToStructure(val, typeof(T));
if (Environment.Is64BitProcess)
return (IntPtr)((long)val + Marshal.SizeOf(retVal));
return (IntPtr)((int)val + Marshal.SizeOf(retVal));
}
This is the code that I use to enumerate through a pointer to an array of structures. The structure type is passed in and an IntPtr to the next structure in the array should be returned. The 32 bit version is working just as it should, but apparently my logic in the 64 bit version is somewhat lacking.

Set existing service to "Auto (Delayed start)"

I'm trying to set an already installed windows service to automatic delayed start in C#. How do I set a windows service to
Automatic (Delayed Start)
Can't find that value in the ServiceStartMode enum.
Edit:1
public class ServiceAutoStartInfo
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
// Service configuration parameter
const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
public bool ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Validate service handle
if (hService != IntPtr.Zero)
{
// Create
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property
info.fDelayedAutostart = delayed;
// Allocate necessary memory
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(
typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory
Marshal.FreeHGlobal(hInfo);
return result;
}
return false;
}
}
This is how I call it:
var controller = new ServiceController(s.ServiceName);
var autoDelay = new ServiceAutoStartInfo();
autoDelay.ChangeDelayedAutoStart(controller.ServiceHandle.DangerousGetHandle(), true);
But with no result.
Look into calling the Windows ChangeServiceConfig2 function, with dwInfoLevel of SERVICE_CONFIG_DELAYED_AUTO_START_INFO and a SERVICE_DELAYED_AUTO_START_INFO struct with fDelayedAutostart set to TRUE.
Or, you can do this with the command line:
sc.exe config <servicename> start= delayed-auto
I'm using the following, which works for me on Windows 7 (when run as admin):
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace ServiceManager
{
/// <summary>
/// Extensions to the ServiceController class.
/// </summary>
public static class ServiceControlerExtensions
{
/// <summary>
/// Set the start mode for the service.
/// </summary>
/// <param name="serviceController">The service controller.</param>
/// <param name="mode">The desired start mode.</param>
public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
{
IntPtr serviceManagerHandle = OpenServiceManagerHandle();
IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);
try
{
if (mode == ServiceStartModeEx.DelayedAutomatic)
{
ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
ChangeDelayedAutoStart(serviceHandle, true);
}
else
{
// Delayed auto-start overrides other settings, so it must be set first.
ChangeDelayedAutoStart(serviceHandle, false);
ChangeServiceStartType(serviceHandle, mode);
}
}
finally
{
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceHandle);
}
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceManagerHandle);
}
}
}
private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
{
var serviceHandle = OpenService(
serviceManagerHandle,
serviceController.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
return serviceHandle;
}
private static IntPtr OpenServiceManagerHandle()
{
IntPtr serviceManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (serviceManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
return serviceManagerHandle;
}
private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
{
bool result = ChangeServiceConfig(
serviceHandle,
SERVICE_NO_CHANGE,
(uint)mode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
ThrowLastWin32Error("Could not change service start type");
}
}
private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Create structure that contains DelayedAutoStart property.
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property in that structure.
info.fDelayedAutostart = delayed;
// Allocate necessary memory.
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer.
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration.
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory.
Marshal.FreeHGlobal(hInfo);
if (result == false)
{
ThrowLastWin32Error("Could not set service to delayed automatic");
}
}
private static void ThrowLastWin32Error(string messagePrefix)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
throw new ExternalException(message);
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(
IntPtr hService,
int dwInfoLevel,
IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
private static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
}
}
namespace ServiceManager
{
public enum ServiceStartModeEx
{
Automatic = 2,
Manual = 3,
Disabled = 4,
DelayedAutomatic = 99
}
}
You call it like this:
var serviceController = new ServiceController("Windows Update");
try
{
serviceController.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
}
finally
{
serviceController.Close();
}
Update: This only works for setting up new services and is not what the OP asked for:
You can use the DelayedAutoStart property of the ServiceInstaller.
installer.DelayedAutoStart = true;
I believe you need to combine both methods ChangeServiceConfig and ChangeServiceConfig2.
pseudo-code follows:
public static void ChangeServiceStartupType(ServiceStartupType type, ...)
{
if (type == AutomaticDelayed)
{
if (ChangeServiceConfig2(.., DelayedAutoStart, ..))
{
ChangeServiceConfig(.., Automatic, ..);
}
}
else
{
ChangeServiceConfig2(.., !DelayedAutoStart, ..)
ChangeServiceConfig(.., type, ..)
}
}
edit: you also need to remove "delayed-automatic" when requesting non-delayed startup-type. Otherwise it won't be possible to set "automatic" type. ("automatic-delayed" overrides "automatic")
SERVICE_DELAYED_AUTO_START_INFO structure documentation states:
fDelayedAutostart
If this member is TRUE, the service is started after other auto-start
services are started plus a short delay. Otherwise, the service is
started during system boot.
This setting is ignored unless the service is an auto-start service.
https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_delayed_auto_start_info?redirectedfrom=MSDN#members
So I think if the service is not auto-start it won't change it to auto delayed, you would have to use sc.exe

Drag & drop from Windows Portable Device to WPF Application

I'm working on a WPF app which allows user to drag and drop files from Windows Explorer. For normal files, I'm able to access the path using
string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
But for WPD files, its returning null. Have tried to follow the solution given in http://us.generation-nt.com/answer/drag-drop-pictures-wpd-camera-help-31497882.html#r , but i couldn't make it work. I'm getting AccessVoilationException when trying to get the count of items from the Shell array. I have posted the question in MSDN(http://social.msdn.microsoft.com/Forums/vstudio/en-US/ef7fc152-dd1b-4774-adb7-47b48726daea/drag-drop-from-windows-portable-device-to-wpf-application?forum=wpf), but didn't get any leads.
Is there something that I'm missing here? Could you please help me solve this issue?
Following is the relevant part my code.
public enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000
}
internal class IIDGuid
{
private IIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses
// IID GUID strings for relevant COM interfaces
internal const string IModalWindow = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802";
internal const string IFileDialog = "42f85136-db7e-439c-85f1-e4075d135fc8";
internal const string IFileOpenDialog = "d57c7288-d4ad-4768-be02-9d969532d960";
internal const string IFileSaveDialog = "84bccd23-5fde-4cdb-aea4-af64b83d78ab";
internal const string IFileDialogEvents = "973510DB-7D7F-452B-8975-74A85828D354";
internal const string IShellItem = "43826D1E-E718-42EE-BC55-A1E261C37BFE";
internal const string IShellItemArray = "B63EA76D-1F85-456F-A19C-48159EFA858B";
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)]Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
};
[ComImport]
[Guid(IIDGuid.IShellItemArray)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemArray
{
// Not supported: IBindCtx
void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);
void GetPropertyStore([In] int Flags, [In] ref Guid riid, out IntPtr ppv);
void GetCount(out uint pdwNumItems);
void GetItemAt([In] uint dwIndex, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern int SHCreateShellItemArrayFromDataObject(
System.Runtime.InteropServices.ComTypes.IDataObject pdo,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out IShellItemArray ppv);
[DllImport("kernel32.dll", SetLastError = true)]
static extern Int32 GetLastError();
private void OnFileDrop(object sender, DragEventArgs e)
{
string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);// null
System.Runtime.InteropServices.ComTypes.IDataObject mydata = e.Data as System.Runtime.InteropServices.ComTypes.IDataObject;
IShellItemArray nativeShellItemArray;
Guid guid = new Guid(IIDGuid.IShellItemArray);
int retCode = SHCreateShellItemArrayFromDataObject(mydata, ref guid, out nativeShellItemArray);
IShellItem nativeShellItem;
if (retCode == 0)
{
IntPtr displayname;
uint items = 0;
try
{
nativeShellItemArray.GetCount(out items); //Getting AccessVoilationException in this line
}
catch (Exception ex)
{
}
if (items > 0)
{
for (uint item = 0; item < items; item++)
{
nativeShellItemArray.GetItemAt(item, out nativeShellItem);
nativeShellItem.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING, out displayname);
//Do something
}
}
}
}

How to dual sign a binary with Authenticode?

Short question
How can I get information about multiple code signing certificates from an executable (.EXE/.DLL)?
Expected answer
The final accepted answer should propose a way to get all certificates in C#. Concept / pseudo code is ok, I don't expect you to write the full source.
For an intermediate answer suggesting a tool, please see my question on Security.StackExchange.
Long question
I am researching whether we could use multiple code signing certificates on a plugin (.DLL) to check whether it has been officially tested or not. This is the procedure:
the plugin DLL is signed by the vendor just like any other application
the plugin DLL comes into a test lab and undergoes a set of tests
the plugin DLL gets signed again by the test lab so that the application using the DLL can find out whether it is using a tested plugin or not
It seems possible to sign a DLL a second time using
signtool /v /f <pfx> /as <dll>
Indications that this may have worked:
the file increases in size
the tool prints a success message
However, there are some issues showing the second signature:
although Windows Explorer says "Signature list", it shows only one certificate
the C# X509Certificate.CreateFromSignedFile() method can only return one certificate
At the moment I'm actually trying my code on an EXE file rather than a DLL file, but that shouldn't matter. The EXE is already signed with a trusted root certificate and a timestamp. The second signature is created with my own certificate following these steps currently without a timestamp.
Things I did before asking the question:
search on Stackoverflow for existing answers
search for tools on Google
The only related question I found so far is How does one correctly dual-sign with a timestamp but it doesn't have an answer.
I have recently implemented code to do this myself. I can't post the full solution as it is embedded in a larger static analysis tool, but the code for a working bare-bones C# console application that enumerates the Authenticode signatures in a specified file path is provided below using the WinVerifyTrust() Windows API function with assistance from this Knowledge Base article.
Things to note:
Enumerating more than one certificate is only supported on Windows 8 and Windows Server 2012 (or later). Earlier versions of Windows will only ever report there being zero or one certificates.
The code as provided here only handles validly signed files and files with no Authenticode signature. It does not properly handle files with invalid signatures. This is left as an excercise for the reader.
Here's the code:
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace ReadAuthenticodeSignatures
{
internal static class Program
{
internal static void Main(string[] args)
{
string fileName = args[0];
IntPtr hWind = IntPtr.Zero;
Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
byte[] actionIdBytes = WINTRUST_ACTION_GENERIC_VERIFY_V2.ToByteArray();
IntPtr pcwszFilePath = Marshal.StringToHGlobalAuto(fileName);
try
{
WINTRUST_FILE_INFO File = new WINTRUST_FILE_INFO()
{
cbStruct = Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)),
pcwszFilePath = pcwszFilePath,
hFile = IntPtr.Zero,
pgKnownSubject = IntPtr.Zero,
};
IntPtr ptrFile = Marshal.AllocHGlobal(File.cbStruct);
try
{
Marshal.StructureToPtr(File, ptrFile, false);
WINTRUST_DATA WVTData = new WINTRUST_DATA()
{
cbStruct = Marshal.SizeOf(typeof(WINTRUST_DATA)),
pPolicyCallbackData = IntPtr.Zero,
pSIPClientData = IntPtr.Zero,
dwUIChoice = WTD_UI_NONE,
fdwRevocationChecks = WTD_REVOKE_NONE,
dwUnionChoice = WTD_CHOICE_FILE,
pFile = ptrFile,
dwStateAction = WTD_STATEACTION_IGNORE,
hWVTStateData = IntPtr.Zero,
pwszURLReference = IntPtr.Zero,
dwProvFlags = WTD_REVOCATION_CHECK_NONE,
dwUIContext = WTD_UICONTEXT_EXECUTE,
pSignatureSettings = IntPtr.Zero,
};
// N.B. Use of this member is only supported on Windows 8 and Windows Server 2012 (and later)
WINTRUST_SIGNATURE_SETTINGS signatureSettings = default(WINTRUST_SIGNATURE_SETTINGS);
bool canUseSignatureSettings = Environment.OSVersion.Version > new Version(6, 2, 0, 0);
IntPtr pSignatureSettings = IntPtr.Zero;
if (canUseSignatureSettings)
{
// Setup WINTRUST_SIGNATURE_SETTINGS to get the number of signatures in the file
signatureSettings = new WINTRUST_SIGNATURE_SETTINGS()
{
cbStruct = Marshal.SizeOf(typeof(WINTRUST_SIGNATURE_SETTINGS)),
dwIndex = 0,
dwFlags = WSS_GET_SECONDARY_SIG_COUNT,
cSecondarySigs = 0,
dwVerifiedSigIndex = 0,
pCryptoPolicy = IntPtr.Zero,
};
pSignatureSettings = Marshal.AllocHGlobal(signatureSettings.cbStruct);
}
try
{
if (pSignatureSettings != IntPtr.Zero)
{
Marshal.StructureToPtr(signatureSettings, pSignatureSettings, false);
WVTData.pSignatureSettings = pSignatureSettings;
}
IntPtr pgActionID = Marshal.AllocHGlobal(actionIdBytes.Length);
try
{
Marshal.Copy(actionIdBytes, 0, pgActionID, actionIdBytes.Length);
IntPtr pWVTData = Marshal.AllocHGlobal(WVTData.cbStruct);
try
{
Marshal.StructureToPtr(WVTData, pWVTData, false);
int hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);
if (hRESULT == 0)
{
if (pSignatureSettings != IntPtr.Zero)
{
// Read back the signature settings
signatureSettings = (WINTRUST_SIGNATURE_SETTINGS)Marshal.PtrToStructure(pSignatureSettings, typeof(WINTRUST_SIGNATURE_SETTINGS));
}
int signatureCount = signatureSettings.cSecondarySigs + 1;
Console.WriteLine("File: {0}", fileName);
Console.WriteLine("Authenticode signatures: {0}", signatureCount);
Console.WriteLine();
for (int dwIndex = 0; dwIndex < signatureCount; dwIndex++)
{
if (pSignatureSettings != IntPtr.Zero)
{
signatureSettings.dwIndex = dwIndex;
signatureSettings.dwFlags = WSS_VERIFY_SPECIFIC;
Marshal.StructureToPtr(signatureSettings, pSignatureSettings, false);
}
WVTData.dwStateAction = WTD_STATEACTION_VERIFY;
WVTData.hWVTStateData = IntPtr.Zero;
Marshal.StructureToPtr(WVTData, pWVTData, false);
hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);
try
{
if (hRESULT == 0)
{
WVTData = (WINTRUST_DATA)Marshal.PtrToStructure(pWVTData, typeof(WINTRUST_DATA));
IntPtr ptrProvData = WTHelperProvDataFromStateData(WVTData.hWVTStateData);
CRYPT_PROVIDER_DATA provData = (CRYPT_PROVIDER_DATA)Marshal.PtrToStructure(ptrProvData, typeof(CRYPT_PROVIDER_DATA));
for (int idxSigner = 0; idxSigner < provData.csSigners; idxSigner++)
{
IntPtr ptrProvSigner = WTHelperGetProvSignerFromChain(ptrProvData, idxSigner, false, 0);
CRYPT_PROVIDER_SGNR ProvSigner = (CRYPT_PROVIDER_SGNR)Marshal.PtrToStructure(ptrProvSigner, typeof(CRYPT_PROVIDER_SGNR));
CMSG_SIGNER_INFO Signer = (CMSG_SIGNER_INFO)Marshal.PtrToStructure(ProvSigner.psSigner, typeof(CMSG_SIGNER_INFO));
if (Signer.HashAlgorithm.pszObjId != IntPtr.Zero)
{
string objId = Marshal.PtrToStringAnsi(Signer.HashAlgorithm.pszObjId);
if (objId != null)
{
Oid hashOid = Oid.FromOidValue(objId, OidGroup.All);
if (hashOid != null)
{
Console.WriteLine("Hash algorithm of signature {0}: {1}.", dwIndex + 1, hashOid.FriendlyName);
}
}
}
IntPtr ptrCert = WTHelperGetProvCertFromChain(ptrProvSigner, idxSigner);
CRYPT_PROVIDER_CERT cert = (CRYPT_PROVIDER_CERT)Marshal.PtrToStructure(ptrCert, typeof(CRYPT_PROVIDER_CERT));
if (cert.cbStruct > 0)
{
X509Certificate2 certificate = new X509Certificate2(cert.pCert);
Console.WriteLine("Certificate thumbprint of signature {0}: {1}", dwIndex + 1, certificate.Thumbprint);
}
if (ProvSigner.sftVerifyAsOf.dwHighDateTime != provData.sftSystemTime.dwHighDateTime &&
ProvSigner.sftVerifyAsOf.dwLowDateTime != provData.sftSystemTime.dwLowDateTime)
{
DateTime timestamp = DateTime.FromFileTimeUtc(((long)ProvSigner.sftVerifyAsOf.dwHighDateTime << 32) | (uint)ProvSigner.sftVerifyAsOf.dwLowDateTime);
Console.WriteLine("Timestamp of signature {0}: {1}", dwIndex + 1, timestamp);
}
}
}
}
finally
{
WVTData.dwStateAction = WTD_STATEACTION_CLOSE;
Marshal.StructureToPtr(WVTData, pWVTData, false);
hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);
}
Console.WriteLine();
}
}
else if ((uint)hRESULT == 0x800b0100)
{
Console.WriteLine("{0} has no Authenticode signatures.", fileName);
}
}
finally
{
Marshal.FreeHGlobal(pWVTData);
}
}
finally
{
Marshal.FreeHGlobal(pgActionID);
}
}
finally
{
if (pSignatureSettings != IntPtr.Zero)
{
Marshal.FreeHGlobal(pSignatureSettings);
}
}
}
finally
{
Marshal.FreeHGlobal(ptrFile);
}
}
finally
{
Marshal.FreeHGlobal(pcwszFilePath);
}
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
private const int SGNR_TYPE_TIMESTAMP = 0x00000010;
private const int WTD_UI_NONE = 2;
private const int WTD_CHOICE_FILE = 1;
private const int WTD_REVOKE_NONE = 0;
private const int WTD_REVOKE_WHOLECHAIN = 1;
private const int WTD_STATEACTION_IGNORE = 0;
private const int WTD_STATEACTION_VERIFY = 1;
private const int WTD_STATEACTION_CLOSE = 2;
private const int WTD_REVOCATION_CHECK_NONE = 16;
private const int WTD_REVOCATION_CHECK_CHAIN = 64;
private const int WTD_UICONTEXT_EXECUTE = 0;
private const int WSS_VERIFY_SPECIFIC = 0x00000001;
private const int WSS_GET_SECONDARY_SIG_COUNT = 0x00000002;
[DllImport("wintrust.dll")]
private static extern int WinVerifyTrust(IntPtr hWind, IntPtr pgActionID, IntPtr pWVTData);
[DllImport("wintrust.dll")]
private static extern IntPtr WTHelperProvDataFromStateData(IntPtr hStateData);
[DllImport("wintrust.dll")]
private static extern IntPtr WTHelperGetProvSignerFromChain(IntPtr pProvData, int idxSigner, bool fCounterSigner, int idxCounterSigner);
[DllImport("wintrust.dll")]
private static extern IntPtr WTHelperGetProvCertFromChain(IntPtr pSgnr, int idxCert);
[StructLayout(LayoutKind.Sequential)]
private struct WINTRUST_DATA
{
internal int cbStruct;
internal IntPtr pPolicyCallbackData;
internal IntPtr pSIPClientData;
internal int dwUIChoice;
internal int fdwRevocationChecks;
internal int dwUnionChoice;
internal IntPtr pFile;
internal int dwStateAction;
internal IntPtr hWVTStateData;
internal IntPtr pwszURLReference;
internal int dwProvFlags;
internal int dwUIContext;
internal IntPtr pSignatureSettings;
}
[StructLayout(LayoutKind.Sequential)]
private struct WINTRUST_SIGNATURE_SETTINGS
{
internal int cbStruct;
internal int dwIndex;
internal int dwFlags;
internal int cSecondarySigs;
internal int dwVerifiedSigIndex;
internal IntPtr pCryptoPolicy;
}
[StructLayout(LayoutKind.Sequential)]
private struct WINTRUST_FILE_INFO
{
internal int cbStruct;
internal IntPtr pcwszFilePath;
internal IntPtr hFile;
internal IntPtr pgKnownSubject;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_PROVIDER_DATA
{
internal int cbStruct;
internal IntPtr pWintrustData;
internal bool fOpenedFile;
internal IntPtr hWndParent;
internal IntPtr pgActionID;
internal IntPtr hProv;
internal int dwError;
internal int dwRegSecuritySettings;
internal int dwRegPolicySettings;
internal IntPtr psPfns;
internal int cdwTrustStepErrors;
internal IntPtr padwTrustStepErrors;
internal int chStores;
internal IntPtr pahStores;
internal int dwEncoding;
internal IntPtr hMsg;
internal int csSigners;
internal IntPtr pasSigners;
internal int csProvPrivData;
internal IntPtr pasProvPrivData;
internal int dwSubjectChoice;
internal IntPtr pPDSip;
internal IntPtr pszUsageOID;
internal bool fRecallWithState;
internal System.Runtime.InteropServices.ComTypes.FILETIME sftSystemTime;
internal IntPtr pszCTLSignerUsageOID;
internal int dwProvFlags;
internal int dwFinalError;
internal IntPtr pRequestUsage;
internal int dwTrustPubSettings;
internal int dwUIStateFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_PROVIDER_SGNR
{
internal int cbStruct;
internal System.Runtime.InteropServices.ComTypes.FILETIME sftVerifyAsOf;
internal int csCertChain;
internal IntPtr pasCertChain;
internal int dwSignerType;
internal IntPtr psSigner;
internal int dwError;
internal int csCounterSigners;
internal IntPtr pasCounterSigners;
internal IntPtr pChainContext;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_PROVIDER_CERT
{
internal int cbStruct;
internal IntPtr pCert;
internal bool fCommercial;
internal bool fTrustedRoot;
internal bool fSelfSigned;
internal bool fTestCert;
internal int dwRevokedReason;
internal int dwConfidence;
internal int dwError;
internal IntPtr pTrustListContext;
internal bool fTrustListSignerCert;
internal IntPtr pCtlContext;
internal int dwCtlError;
internal bool fIsCyclic;
internal IntPtr pChainElement;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_ALGORITHM_IDENTIFIER
{
internal IntPtr pszObjId;
internal CRYPT_INTEGER_BLOB Parameters;
}
[StructLayout(LayoutKind.Sequential)]
private struct CMSG_SIGNER_INFO
{
internal int dwVersion;
internal CRYPT_INTEGER_BLOB Issuer;
internal CRYPT_INTEGER_BLOB SerialNumber;
internal CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
internal CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
internal CRYPT_INTEGER_BLOB EncryptedHash;
internal CRYPT_ATTRIBUTES AuthAttrs;
internal CRYPT_ATTRIBUTES UnauthAttrs;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_INTEGER_BLOB
{
internal int cbData;
internal IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential)]
private struct CRYPT_ATTRIBUTES
{
internal int cAttr;
internal IntPtr rgAttr;
}
}
}
Running the application against the SQL Server 2014 SP1 installer gives the following output on Windows 8.1:
File: SQLServer2014SP1-KB3058865-x64-ENU.exe
Authenticode signatures: 2
Hash algorithm of signature 1: sha1.
Certificate thumbprint of signature 1: 67B1757863E3EFF760EA9EBB02849AF07D3A8080
Timestamp of signature 1: 22/04/2015 06:03:40
Hash algorithm of signature 2: sha256.
Certificate thumbprint of signature 2: 76DAF3E30F95B244CA4D6107E0243BB97F7DF965
Timestamp of signature 2: 22/04/2015 06:03:51
Press enter to exit.
Give Mono a try. It's able to pull all of the file's Authenticode certificates in one line!
using Mono.Security.Authenticode;
AuthenticodeDeformatter monoFileCert = new AuthenticodeDeformatter("System.Windows.dll");
Console.WriteLine($"Found certificates {monoFileCert.Certificates.Count}");
https://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
Usage example:
https://github.com/mono/mono/blob/master/mcs/tools/security/chktrust.cs

Shared Memory, write to file

I have TXT file in shard memory.
The code is at the end. I've been attempting to get it out of the memory and write to a file in the C:\ drive.
But i get a error:
Type 'SharedMemSaveToFile.SharedMemSaver+Data' cannot be marshaled as an
unmanaged structure; no meaningful size or offset can be computed.
If i change the code to write the Memory the CMD, it works, so i know the memory is there. I've also tried using these to write the TXT:
System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\file.txt");
file.WriteLine(d);
and:
using (StreamWriter outfile = new StreamWriter(d + #"C:\\file.txt"))
{
outfile.Write(sb.ToString());
}
and:
StreamWriter sw = new StreamWriter("file.txt");
sw.Write(d);
sw.Close();
Thanks!
public class Data
{
static void Main(string[] args)
{
SharedMemSaver sf = new SharedMemSaver();
sf.OpenView();
String d = sf.GetData();
System.IO.File.WriteAllText(#"C:\file.txt", d);
}
}
#region Win32 API stuff
public const int FILE_MAP_READ = 0x0004;
[DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr OpenFileMapping(int dwDesiredAccess,
bool bInheritHandle, StringBuilder lpName);
[DllImport("Kernel32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr MapViewOfFile(IntPtr hFileMapping,
int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow,
int dwNumberOfBytesToMap);
[DllImport("Kernel32.dll")]
internal static extern bool UnmapViewOfFile(IntPtr map);
[DllImport("kernel32.dll")]
internal static extern bool CloseHandle(IntPtr hObject);
#endregion
private bool fileOpen = false;
private IntPtr map;
private IntPtr handle;
~SharedMemSaver()
{
CloseView();
}
public bool OpenView()
{
if (!fileOpen)
{
StringBuilder sharedMemFile = new StringBuilder("Mem_Values");
handle = OpenFileMapping(FILE_MAP_READ, false, sharedMemFile);
if (handle == IntPtr.Zero)
{
throw new Exception("Unable to open file mapping.");
}
map = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, Marshal.SizeOf((Type)typeof(Data)));
if (map == IntPtr.Zero)
{
throw new Exception("Unable to read shared memory.");
}
fileOpen = true;
}
return fileOpen;
}
public void CloseView()
{
if (fileOpen)
{
UnmapViewOfFile(map);
CloseHandle(handle);
}
}
public String GetData()
{
if (fileOpen)
{
String data = (String)Marshal.PtrToStringAuto(map);
return data;
}
else
{
return null;
}
}
}
}
I would strongly recommend to use the built-in MemoryMappedFile class (new in .NET 4).
See Yahia's answer for the solution.
But trying to fix your code, the error message says all:
What are you trying to get with Marshal.SizeOf((Type)typeof(Data))?
It has no size, because it holds not data.
Looking at the MSDN doc. of MapViewOfFile's last parameter, "If this parameter is 0 (zero), the mapping extends from the specified offset to the end of the file mapping."

Categories

Resources