Tuesday, May 12, 2009

IE Hosted Controls - More manifests!

Arrg - back into the world of barely documented manifests.

This time, I need to create a managed control in IE with full trust. This allows you to simply create an activeX "like" control in C#, as detailed here:

http://stackoverflow.com/questions/299323/creating-and-deploying-an-activex-control-in-net


Getting full trust for the site isn't terribly hard, if you read the following posts from Shawn @ Microsoft - basically, you have to create application and deployment manifests, host them on the site using the control, and reference them in your HTML:

http://blogs.msdn.com/shawnfa/archive/2008/01/24/manifested-controls-redux.aspx
http://blogs.msdn.com/shawnfa/archive/2007/03/09/manifests-for-ie-hosted-controls.aspx

Here's my recipe for creating app and deployment manifests for an IE hosted control using Mage:

** update the application manifest with the assemblies hash **
1. mage -u myControl.dll.manifest

2. Open app manifest in notepad, and remove "extra" <dependency> tags such as osVersionInfo, and prerequisite

** sign the app manifest **
3. mage -Sign myControl.dll.manifest -CertHash 41b338fef3747f53e23929b428385ce9ccf9b891


** build the Base64 encoded SHA1 hash of the app manifest (source for hash.exe is below) **
4. hash -file myControl.dll.manifest

** Put hash into deployment manifest **
5. Open myControl.control in notepad, alter <dsig:DigestValue> with value computed in 4.

** update the deployment manifest **
6. mage -u myControl.control

** sign deployment manifest **
7. mage -Sign myControl.control -CertHash 41b338fef3747f53e23929b428385ce9ccf9b891

There are a couple of gotchas in the instructions, mainly because Mage was built for ClickOnce deployments, and not necessarily for hosted controls. In many cases if you build a "new" manifest using mage (rather than use the examples from Shawn's blog), you'll get the right information, but if you update manifests with the command line, several things will be missing in the final manifests -


1) When updating, and signing, the application manifest as detailed above, the Mage tool doesn't update the publisherIdentity associated with the signing certificate, or the assembly version (but it does update the assembly's hash). You'll need to manually update the version, update the RDN in the publisher identity (to match your signing certificate's RDN and issuerKeyHash).

2) When updating, and signing the deployment manifest (e.g. foobar.control), the Mage tool doesn't update the publisherIdentity, nor does it update the hash of the application manifest (addressed in step 4 and 5 above).


** Fixing the publisher Identity **

The publisherIdentity looks like this in the application and deployment manifests - its composed of the Distinguished Name of the certificate, and the hash of the public key of the issuer of the signing certificate :

<publisherIdentity name="CN=multipart, OU=RDN" issuerKeyHash="1d394e8e3ab5e9f1fb9921b3431543e288810066" />

If it is not correct, then your IEDebugLog will show the certificate is untrusted, and you will not be able to elevate:

Microsoft.IE.ApplicationManifest: Signing certificate trust level: Untrusted

Microsoft.IE.DeploymentManifest: Attempt to elevate CAS permissions by an untrusted control was blocked.


Getting the DN is easy, just open certmgr.msc (a MMC snapin), and copy the subject for the signing certificate into the publisherIdentity (adding in comma's). Getting the issuerKeyHash is a bit harder, and requires code, provided below. You basically take the certificate for the CA which issued the signing certificate, and build a SHA1 hash of the public key (encoded in a HEX string).

** HASH.EXE **

The partial code below gets the issuerKeyHash from a certificate file, and is also used for getting the SHA1 hash of the application manifest.


static private void fileHash(String filename)
{
byte[] hash;
SHA1Managed sha = new SHA1Managed();
FileStream strm = null;

try
{
strm = new FileStream(filename, FileMode.Open, FileAccess.Read);
hash = sha.ComputeHash(strm);
}
finally
{
if (strm != null)
strm.Close();
}
Console.Write(Convert.ToBase64String(hash));
}

protected static string[] HexStringValueArray = {
"00", "01", ...., "FD", "FE", "FF" };

public static string DataToHex(byte[] data)
{
char[] list = new char[data.Length * 2];
for (int k = 0; k < data.Length; ++k)
{
list[k * 2 + 0] = HexStringValueArray[data[k]][0];
list[k * 2 + 1] = HexStringValueArray[data[k]][1];
}
return new string(list);
}

static void certPublicKeyHash(String certificateFileName)
{
byte[] hash;
SHA1Managed sha = new SHA1Managed();
X509Certificate2 cert = new X509Certificate2(certificateFileName);
hash = sha.ComputeHash(cert.PublicKey.EncodedKeyValue.RawData);
Console.Write(DataToHex(hash));
}

Friday, March 20, 2009

WIX EventManifest

Not a blogger. Never will be, because I'm too friggin busy. But I do hate spending hours finding solutions to (undocumented) software problems. As an independent consultant who works on nearly *everything* (linux, unix, windows, mac, c#, c/c++, asp.net, low level crypto, assembler, apache, clustering, computer security, mobile, ...), I spend a lot of time tracking down obscure solutions.

My current project involves using wix (http://wix.sourceforge.net/), an XML --> MSI toolset. It rocks, but the documentation nearly always lags behind the capabilities. One of the items I needed to address was adding a custom event channel to a Vista / W2008 system.

Easy enough - the new event manifest technology makes this "easy" - (see http://msdn.microsoft.com/en-us/library/aa385619(VS.85).aspx and http://msdn.microsoft.com/en-us/magazine/cc163431.aspx), at least during product development.

Getting the event registered and the channel created on the finished product is also "easy" if you use the EventManifest capabilities in Wix 3.0. Here's an example:

Step 1 is to generate and install the resource dll containing the event text - see the msdn links above on how to do this simple op. Include a component with the .dll in your WIX file:

<component id="cEventRes" guid="C27399CA-17DD-4bcb-8414-25D75DA78E63">
<file id="'eventsRES'" name="'events.dll'" diskid="'1'" source="'Z:\myfiles\events.dll'" vital="'yes'">

</component>

Channels, and event messages are defined / registered by calling wevutil on your event manifest. The manifest (a simple XML file) needs to be installed, and registered on the system. This can be tricky using a custom action, but using WIX and the EventManifest tag, its easy. The sort of undocumented part is putting the EventManifest type *inside* of the tag corresponding to the manifest file (that wasn't totally obvious, which is why I blogged this mofo):


<component id="cEvent XML" guid="8A2BF265-1A6E-4667-8962-3662561957B8">
<file id="'eventsXML'" name="'events.xml'" diskid="'1'" source="'Z:\myfiles\eventman.xml'" vital="'yes'">
<util:eventmanifest messagefile="'[#eventsRES]'" resourcefile="'[#eventsRES]'"></util:EventManifest>
</file>
</component>

One important note - you need to include a reference to the WixUtilExtension in your light command line / VS studio project. It also helps w/ intellisense to define the namespace in your .wxs file:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension%22&gt;


That's about all I know about EventManifest and WIX - good luck!