Monday, November 13, 2017

Signing ClickOnce manifests and Digitally signing assemblies which are part of the package




Some Definitions

Digital signature: A digital signature is used to help authenticate the identity of the creator of digital information — such as documents, e-mail messages, and macros — by using cryptographic algorithms. 

What does it accomplish?
Digital signatures help establish the following authentication measures:

  1. Authenticity: The digital signature helps ensure that the signer is whom he or she claims to be. This helps prevent others from pretending to be the originator of a particular document (the equivalent of forgery on a printed document).
  2. Integrity: The digital signature helps ensure that the content has not been changed or tampered with since it was digitally signed. This helps prevent documents from being intercepted and changed without knowledge of the originator of the document.
  3. Non-repudiation: The digital signature helps prove to all parties the origin of the signed content. "Repudiation" refers to the act of a signer's denying any association with the signed content. This helps prove that the originator of the document is the true originator and not someone else, regardless of the claims of the signer. A signer cannot repudiate the signature on that document without repudiating his or her digital key, and therefore other documents signed with that key.


ClickOnce: ClickOnce is a Microsoft technology that enables the user to install and run a Windows-based smart client application by clicking a link in a web page. ClickOnce is a component of Microsoft .NET Framework 2.0 and later, and supports deploying applications made with Windows Forms or Windows Presentation Foundation. It is similar to Java Web Start for the Java Platform or Zero Install for Linux.


Scenario
We have a security policy enforced where dlls can only be downloaded from a url if they are originating from a trusted publisher, i.e. they are digitally signed.

What is the issue?
There were two issues that we came across:
1.    Dlls are not digitally signed as part of the ClickOnce process. 
2.    Even if you sign dlls as part of the process, ClickOnce manifest that store "hash value" of the dlls for validation, is no more valid as that "hash value" for each dll changes.
3.    ClickOnce changes the extension of each file in the package to ".deploy", e.g. if there is a file called SomeFile.dll, it changes to SameFile.dll.deploy

Steps to resolve the issue?
To resolve this issue, we have to follow the following steps:
1.    Call "Publish" target on the client application project as part of the build pipeline.
2.    In "AfterPublish" task (when the manifest and files are genterted for clickonce package) we           follow rest of the steps.
3.  Rename "*.dll.deploy" to "*.dll" (similarly files with other extensions) 
4.  Sign the dlls/exes using SignFile task
5.  Update "hash values" for dlls which is stored in application manifest file (having extension ".manifest") using mage.exe tool. (You can have this mage tool the part of your source code)
6.  Update deployment manifest (having extension ".application") which stores sign value of the application manifest.
7.  Rename "*.dll" back to "*.dl.deploy" (files renamed in step 3) 

Code and Steps for implementing the solution
Here is the MSbuild code that goes in the AfterPublish target (Please update the values as directed in the code):

<PropertyGroup>   
    <!-- $(CertificateThumbprint) is the thumbrint of the certificate to sign the dlls -->
    <CertificateThumbprint>xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx</CertificateThumbprint>
   
    <!-- $(CertificateHash) is the thumbrint of the certificate to sign the manifest files -->
    <CertificateHash>"xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx"</CertificateHash>
   
    <!-- $(PublisherName) put publisher name for this property -->
    <PublisherName>Your Publisher Name</PublisherName>
   
    <!-- $(MageCurrentPath) is the absolute path to mage.exe -->
<MageCurrentPath>"PathToUpdate\mage.exe"</MageCurrentPath>
   
    <!-- $(PublishDir) is the publish folder while buliding the application - please update this -->
    <PublishDir>Publish Directory for the package</PublishDir>
   
<TimestampUrl>http://timestamp.verisign.com/scripts/timstamp.dll</TimestampUrl>
</PropertyGroup>

