12 October 2015

Encrypting sections of .NET configuration files (web.config/app.config)

In a .NET application, we generally store application specific configuration information under “appSettings” sections of our configuration file (web.config / app.config). Under normal circumstances, this information is stored as plain text and anyone having read access to the configuration file can read them. If we need to store some confidential information in configuration file, it is a good idea to encrypt them so that it is not legible to prying eyes. It also ensures that if a hacker is able to get access to the configuration file, the information will still be safe due to its encrypted nature.

To achieve our objective, we can use ASP.NET IIS Registration tool (aspnet_regiis.exe), to encrypt sections in configuration file.

Let’s take a simple example of a web.config file in your website “C:\inetpub\wwwroot\MySampleWebSite” having “appSettings” section as shown below:-

<?xml version="1.0"?>
<configuration>
 
  <appSettings>
    <add key="SampleAppSettingKey" value="SampleAppSettingValue" />
  </appSettings>

  <system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>
  </system.web>
</configuration>

To access the value of appSetting key in code behind, you will be using code like this:-


string sampleKeyValue = ConfigurationManager.AppSettings["SampleAppSettingKey"].ToString();


Let’s say you want to encrypt all the data under “appSettings” section in your website “C:\inetpub\wwwroot\MySampleWebSite”. To do this, we need to execute the following command in Visual Studio command prompt.

General syntax of command


aspnet_regiis.exe -pef <section> <physical_directory> –prov <provider>

   -- or --

aspnet_regiis.exe -pe <section> -app <virtual_directory> –prov <provider>


Concrete syntax for our example


aspnet_regiis.exe -pef "appSettings" "C:\Inetpub\wwwroot\MySampleWebSite" –prov "DataProtectionConfigurationProvider"

   -- or --

aspnet_regiis.exe -pe "appSettings" -app "/MySampleWebSite" –prov "DataProtectionConfigurationProvider"


  • “appSettings” is the name of section in web.config file which needs to be encrypted.
  • “C:\Inetpub\wwwroot\MySampleWebSite” is the physical directory where our web.config file is located.
  • “DataProtectionConfigurationProvider” is the name of in-built .NET provider which is used to encrypt the data.

After executing this command, our web.config will look something like this:-


<?xml version="1.0"?>

<appSettings configProtectionProvider="DataProtectionConfigurationProvider">
    <EncryptedData>
      <CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAASQ/EStxMHUyguKcdTy1AzQQAAAACAAAAAAADZgAAqAAAABAAAABfeSptP6xLarF/zp02qg7AAAAAAASAAACgAAAAEAAAAKq03YHFEHRUId9btkN7pqnoAAAA2XEU9DY3KxRrDBxtdgSiLKLZW6uJv9zRuKQfBTkLiEhsXF+jtg5pIwec4yZ4/10xkYJL293nC/o7jCiFMvtdX0c9m18YlIr2xK1ux2FCrQURNCEUXJ+M4szB8OZTRZmZoiXMEqmwr9Wvernkn2RdjoCXrWIAYTG5ut+/PHiA42yv69TLJI7jQvdPyRKofjKyw4j2iJJyzNSG6V+KiPIlzgP494tmo+CAGMxcQTIp44CgIoruchcr3oKtGWgTk1KF4nbM3iFa3T06AwFMRtG0ZDtPOVv5uYX5vubmg0jGGaCXlKbTQQLLXRQAAADrc9Rr9YwhIo0tLYERIHWKoy+F0A==</CipherValue>
      </CipherData>
    </EncryptedData>
 </appSettings>

  <system.web>
   <compilation debug="true"/>
   <authentication mode="Windows"/>
 </system.web>
</configuration>


Note that the “appSettings” section has been modified by command to contain encrypted data as shown.

The advantage that lies with .NET is that inspite of appSetting values being encrypted, you need not write any additional code to decrypt the values in your code. You can continue using the following code to retrieve the value back.


string sampleKeyValue = ConfigurationManager.AppSettings["SampleAppSettingKey"].ToString();


The .NET internally decrypts the value on its own when reading them in C# code.

