Home > Development > Digitally Sign your code

Digitally Sign your code

6 06UTC October 06UTC 2009 Sérgio Charrua Leave a comment Go to comments

In one of our R&D Department’s project (IDI), one feature needed was to let people select an extent and send the output data by mail, including an embedded picture of the map extent, its coordinates and other related info. All this has to be sent using the Outlook 2003/2007 mail client, installed on each of the client’s machines. This was a “must be/must have” requisits! Of course, installing the Outlook client on the server and make use of some webservices to call the Outlook stuffs was not a reliable solution, for several reasons, including server security.

The solution i found to let a webapplication, based on FLEX, to open on the client’s computer the Outlook mail editor was to deploy an ActiveX, preferably coded using the .NET Framework.

After some googling and coaching, one of my fellow colleagues made up his code and released a fully funcitional version of the ActiveX. One problem, though, was to let every client’s browsers to install and run the ActiveX control without the need to change the Security Features of each client’s browser.

The only way is to digitally sign the code! This is done by aquiring a certificate, sign your code with that certificate and deploy it on your webserver!

I found very little and confusing information about this subject on the web, and to help anyone with the same problems, i decided to publish this small how-to (also, this might help me too in the future… ).

After receiving your certificates from a certified CA (Certifacte Authority), install by clicking on each one.

Normally you should have received 3 certificates:
    – a root certificate
    – a private certificate
    – a public certificate

Open your VS2008 IDE, open your solution, and build *EACH PROJECT SEPARATELY*.
    – build your ActiveX Control first
        – go to the Bin folder of your project (might be bin/Debug or bin/Release, depending of your compiling setup)
        – check if the DLL or EXE (or other) exists
        – go to Start Button -> Microsoft Visual Studio 2008 (or any other version) -> Visual Studio Tools
        – open the Visual Sudio Command Prompt (note: NOT the Windows Command Prompt – aka cmd.exe)
        – enter: signtool signwizard
        – when asking for a file to sign, click on Browse, and go to your ActiveX Control Bin directory (might be bin/Debug or bin/Release, depending of your compiling setup)
        – select the DLL or EXE (or other)
        – NEXT
        – select Typical
        – NEXT
        – click on Select From Store
        – select the public certificate you wish to use to sign your code and then click Ok
        – NEXT
        – optionally, enter Description and Web Locator
        – Next and Finish
All these steps are not obligatory, as they only sign your DLL or EXE file. Personally, i like to make sure that all my builds are signed, so that there won’t be any browser security issues upon installing or running.

Create a Setup project (if not existing yet…), set it to View-> File System, and Add a File. Browse to your newly signed DLL or EXE and select it as your primary output.
Also, on your Setup project properties (right-click on the project and select properties), go to Pre-Requisits and select according to your needs.
Close the Properties window. Left click (once only) on the Setup project once again, and on the properties tab, change the ESRI Outlook ActiveX Control
to whatever you wish. Remember that this value will appear on the Control Panel’s "Add & Remove Software" applet, so chose the most userfriendly name possible.
You can now build the Setup project. This will deploy in the /bin/debug/ folder an .EXE and .MSI file.
Add a .INF file in that folder (preferably, name it as your .EXE or .MSI file).
Add these lines in the .INF file :

[version]
signature="$CHICAGO$"
AdvancedINF=2.0
[Setup Hooks]
hook1=hook1 

[hook1]
run=msiexec.exe /i "%EXTRACT_DIR%\Setup.msi" /qn 

Change the Setup.msi name to whatever .MSI filename you’ve set on your Setup project properties, and save.

Now for the Cab file…

Any ActiveX should be installed using a CAB file. Of course, you can use other methods on your own risks, but as this is a Best Practice, the less risk you use, the best results you get!

To deploy .CAB files, you can use the CAB Setup Project from your Visual Studio, but personally, i prefer to use the CAB SDK.

Download the CabArc SDK available at http://support.microsoft.com/kb/310618 .

Still in the Visual Studio Command Prompt, go to the bin/debug folder of your setup project, and type the following line:

cabarc n Setup.cab *

This will zip all the files (the .EXE, .MSI and .INF files) into a unique file, named Setup.cab.

Type once again: signtool signwizard, and do all these step again, but now selecting your .CAB file

- when asking for a file to sign, click on Browse, and go to your ActiveX Control Bin directory (might be bin/Debug or bin/Release, depending of your compiling setup)

        – select the .CAB file

        – NEXT

        – select Typical

        – NEXT

        – click on Select From Store

        – select the public certificate you wish to use to sign your code and then click Ok

        – NEXT

        – optionally, enter Description and Web Locator

        – Next and Finish

Et voilá! You now have a Digitally Signed .CAB file ready for install and a Digitally Signed your code.

You may now deploy it to your website.

You might want to check these websites for some more tips and tricks:

http://www.codeproject.com/KB/cs/C__Deployment.aspx

Other tips:

you may need (almost certainly) to let your ActiveX install as a Safe Plugin/Script or Executable. For that, add the following lines

    [ComImport()]
    [Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IObjectSafety
    {
        [PreserveSig()]
        int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);

        [PreserveSig()]
        int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
    }

 

On your main ActiveX Control class, implement the IObjectSafety interface and add these lines:

        private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
        private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
        private const int S_OK = 0;

#region IObjectSafety Members

        public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
        {
            pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
            pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
            return S_OK;
        }

        public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
        {
            return S_OK;

        }

        #endregion

 

Also, to automatically register/unregister the DLL or ActiveX control add:


        ///    <summary>
        ///    Register the class as a    control    and    set    it's CodeBase entry
        ///    </summary>
        ///    <param name="key">The registry key of the control</param>
        [ComRegisterFunction()]
        public static void RegisterClass(string key)
        {
            // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
            StringBuilder sb = new StringBuilder(key);

            sb.Replace(@"HKEY_CLASSES_ROOT\", "");
            // Open the CLSID\{guid} key for write access
            RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

            // And create    the    'Control' key -    this allows    it to show up in
            // the ActiveX control container
            RegistryKey ctrl = k.CreateSubKey("Control");
            ctrl.Close();

            // Next create the CodeBase entry    - needed if    not    string named and GACced.
            RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
            inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
            inprocServer32.Close();
            // Finally close the main    key
            k.Close();
            MessageBox.Show("Registered");
        }

        ///    <summary>
        ///    Called to unregister the control
        ///    </summary>
        ///    <param name="key">Tke registry key</param>
        [ComUnregisterFunction()]
        public static void UnregisterClass(string key)
        {
            StringBuilder sb = new StringBuilder(key);
            sb.Replace(@"HKEY_CLASSES_ROOT\", "");

            // Open    HKCR\CLSID\{guid} for write    access
            RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

            // Delete the 'Control'    key, but don't throw an    exception if it    does not exist
            k.DeleteSubKey("Control", false);

            // Next    open up    InprocServer32
            //RegistryKey    inprocServer32 =
            k.OpenSubKey("InprocServer32", true);

            // And delete the CodeBase key,    again not throwing if missing
            k.DeleteSubKey("CodeBase", false);

            // Finally close the main key
            k.Close();
            MessageBox.Show("UnRegistered");
        } 

 

That should do the trick!

Categories: Development Tags: ,
  1. No comments yet.
  1. No trackbacks yet.