<Target Name="SignClickOnceAssemblies" AfterTargets="AfterPublish">
    <ItemGroup> 
       <ApplicationManifest Include="$(PublishDir)**\*.manifest;"/>
    </ItemGroup>
    <ItemGroup> 
       <DeplymentManifest Include="$(PublishDir)**\*.application;"/>
    </ItemGroup>

    <PropertyGroup>
       <AppManifestFullPath>%(ApplicationManifest.FullPath)</AppManifestFullPath>
       <AppDir>%(ApplicationManifest.RootDir)%(ApplicationManifest.Directory)</AppDir>    
       <DeploymentManifestFullPath>%(DeplymentManifest.FullPath)</DeploymentManifestFullPath>
       <DeplyDir>%(DeplymentManifest.RootDir)%(DeplymentManifest.Directory)</DeplyDir>
    </PropertyGroup>

    <Message Text="AppManifestFullPath : $(AppManifestFullPath)" />
    <Message Text="AppDir : $(AppDir)" />
    <Message Text="MageCurrentPath : $(MageCurrentPath)" />
    <Message Text="DeploymentManifestFullPath : $(DeploymentManifestFullPath)" />

    <!-- Application Manifest -->
    <ItemGroup> 
       <FilesToCopy Include="$(AppDir)**\*.*;" Exclude="$(AppManifestFullPath)"/> 
    </ItemGroup>

    <Copy SourceFiles="@(FilesToCopy)"
           DestinationFiles="%(FilesToCopy.RootDir)%(FilesToCopy.Directory)%(FilesToCopy.Filename)"/>
    <Delete Files="@(FilesToCopy)" />

    <ItemGroup> 
       <FilesToSign Include="$(PublishDir)**\*.dll;$(PublishDir)\$(ProjectName).exe;"/>
    </ItemGroup>

    <Message Text="FilesToSign : @(FilesToSign)" />

    <SignFile 
       CertificateThumbprint="$(CertificateThumbprint)" 
           SigningTarget="%(FilesToSign.FullPath)"
           TargetFrameworkVersion="v4.5"
           TimestampUrl="$(TimestampUrl)" />

    <Exec Command='$(MageCurrentPath) -Update "$(AppManifestFullPath)" -CertHash $(CertificateHash) -Algorithm sha256RSA -TimestampUri $(TimestampUrl)' />

    <ItemGroup> 
       <FilesToCopyDeploy Include="$(AppDir)**\*.*;" Exclude="$(AppManifestFullPath)"/> 
    </ItemGroup>

    <Copy SourceFiles="@(FilesToCopyDeploy)"
           DestinationFiles="%(FilesToCopyDeploy.RootDir)%(FilesToCopyDeploy.Directory)%(FilesToCopyDeploy.Filename)%(FilesToCopyDeploy.Extension).deploy"/>
    <Delete Files="@(FilesToCopyDeploy)" />

    <!-- Deployment Manifest -->

    <Exec Command='$(MageCurrentPath) -Update "$(DeploymentManifestFullPath)" -AppManifest "$(AppManifestFullPath)" -Publisher "$(PublisherName)" -CertHash $(CertificateHash) -Algorithm sha256RSA -TimestampUri $(TimestampUrl)' />


</Target>


Saturday, August 6, 2016

Json Serializer | Serializing Interface and Generic List

Json Serialization though can be very easy but there are situations when it is not very straight forward, specially when it is one of the following cases:
1. An interface object
2. List of objects
3. IList of objects
4. List of interface
5. IList of interface

So, you can utilize the generic code below to solve the problem:

You need to install Newtonsoft.JSON to use this code:


using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

namespace Utilities.Core
{
    public static class JsonSerializer<T> where T : class
    {
        public static string ToJson(T objectToSerialize)
        {
            if (null == objectToSerialize)
                return null;
            return JsonConvert.SerializeObject(objectToSerialize);
        }

        public static T ToObject(string json)
        {
            if (string.IsNullOrWhiteSpace(json))
                return null;
            return JsonConvert.DeserializeObject<T>(json);
        }

        public static List<T> ToObjectList(string json)
        {
            if (string.IsNullOrWhiteSpace(json))
                return null;
            return JsonConvert.DeserializeObject<List<T>>(json);
        }
    }

    public static class JsonSerializer<TImplementation, TInterface> where TImplementation : class, TInterface
    {
        public static string ToJson(TInterface objectToSerialize)
        {
            if (null == objectToSerialize)
                return null;
            return JsonConvert.SerializeObject(objectToSerialize);
        }

        public static TInterface ToObject(string json)
        {
            return JsonSerializer<TImplementation>.ToObject(json);
        }