Selectively encrypting sections of configuration file

Generally, in our actual application, we do not want to encrypt the whole appSettings section but only certain keys which have confidential data. Unfortunately, .NET does not provide any way to selectively encrypt only certain appSetting keys in your configuration file. It only gives us the option to encrypt a whole section in configuration file. To circumvent this inflexibility, we can take following approach:-

  1. Create our own custom configuration section in configuration file.
  2. Store all the confidential data in the custom configuration section.
  3. Encrypt the custom configuration section.
  4. Write custom C# code to access the custom configuration section keys.

This approach is described below.

Define custom configuration section

To create a custom configuration section in configuration file, we need to first define a new section name in web.config file.


  <configSections>
    <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
        System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>


The version and public-key-token of DLL for System.Configuration may differ on your system and you may need to change it accordingly.

Add custom configuration section

Once the custom section name is defined, we can add the section in our web.config file like this:-


<secureAppSettings>
<add key="SampleSecureKey" value="SampleSecureKeyValue"/>
</secureAppSettings>


Encrypt the custom configuration section

Once all the keys are created in custom creation section, we can use following command to encrypt it.


aspnet_regiis.exe -pef "secureAppSettings" "C:\Inetpub\wwwroot\MySampleWebSite" –prov "DataProtectionConfigurationProvider"

   -- or –

aspnet_regiis.exe -pe "secureAppSettings" -app "/MySampleWebSite" –prov "DataProtectionConfigurationProvider"


Access custom configuration section keys

To access the keys of custom configuration section we need to write following code.


NameValueCollection secureAppSettings =          (NameValueCollection)ConfigurationManager.GetSection("secureAppSettings");

string sampleSecureKeyValue = secureAppSettings["SampleSecureKey"];



A sample web.config file before encryption is shown below:-


<?xml version="1.0"?>

<configuration>

  <configSections>
    <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
        System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>

  <appSettings>
    <add key="SampleAppSettingKey" value="SampleAppSettingValue" />
  </appSettings>
 
<secureAppSettings>
<add key="SampleSecureKey" value="SampleSecureKeyValue"/>
</secureAppSettings>

<system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>
</system.web>

</configuration>


The same file after section “secureAppSettings” is encrypted is shown below:-


<?xml version="1.0"?>

<configuration>

  <configSections>
    <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler,
        System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>
 
  <appSettings>
    <add key="SampleAppSettingKey" value="SampleAppSettingValue" />
  </appSettings>

  <secureAppSettings configProtectionProvider="DataProtectionConfigurationProvider">
    <EncryptedData>
      <CipherData>
  <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAASQ/EStxMHUyguKcdTy1AzQQAAAACAAAAAAADZgAAqAAAABAAAABfeSptP6xLarF/zp02qg7AAAAAAASAAACgAAAAEAAAAKq03YHFEHRUId9btkN7pqnoAAAA2XEU9DY3KxRrDBxtdgSiLKLZW6uJv9zRuKQfBTkLiEhsXF+jtg5pIwec4yZ4/10xkYJL293nC/o7jCiFMvtdX0c9m18YlIr2xK1ux2FCrQURNCEUXJ+M4szB8OZTRZmZoiXMEqmwr9Wvernkn2RdjoCXrWIAYTG5ut+/PHiA42yv69TLJI7jQvdPyRKofjKyw4j2iJJyzNSG6V+KiPIlzgP494tmo+CAGMxcQTIp44CgIoruchcr3oKtGWgTk1KF4nbM3iFa3T06AwFMRtG0ZDtPOVv5uYX5vubmg0jGGaCXlKbTQQLLXRQAAADrc9Rr9YwhIo0tLYERIHWKoy+F0A==</CipherValue>
      </CipherData>
    </EncryptedData>
  </secureAppSettings>

<system.web>
    <compilation debug="true"/>
    <authentication mode="Windows"/>
</system.web>

</configuration>


Decrypting an encrypted section in configuration file

