I signed a file using Signtool.exe and now I am trying to load the certificate attached to the file using the following method
var cert = X509Certificate2.CreateFromSignedFile(filePath);
but his line throws an error "Cannot find the requested object.". When I try reading the certificate from a microsoft signed dll e.g. EntityFramework.dll, it works without any problems. I thought it could be because I don't have the certificate in the Trusted Store but even after adding it there, it continues to throw error. Does anyone know how to fix this?
You can use wintrust component to collect the signature information
[DllImportAttribute("wintrust.dll", EntryPoint = "WTGetSignatureInfo", CallingConvention = CallingConvention.StdCall)]
internal static extern int WTGetSignatureInfo([InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] string pszFile, [InAttribute()] System.IntPtr hFile, SIGNATURE_INFO_FLAGS sigInfoFlags, ref SIGNATURE_INFO psiginfo, ref System.IntPtr ppCertContext, ref System.IntPtr phWVTStateData);
This will collect the signature details from any signable files that microsoft prescribes. But make sure that you execute the given function under single threaded apartment model. Otherwise you will find weird results for signed script files like .js/.vbs and etc.
Please refer How to validate authenticode for Javascript in C# for more details.
Related
There is a GlobalSign CA. In most cases its root certificate is already exists in the Windows Certificates storage.
But sometimes (especially on old Windows versions) the storage doesn't contain the certificate.
I need to check if the certificate exists and import it if does not. I exported the certificate to a file and imported it using the code below:
public void ImportCertificate(StoreName storeName,
StoreLocation location,
byte[] certificateData)
{
X509Store x509Store = new X509Store(storeName, location);
X509Certificate2 certificate = new X509Certificate2(certificateData);
x509Store.Open(OpenFlags.ReadWrite);
x509Store.Add(certificate);
x509Store.Close();
}
The code adds the certificate but all certificate purposes are checked:
I don't want to add extra purposes to the certificate just want to set those ones which have other root CAs like below:
How to do it programatically?
You need to use CertSetCertificateContextProperty function to set store-attached properties.
In the dwPropId parameter you pass CERT_ENHKEY_USAGE_PROP_ID. You can find its numeric value in Wincrypt.h C++ header file. In a given case, dwPropId is 9:
#define CERT_ENHKEY_USAGE_PROP_ID 9
In the dwFlags you pass zero (0).
In the pvData parameter (which is IntPtr in managed signature) you pass an unmanaged pointer to an ASN.1-encoded byte array that represents a collection of object identifiers where each OID represents explicitly enabled key usage.
Here is the interop signature:
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern Boolean CertSetCertificateContextProperty(
[In] IntPtr pCertContext,
[In] UInt32 dwPropId,
[In] UInt32 dwFlags,
[In] IntPtr pvData,
);
Add usings reference to System.Runtime.InteropServices namespace.
Next, prepare a collection of key usages:
create a new instance of OidCollection class.
add required OIDs to the collection.
Use created OID collection to instantiate the X509EnhancedKeyUsageExtension class. This ctor overload is fine: X509EnhancedKeyUsageExtension(OidCollection, Boolean).
RawData property of EKU extension will contain ASN.1-encoded byte array which will be passed to CertSetCertificateContextProperty function.
Last parameter of CertSetCertificateContextProperty function is of IntPtr type and expects a pointer to an unmanaged memory block, so:
Use Marshal.AllocHGlobal(eku.RawData.Length) to allocate the properly sized buffer in unmanaged memory.
Use Marshal.Copy(byte[], IntPtr, int, int) static method overload to copy eku.RawData byte array to unmanaged pointer acquired in step 1.
call the CertSetCertificateContextProperty function. If it returns true then everything is ok.
After finishing all job, you must release unmanaged resources to avoid memory leak. Use Marshal.FreeHGlobal(IntPtr) method to release the pointer acquired during Marshal.AllocHGlobal call.
I lack the reputation to comment on other answers but Crypt32's answer is actually incorrect in two places so I'm posting a new answer that is based on Crypt32's answers.
pvData of CertSetCertificateContextProperty cannot accept X509EnhancedKeyUsageExtension.RawData directly, it needs to be in a CRYPT_INTEGER_BLOB struct
The function signature of Marshal.Copy is incorrect, it should be Marshal.Copy(Byte[], Int32, IntPtr, Int32)
With this in mind, let's start from the top:
Make sure you obtain the X509Certificate2 you require (Certificate), probably from a X509Store that has been opened as ReadWrite.
Import the Interop Signature and use the System.Runtime.InteropServices namespace.
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern Boolean CertSetCertificateContextProperty(
[In] IntPtr pCertContext,
[In] UInt32 dwPropId,
[In] UInt32 dwFlags,
[In] IntPtr pvData,
);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPTOAPI_BLOB {
public uint cbData;
public IntPtr pbData;
}
Create an OidCollection instance
Add the require OIDs of the EKUs you desire to the OidCollection
Create an X509EnhancedKeyUsageExtension instance (eku) using the OidCollection
Use Marshal.AllocHGlobal(eku.RawData.Length) to allocate the properly sized buffer in unmanaged memory as pbData
Use Marshal.AllocHGlobal with the size of the CRYPT_INTEGER_BLOB struct to allocate the properly sized buffer in unmanaged memory as pvData
Create an instance of the CRYPT_INTEGER_BLOB struct and allocate pbData to it and the length, eku.RawData.Length, to cbData
Use Marshal.StructureToPtr to assign the CRYPT_INTEGER_BLOB struct to pvData
Call CertSetCertificateContextProperty with pCertContext as Certificate.Handle, dwPropId being CERT_ENHKEY_USAGE_PROP_ID which is 9, dwFlags of 0 and pvData as pvData. It will return return true if successful, it may throw an error regarding a memory access exception if the Certificate Handle you passed into it is read-only, make sure it's from an X509Store opened as ReadWrite.
Free allocated unmanaged memory via Marshal.FreeHGlobal(pvData) and Marshal.FreeHGlobal(pbData)
Close any Opened X509Store
Again, thanks to Crypt32 for this answer.
I am using Tamas Szekeres builds of GDAL including the C# bindings in a desktop GIS application using C# and .net 4.0
I am including the entire GDAL distribution in a sub-directory of my executable with the following folder structure:
\Plugins\GDAL
\Plugins\GDAL\gdal
\Plugins\GDAL\gdal-data
\Plugins\GDAL\proj
We are using EPSG:4326, and the software is built using 32-bit target since the GDAL C# API is using p/invoke to the 32-bit libraries (could try 64 bit since Tamas provides these, haven't gotten around to it yet).
When I run my application I get the following error
This error typically happens when software tries to access a device that is no longer attached, such as a removable drive. It is not possible to "catch" this exception because it pops up a system dialog.
After dismissing the dialog using any of the buttons, the software continues to execute as designed.
The error occurs the first time I call the following method
OSGeo.OSR.CoordinateTransformation.TransformPoint(double[] inout);
The strange stuff:
The error occurs on one, and only one computer (so far)
I've run this software in several other computers both 32 and 64 bit without problems
The error does not ocurr on the first run after compiling the GDAL shim library I am using, it only occurrs on each subsequent run
it happens regardless of release, or debug builds
it happens regardless of whether the debugger is attached or not
it happens regardless of whether I turn on or off Gdal.UseExceptions or Osr.UseExceptions();
disabling removable drives causes the bug to disappear. This is not what I consider a real solution as I will not be able to ask a customer to do this.
I have tried the following:
catching the error
changing GDAL directories and environment settings
changing computers and operating systems: this worked
used SysInternals ProcMon to trace what files are being opened with no luck, they all appear to be files that exist
I re-built the computer in question when the hard drive failed, to no avail.
"cleaning" the registry using CCleaner
files in GDAL Directory are unchanged on execution
Assumptions
Error is happening in unmanaged code
During GDAL initialization, some path is referring to a drive on the computer that is no longer attached.
I am also working on the assumption this is limited to a computer configuration error
Configuration
Windows 7 Pro
Intel Core i7 920 # 2,67GHz
12.0 GB RAM
64-bit OS
Drive C: 120 GB SSD with OS, development (Visual Studio 10), etc
Drive D: 1 TB WD 10,000k with data, not being accessed for data.
The Question
I either need a direction to trap the error, or a tool or technique that will allow me to figure out what is causing it. I don't want to release the software with the possibility that some systems will have this behaviour.
I have no experience with this library, but perhaps some fresh eyes might give you a brainwave...
Firstly, WELL WRITTEN QUESTION! Obviously this problem really has you stumped...
Your note about the error not occurring after a rebuild screams out: Does this library generate some kind of state file, in its binary directory, after it runs?
If so, it is possible that it is saving incorrect path information into that 'configuration' file, in a misguided attempt to accelerate its next start-up.
Perhaps scan this directory for changes between a 'fresh build' and 'first run'?
At very least you might find a file you can clean up on shut-down to avoid this alert...
HTH
Maybe you can try this:
Run diskmgmt.msc
Change the driveletter for Disk 2 (right click) if my assumption that Disk 2 is a Removable Disk is true
Run your application
If this removes the error, something in the application is referring to the old driveletter
It could be in the p/invoked libs
Maybe see: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 It talks about gcc somehow compiling a driveletter into a binary
+1 Great question, but It is not possible to "catch"
Its one of these awful solutions that will turn up on DailyWTF in 5 years. But for now it is stored here http://www.pinvoke.net/default.aspx/user32.senddlgitemmessage
using Microsoft.VisualBasic; //this reference is for the Constants.vbNo;
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SendDlgItemMessage(IntPtr hDlg, int nIDDlgItem, uint Msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
// For Windows Mobile, replace user32.dll with coredll.dll
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem,[Out] StringBuilder lpString, int nMaxCount);
public void ClickSaveBoxNoButton()
{
//In this example, we've opened a Notepad instance, entered some text, and clicked the 'X' to close Notepad.
//Of course we received the 'Do you want to save...' message, and we left it sitting there. Now on to the code...
//
//Note: this example also uses API calls to FindWindow, GetDlgItemText, and SetActiveWindow.
// You'll have to find those separately.
//Find the dialog box (no need to find a "parent" first)
//classname is #32770 (dialog box), dialog box title is Notepad
IntPtr theDialogBoxHandle; // = null;
string theDialogBoxClassName = "#32770";
string theDialogBoxTitle = "Notepad";
int theDialogItemId = Convert.ToInt32("0xFFFF", 16);
StringBuilder theDialogTextHolder = new StringBuilder(1000);
//hardcoding capacity - represents maximum text length
string theDialogText = string.Empty;
string textToLookFor = "Do you want to save changes to Untitled?";
bool isChangeMessage = false;
IntPtr theNoButtonHandle; // = null;
int theNoButtonItemId = (int)Constants.vbNo;
//actual Item ID = 7
uint theClickMessage = Convert.ToUInt32("0x00F5", 16);
//= BM_CLICK value
uint wParam = 0;
uint lParam = 0;
//Get a dialog box described by the specified info
theDialogBoxHandle = FindWindow(theDialogBoxClassName, theDialogBoxTitle);
//a matching dialog box was found, so continue
if (theDialogBoxHandle != IntPtr.Zero)
{
//then get the text
GetDlgItemText(theDialogBoxHandle, theDialogItemId, theDialogTextHolder, theDialogTextHolder.Capacity);
theDialogText = theDialogTextHolder.ToString();
}
//Make sure it's the right dialog box, based on the text we got.
isChangeMessage = Regex.IsMatch(theDialogText, textToLookFor);
if ((isChangeMessage))
{
//Set the dialog box as the active window
SetActiveWindow(theDialogBoxHandle);
//And, click the No button
SendDlgItemMessage(theDialogBoxHandle, theNoButtonItemId, theClickMessage, (System.UIntPtr)wParam, (System.IntPtr)lParam);
}
}
It turns out there was no way to definitely answer this question.
I ended up "solving" the problem by figuring out that there was some hardware registered on the system that wasn't present. It is still a mystery to me why, after several years, only GDAL managed to provoke this bug.
I will put the inability to catch this exception down to the idiosyncrasies involved with p/invoke and the hardware error thrown at a very low level on the system.
You could add custom error handlers to gdal. This may help:
Link
http://trac.osgeo.org/gdal/ticket/2895
I'm using FindMimeFromData from urlmon.dll for sniffing uploaded files' MIME type. According to MIME Type Detection in Internet Explorer, image/tiff is one of the recognized MIME types. It works fine on my development machine (Windows 7 64bit, IE9), but doesn't work on the test env (Windows Server 2003 R2 64bit, IE8) - it returns application/octet-stream instead of image/tiff.
The above article describes the exact steps taken to determine the MIME type, but since image/tiff is one of the 26 recognized types, it should end on step 2 (sniffing the actual data), so that file extensions and registered applications (and other registry stuff) shouldn't matter.
Oh and by the way, TIFF files actually are associated with a program (Windows Picture and Fax Viewer) on the test server. It's not that any reference to TIFF is absent in Windows registry.
Any ideas why it doesn't work as expected?
EDIT: FindMimeFromData is used like this:
public class MimeUtil
{
[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
private static extern int FindMimeFromData(
IntPtr pBC,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] byte[] pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
out IntPtr ppwzMimeOut,
int dwReserved);
public static string GetMimeFromData(byte[] data)
{
IntPtr mimetype = IntPtr.Zero;
try
{
const int flags = 0x20; // FMFD_RETURNUPDATEDIMGMIMES
int res = FindMimeFromData(IntPtr.Zero, null, data, data.Length, null, flags, out mimetype, 0);
switch (res)
{
case 0:
string mime = Marshal.PtrToStringUni(mimetype);
return mime;
// snip - error handling
// ...
default:
throw new Exception("Unexpected HRESULT " + res + " returned by FindMimeFromData (in urlmon.dll)");
}
}
finally
{
if (mimetype != IntPtr.Zero)
Marshal.FreeCoTaskMem(mimetype);
}
}
}
which is then called like this:
protected void uploader_FileUploaded(object sender, FileUploadedEventArgs e)
{
int bsize = Math.Min(e.File.ContentLength, 256);
byte[] buffer = new byte[bsize];
int nbytes = e.File.InputStream.Read(buffer, 0, bsize);
if (nbytes > 0)
string mime = MimeUtil.GetMimeFromData(buffer);
// ...
}
I was unable to reproduce your problem, however I did some research on the subject. I believe that it is as you suspect, the problem is with step 2 of MIME Type Detection: the hard-coded tests in urlmon.dll v9 differ from those in urlmon.dll v8.
The Wikipedia article on TIFF shows how complex the format is and that is has been a problem from the very beginning:
When TIFF was introduced, its extensibility provoked compatibility problems. The flexibility in encoding gave rise to the joke that TIFF stands for Thousands of Incompatible File Formats.
The TIFF Compression Tag section clearly shows many rare compression schemes that, as I suspect, have been omitted while creating the urlmon.dll hard-coded tests in earlier versions of IE.
So, what can be done to solve this problem? I can think of three solutions, however each of them brings different kind of new problems along:
Update the IE on your dev machine to version 9.
Apply the latest IE 8 updates on your dev machine. It is well known that modified versions of urlmon.dll are introduced frequently (eg. KB974455). One of them may contain the updated MIME hard-coded tests.
Distribute own copy of urlmon.dll with your application.
It seems that solutions 1 and 2 are the ones you should choose from. There may be a problem, however, with the production environment. As my experience shows the administrators of production env often disagree to install some updates for many reasons. It may be harder to convince an admin to update the IE to v9 and easier to install an IE8 KB update (as they are supposed to, but we all know how it is). If you're in control of the production env, I think you should go with solution 1.
The 3rd solution introduces two problems:
legal: It may be against the Microsoft's policies to distribute own copy of urlmon.dll
coding: you have to load the dll dynamically to call the FindMimeFromData function or at least customize your app's manifest file because of the Dynamic-Link Library Search Order. I assume you are aware, that it is a very bad idea just to manually copy a newer version of urlmon.dll to the system folder as other apps would most likely crash using it.
Anyway, good luck with solving your urlmon riddle.
For some odd reason, when I marshal the LogonUser DLLImport parameters I am no longer able to login succesfully when using the INTERACTIVE logon type, it works for NETWORK logon type.
This is my code:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool LogonUser
(
[MarshalAs(UnmanagedType.LPStr)]
String lpszUsername,
[MarshalAs(UnmanagedType.LPStr)]
String lpszDomain,
[MarshalAs(UnmanagedType.LPStr)]
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr hToken
);
bResult = LogonUser(
"username",
".",
"password",
(int)LogonType.INTERACTIVE, // = 2
(int)LogonProvider.DEFAULT, // = 0
out hToken
);
Now, as-is my call to LogonUser fails (Logon Exception: Logon failure: unknown user name or bad password), but if I remove the [MarshalAs(UnmanagedType.LPStr)]s from the DLLImport it works fine, also if I switch to LogonType.NETWORK it works fine, why is it different with INTERACTIVE?
Sadly I need to keep it as I use this with other functions such as LoadUserProfile that needs it to be Marshalled (only way I could get it to work and not display unknown windows characters [squares]). Do I need to do some funky marshaling of strings or something to get it to validate correctly?
Any help would be appreciated.
Thanks,
LogonUser takes an LPTSTR, not an LPSTR, as parameters. You should just use the default string marshaling, and it will work correctly.
See LogonUser and pinvoke.net's declaration for a property P/Invoke of LogonUser.
I had the same issue, but the cause was different. Then I realized that in the place I work we must log-on our machines with a digital certificate instead of user and password.
I had forgotten that we have this restriction on our domain.
So I have to use another domain account, instead of mine, to test my application.
I don't know if these information will help but, but they maybe be of some use for other people.
Can you elaborate what goes behind the scene when we create DirectoryEntry instance?
Code snippet:
var dirEntry = new DirectoryEntry("LDAP://CN=jsmith,DC=fabrikam,DC=Com", userName, password);
I mean, how authentication works? Who talks with whom? Assume the code above is in a console application.
Creating the DirectoryEntry doesn't do much more than actually creating the object in memory. The DirectoryEntry object is actually just a managed wrapper around the basic IADsObject of the unmanaged, COM-based ADSI (Active Directory Service Interfaces) interface to Active Directory (which you could - if you really wanted to - use directly, too).
Only when you start using its properties, or when you access the underlying .NativeObject COM object, will it actually connect to Active Directory, log on with your current credentials (or any alternate credentials you supplied), and try and fetch the information for that DirectoryEntry from AD.
Marc
From what I can see using Reflector, it uses the activds.dll
For example:
[DllImport("activeds.dll", EntryPoint="ADsOpenObject", CharSet=CharSet.Unicode, ExactSpelling=true)]
private static extern int IntADsOpenObject(string path, string userName, string password, int flags, [In, Out] ref Guid iid, [MarshalAs(UnmanagedType.Interface)] out object ppObject);
http://msdn.microsoft.com/en-us/library/aa772238(VS.85).aspx