        public static IList<TInterface> ToObjectList(string json)
        {
            if (string.IsNullOrWhiteSpace(json))
                return null;
            var results = JsonConvert.DeserializeObject<List<TImplementation>>(json);
            IList<TInterface> objectList = results.Select(item => (TInterface) item).ToList();
            return objectList;
        }
    }
}

Thursday, January 28, 2016

Enum extensions : Some useful methods while dealing with enums

Enums are used most widely when one wants to use discrete values for a set.
Example is Currency, say I have an enum:
 
namespace EnumUtility
{
    public enum Currency
    {
        [Description("US Dollars")]
        USD,
        [Description("Indian Rupee")]
        INR
    }
}


We can have some enum extension methods for the following items:
1. Getting description for enum
2. Getting an enum if we have the corresponding description
3. Calling "Equals()" on enum to compare it to description string
4. Calling "Equals()" on description string to compare it to enum


 
using System;
using System.ComponentModel;
using System.Linq;

namespace EnumUtility
{
    public static class EnumExtensions
    {
        /// <summary>
        /// Gets the enum desciption
        /// </summary>
        /// <param name="value">Enum value</param>
        /// <returns>Description of the enum</returns>
        public static string ToDescription(this Enum value)
        {
            var fi = value.GetType().GetField(value.ToString());
            var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof (DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : value.ToString();
        }

        /// <summary>
        /// Can be called on string to convert it to Enum
        /// </summary>
        /// <typeparam name="T">Enum type</typeparam>
        /// <param name="description">Description attribute value</param>
        /// <returns>Returns the corresponding enum</returns>
        public static T ToEnum<T>(this string description)
        {
            var fields = typeof (T).GetFields();
            foreach (var field in from field
                in fields
                let attributes = (DescriptionAttribute[]) field.GetCustomAttributes
                    (typeof (DescriptionAttribute), false)
                where
                    attributes != null && attributes.Length > 0 &&
                    attributes[0].Description.ToLower().Equals(description.ToLower())
                select field)
            {
                return (T) Enum.Parse(typeof (T), field.Name);
            }

            throw new Exception("Description not found for the enum specified");
        }

        /// <summary>
        /// Can be called on enum to compare it to string
        /// </summary>
        /// <typeparam name="T">Enum type</typeparam>
        /// <param name="value">Enum value</param>
        /// <param name="description">String to compate to</param>
        /// <returns>Returns true if enum description matches string else returns false</returns>
        public static bool Equals<T>(this Enum value, string description)
        {
            var enumValue = description.ToEnum<T>();
            return value.Equals(enumValue);
        }

        /// <summary>
        /// Can be called on string to compare it to enum
        /// </summary>
        /// <typeparam name="T">Enum type</typeparam>
        /// <param name="description">Description string on which it is called</param>
        /// <param name="enumValue">Enum value to compare string to</param>
        /// <returns>Returns true if enum description matches string else returns false</returns>
        public static bool Equals<T>(this string description, T enumValue)
        {
            var newEnum = description.ToEnum<T>();
            return newEnum.Equals(enumValue);
        }
    }
}


And while we call these methods, code looks very clean:


 
using System;

namespace EnumUtility
{
    class Program
    {
        static void Main(string[] args)
        {
            string strUS = "US Dollars";

            // string to enum
            var currencyUS = strUS.ToEnum<Currency>();

            // enum to string
            Console.WriteLine(currencyUS.ToDescription());

            // enum compared to string
            Console.WriteLine(currencyUS.Equals<Currency>(strUS));

            // string compared to enum
            Console.WriteLine(strUS.Equals<Currency>(currencyUS));

            Console.ReadKey();
        }
    }
}

Thursday, January 5, 2012

Configuration files transformation for .net applications (both web and client)


Ever felt a need for different configuration files for different environments? This is a very common requirement where you want your configurations to adapt based on the environment it is built for. Let’s see how we have done it for different types of projects (web and client). What is available out of the box and what’s not?

Scenario
We have three different environments for which we change the environment specific values in the configuration file and fire the build. That’s sounds good until we ran into issues described in next section.

What is the issue?
There were two issues that we came across:
1.    We have different projects which have environment specific configuration, whenever we need a build for new environment we have to manually change the values at tons of places which is cumbersome and error-prone. It will become impossible to do once more and more projects are added which have environment specific data. To add to the pain more environments are in place for which we need to build for every deployment cycle. 
2.    For web projects, config transformation is supported out of the box but for client applications, config transformation is not supported at all. In Step 6 of “Code and Steps” below, we have followed different approaches to address this issue.


How to resolve the issue?
While resolving the issue you need to identify the environment specific data. Some of the environment specific data that we generally have is server urls, services bindings, connection strings, etc. Try to keep them in the config files of the projects. You can change the value during build based on the target build environment.
For accomplishing this while building, you can have different build configuration for different environments. Taking into account say, you have two different environments for QA and Production. You need to do the following:
1.    You need to make environment specific build configuration and add that to the solution.
2.    Update configuration in each project which has value that changes with environment.
3.    For picking up the specific configuration in the code from config files you can :
a.    Either transform config files based on the build configuration selected.
b.    Or pick altogether a different config file every time based on the build configuration selected. (You need to do this as client applications do not support config transformation.)


Code and Steps for implementing the solution

First you need to create environment specific configuration data. For accomplishing this while building, we can have different build configuration for different environments. Say, you have two different environments for QA and Production. You need to do the following:
1.    Right click on Solution and Select “Configuration Manager” :
     
2.    In Configuration Manager, select Active Solution Configuration as “New” :
    
NOTE:  You need to modify Active Solution Platform based on your environment settings.

3.    In New Solution Configuration dialog, give the name for the configuration, say “QADeploy”. Just copy the settings from any configuration you think is the similar to the current one and then you can change the values of properties opening the project file. Similarly make configuration for production, say “ProdDeploy”.
    
4.    Now if you open the project file, you can see configuration section added, something like this:
    
NOTE : The properties may differ based on your Visual Studio settings.

5.    You can add more properties or can modify existing ones based on the environment specific needs.
     
6.    Now based on the build configuration you can either transform config files or you can include all together a different config file for different configuration. Let’s see one by one how you can accomplish this.

a.    First Approach : Config Transformation
This approach is useful in case of Web.config which supports config transformation out of the box. Follow the steps listed below:
i.      In Solution Explorer, right-click the Web.config file and then click Add Config Transforms.
    
ii.     Open the transform file for the build configuration that you want to work with :
    
iii.    Edit the transform file to specify the changes that should be made to the deployed Web.config file when you deploy by using that build configuration.
The following example shows how to use the Match locator and the SetAttributes transform attribute. The Match locator attribute identifies the add element in the connectionStrings section as the element to change. The SetAttributes transform attribute specifies that this element's connectionString attribute should be changed to "NewEnvironmentSQLServer".


For more information about how to write transform files, see Web.config Transformation Syntax for Web Application Project Deployment.
iv.   Save and close the transform file.

b.    Second Approach : Including different config based on Build Configuraiton
This approach is particularly useful in case of App.config which does not support config transformation. Follow the steps listed below for including different App.config for different build configuration
i.      Add a folder named Configs in your project.
ii.     Add a folder for each environment under this folder.
iii.    Copy your App.config from project and add it in each of the folder created.
     
iv.   Make changes to the config file based on the environment, e.g. - changing the database connection strings, etc.
v.    REMOVE the following code from the project file:
<None Include="App.config" />
<None Include="Configs\Prod\App.config" />
<None Include="Configs\QA\App.config" />

vi.   ADD the following code to project file for different configurations:
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == Debug|AnyCPU' ">
    <None Include="App.config" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == Release|AnyCPU' ">
    <None Include="App.config" />
</ItemGroup>       
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'QADeploy|AnyCPU' ">
    <None Include="Configs\QA\App.config" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'ProdDeploy|AnyCPU' ">
    <None Include="Configs\Prod\App.config" />
</ItemGroup>
NOTE : Platform may differ, it can be AnyCPU, x86, x64, etc. based on your settings. Basically in the code above, its defined from which folder to pick App.Config based on the BuildConfiguration property value.

7.    You are good to build your solution using the build configuration and your environment specific configuration will automatically be picked according to the configuration specified.