Sometimes, we may need to add another key in our encrypted section. At these times, we may first need to decrypt the section, add the new key and re-encrypt it again. To decrypt an already encrypted section, we just need to tweak the aspnet_regiis command a bit as shown:-

General syntax of command


aspnet_regiis.exe -pdf <section> <physical_directory> –prov <provider>

   -- or --

aspnet_regiis.exe -pd <section> -app <virtual_directory> –prov <provider>


Concrete syntax for our example


aspnet_regiis.exe -pdf "appSettings" "C:\Inetpub\wwwroot\MySampleWebSite" –prov "DataProtectionConfigurationProvider"

   -- or --

aspnet_regiis.exe -pd "appSettings" -app "/MySampleWebSite" –prov "DataProtectionConfigurationProvider"



Note that option “-pef/-pe” in earlier commands was replaced with “-pdf/-pd” for decrypting a particular section.

11 October 2015

SharePoint Provider Hosted App Model apps - Session Storage

If you are planning to use sessions in your SharePoint Provider Hosted App model applications (ASP.NET Web App, ASP.NET MVC), please ensure to use Session-mode as ‘SqlServer’.

Default mode of Session in ASP.NET is ‘InProc’  which will work fine only in Single Server environment.

For Web-farm environment, we need to use  Session-mode as either ‘StateServer’ or ‘SqlServer’.

In StateServer mode, sessions are stored in one of the servers in Web-farm. But this server may go down and our application will not work.

Hence recommended approach is to use ‘SqlServer’ mode where sessions are stored in SQL-Server database.

Also we need to ensure that objects stored in session are Serializable otherwise it will not work with StateServer or SqlServer mode.


10 October 2015

Archiving files in SQL Server as BLOB

In one of my projects, we had requirement of archiving text and excel files in database. We discovered that this can be done as BLOB fields. BLOB stands for Binary Large Objects. This data-type is available in most of the modern databases. In SQL Server 2005 - VARCHAR (MAX), NVARCHAR (MAX) and VARBINARY (MAX) data-types provides this functionality.

For achieving our goal, we had to take care of following things:-

  1. Designing a table structure to store file metadata and file contents.
  2. Writing stored procedures to insert and retrieve data from archival table.
  3. Writing .NET application code which can store files and retrieve them when needed.
Designing a table structure to store files

First we need a table to store our archived data. Let’s name this table as “tblFileArchival”. The structure of table is shown below:-











The table has following fields:-

  1. ArchivalID – Auto incrementing (identity) integer field.
  2. ArchivalDateTime – Field for storing date and time of file archival
  3. ArchivalFileName – Field for storing name of file
  4. ArchivedFileSource – Field for storing source of file. This can be an optional field for your requirement. Hence it can be marked as Nullable if needed.
  5. ArchivedData – Field for storing actual file data. Please note that data type for this field is varbinary (max).
  6. ArchivedFileExtn – Extension of stored file. (Eg.- xls, txt)
  7. ArchivedFileIODirection – In our application, we have to store whether the file is an input to our system or output from our system. Hence this field was used. This may not be required for your requirement. Hence it can be marked as Nullable if needed.
The SQL for creating the table is as follows:-

CREATE TABLE [dbo].[tblFileArchival](
      [ArchivalID] [int] IDENTITY(1,1) NOT NULL,
      [ArchivalDateTime] [datetime] NOT NULL,
      [ArchivedFileName] [nvarchar](100) NOT NULL,
      [ArchivedFileSource] [nvarchar](15) NOT NULL,
      [ArchivedData] [varbinary](max) NOT NULL,
      [ArchivedFileExtn] [nvarchar](4) NOT NULL,
      [ArchivedFileIODirection] [nvarchar](6) NOT NULL,
 CONSTRAINT [PK_tblFileArchival] PRIMARY KEY CLUSTERED
(
      [ArchivalID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Stored Procedure to insert data into table

We need a stored procedure to insert data into table. This procedure will take following input parameters:-

  • Name of file to be archived.
  • Source of file to be archived.
  • Data of file.
  • Extension of file to be archived.
  • File IO direction.

The SQL for stored procedure is as follows:-

CREATE PROCEDURE [dbo].[uspArchiveFile]
      (
      @ArchivedFileName nvarchar(100),
      @ArchivedFileSource nvarchar(15),
      @ArchivedData varbinary(max),
      @ArchivedFileExtn nvarchar(4),
      @ArchivedFileIODirection nvarchar(6)
      )
AS
BEGIN

INSERT INTO [tblFileArchival]
           ([ArchivalDateTime]
           ,[ArchivedFileName]
           ,[ArchivedFileSource]
           ,[ArchivedData]
           ,[ArchivedFileExtn]
           ,[ArchivedFileIODirection])
     VALUES
           (GETDATE()
           ,@ArchivedFileName
           ,@ArchivedFileSource
           ,@ArchivedData
           ,@ArchivedFileExtn
           ,@ArchivedFileIODirection);
END

Stored Procedure to retrieve data from table

We need a stored procedure to retrieve archived file metadata and file data from the table. This stored procedure will take one input parameter - “ArchivalID” of the file whose data needs to be retrieved and will return that data.

The SQL for stored procedure is as follows:-

CREATE PROCEDURE [dbo].[uspGetArchivedFile]
      @ArchivalId int
AS
BEGIN
      SELECT [ArchivalID]
              ,[ArchivalDateTime]
              ,[ArchivedFileName]
              ,[ArchivedFileSource]
              ,[ArchivedData]
              ,[ArchivedFileExtn]
              ,[ArchivedFileIODirection]
      FROM tblFileArchival
     WHERE [ArchivalID] = @ArchivalId
END

.NET Code to archive file in database

To archive a file in database in a field of type VARBINARY (MAX), we need to read the contents of file and pass them to our insert stored procedure as an array of bytes.

The following code shows how to achieve the same.

FileStream fileStream = null;

try
{
    //Open the file and read its contents in a byte array using file stream
    fileStream = File.OpenRead(FilePath);
    int length = (int)fileStream.Length;
    byte[] byteArray = new byte[length];

    fileStream.Read(byteArray, 0, length);

    //Place the file details and content in database
    ArchiveDataToDB(FileName,
                    ReceivedFrom,
                    byteArray,
                    FileExtn,
                    FileIODirection);
}
catch (Exception ex)
{
    throw ex;
}
finally
{
    if (fileStream != null)
    {
        fileStream.Close();
        fileStream = null;
    }
}

Here we have all the required file properties in following variables:-

  • FilePath
  • FileName
  • ReceivedFrom
  • FileExtn
  • FileIODirection

Before calling the stored procedure we need the contents of file also. This is achieved using following steps:-

  • Create a fileStream object and open the file using it.
  • Get the length of the file using fileStream.Length.
  • Create a byte array of size fileSream.Length.
  • Read the file from start till end in this byte array using fileStream.Read method.

Now call the function “ArchiveDataToDB” passing all file information as input to it. The function “ArchiveDataToDB” contains ADO.NET code which calls insert stored procedure – “uspArchiveFile” passing all input parameters and stores the data in SQL Server. For brevity, the code for function “ArchiveDataToDB” is not given as this contains generic ADO.NET code used for executing a stored procedure. This type of code is generally project specific.

.NET code to retrieve archived file from database

There is no use of archiving a file in database if we can’t retrieve it. For regenerating the file from archived data, we need to write custom .NET code. Following code shows how to achieve it:-

FileStream fileStream = null;
BinaryWriter binaryWriter = null;
MemoryStream memStream = null;
byte[] binary = null;
const int chunkSize = 100;
int sizeToWrite = 0;

try
{
    //Get archived file metadata(details) and content from database
    DataSet dsArchivedFile = GetArchivedFile(archivalId);
    DataTable dtArchivedFile = dsArchivedFile.Tables[0];

    //if datatable is empty, it means no such file exists in database
    if (dtArchivedFile.Rows.Count == 0)
    {
        return "No file exists with archival id - " + archivalId;
    }

    //Get file name and extension
    string archivedFileName = dtArchivedFile.Rows[0]["ArchivedFileName"].ToString();
    string archivedFileExtn = dtArchivedFile.Rows[0]["ArchivedFileExtn"].ToString();

    if (!filePathToStore.EndsWith(@"\"))
    {
        filePathToStore += @"\";
    }

    //Create file path where file needs to be created
    filePathToStore = filePathToStore + archivedFileName + "." + archivedFileExtn.ToLower();

    //Create the file stream
    fileStream = new FileStream(filePathToStore, FileMode.Create, FileAccess.Write);
    binaryWriter = new BinaryWriter(fileStream);

    //Get file contents from Database in a byte array
    binary = (byte[])dtArchivedFile.Rows[0]["ArchivedData"];
    sizeToWrite = chunkSize;

    memStream = new MemoryStream(binary);

    //using chunksize, read the byte array and write it to binary writer
    for (int i = 0; i < binary.GetUpperBound(0) - 1; i = i + chunkSize)
    {
        if (i + chunkSize >= binary.Length)
        {
            sizeToWrite = binary.Length - i;
        }

        byte[] chunk = new byte[sizeToWrite];
        memStream.Read(chunk, 0, sizeToWrite);
        binaryWriter.Write(chunk);
        binaryWriter.Flush();
    }

    return "File generated at " + filePathToStore;
}
catch (Exception ex)
{
    throw ex;
}
finally
{

    if (fileStream != null)
    {
        fileStream.Close();
        fileStream.Dispose();
    }

    if (binaryWriter != null)
    {
        binaryWriter.Close();
    }
}

First we get all file details including file data using the stored procedure “uspGetArchivedFile” we created earlier. This stored procedure is executed by the function “GetArchivedFile” which takes “archivalID” as input and uses ADO.NET code to execute the stored procedure and get file details & data in a dataset. For brevity, the code for function “GetArchivedFile” is not given as this contains generic ADO.NET code used for executing a stored procedure. This type of code is generally project specific.

To generate the file from dataset, following steps are taken:-

  • Check if dataset is empty. If yes, we return an error message else we retrieve various file properties like filename, fileExtn in appropriate variables.
  • Create file path where file needs to be stored. This is achieved by specifying directory and appending file name and file extension to it.
  • Create a fileStream object in File create mode and specify the path where file needs to be stored. Open the fileStream object in write mode.
  • Create a binary writer from the file stream object.
  • Get the file contents from dataset in a byte array.
  • Now read this byte array by chunk-size of 100 and write it to binary writer.

The file will be generated at the mentioned file path.

Conclusion

Storing file information and data in SQL server provides a secure, reliable and cost effective way to archive files. The file data can be easily managed. It is also secure because only users who have access to SQL server can reach the table where file data is stored. Since file data is stored in VARBINARY field, it is in hexadecimal format which cannot be interpreted by naked eyes. Hence this provides additional security for data.

09 October 2015

Setup project in Visual Studio 2012 onward

Visual Studio 2012 onward do not support Install-shield to create setup project for deployments.

Recommendation from Microsoft is to use WIX for making setup projects.

Refer to following links for further details -

http://wixtoolset.org/

http://www.c-sharpcorner.com/UploadFile/cb88b2/getting-started-with-wix-windows-installer-xml-in-vs2012/

08 October 2015

ReSharper - Must have tool for .NET Developers

For .NET development projects, ‘ReSharper’ is a must have tool. It is plugged in Visual Studio as an Add-in and helps in following activities –
  1. Detects warnings and errors instantly as you type.
  2. Highlights wrong coding practices, code redundancies.
  3. Gives suggestions as you type which can improve overall quality of your code.
This will provide following advantages –
  1. Will save time spent in fixing warnings and errors later on.
  2. Will help in writing better code which will lead to less StyleCop and Code Analysis errors.
  3. Will reduces testing and code review comments.
  4. It can be customized according to project needs.

There is also StyleCop extension to ReSharper which will detect StyleCop errors on the fly. 

For more information, refer - http://www.jetbrains.com/resharper/