Click here to Skip to main content
Click here to Skip to main content
Go to top

Securing .NET Assemblies

, 21 Oct 2005
Rate this:
Please Sign up or sign in to vote.
Making .NET assemblies secure for an enterprise.

Introduction

We all have worked on .NET and we know there are so many security features that are available. One among them is CAS (Code Access Security). Well, I am not going to discuss what CAS is and how it works? Here, I'll try to explain how is CAS helpful in providing security to our applications?

Description

Most of us are working and developing applications on enterprise level and the architecture that we follow is n-tier. In a typical scenario, we have a UI layer, business layer and a data layer or if we talk about design patterns then we have the MVC pattern which again talks about different layers. When we work on enterprise level it becomes necessary to secure the code libraries from the outside world. This is due to certain business rules in a business layer, or in the case of data some data rules which they don’t want anyone to know. Keeping this in mind, we will implement an example of how to secure our development layers.

Let’s create an assembly, Assembly1. The code for Class1.cs is given below:

using System;
using System.Security.Permissions;
namespace Assembly1
{
    public class Class1
    {
        public Class1()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        public static string SayHello(string name)
        {
            return ("Hello " + name);
        }
        public static string MyData(string idTag)
        {
            return ("Did't Find " + idTag);
        }
    }
}

The code for AssemblyInfo.cs:

using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] 
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(@"..\..\..\key.snk")]
[assembly: AssemblyKeyName("")]

Now create a second assembly, Assembly2 (we will be using Assembly1 as reference):

The code for Class1.cs:

using System;
using Assembly1;
namespace Assembly2
{
    public class Class1
    {
        public Class1()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        public static string GetIDValue(string idTag)
        {
            // Some validation code
            return (Assembly1.Class1.MyData(idTag));
            //return(idTag + ", how can i help you");
        }
        public static string SayHello(string name)
        {
            return(name + ", how can i help you");
        }
    }
}

The code for AssemblyInfo.cs:

using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] 
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(@"..\..\..\key1.snk")]
[assembly: AssemblyKeyName("")]

Here comes our application which is referencing Assembly1. Create a form and add a button on it. On the click of a button add the following code:

MessageBox.Show(Assembly2.Class1.SayHello("User"));
MessageBox.Show(Assembly2.Class1.GetIDValue("I001G12"));

Don’t forget to add reference to Assembly2 in your Win Form application. So, in the end, the code is going to be something like this:

The code for TestAssembly - Form:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Reflection;
using System.Diagnostics; 
//using Assembly1;
using Assembly2;
namespace TestAssembly
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Button button1;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();
            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        #region Windows Form Designer generated code
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(112, 112);
            this.button1.Name = "button1";
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
        }
        #endregion
        [STAThread]
        static void Main() 
        {
            Application.Run(new Form1());
        }
        private void button1_Click(object sender, System.EventArgs e)
        {
            MessageBox.Show(Assembly2.Class1.SayHello("User"));
        
            MessageBox.Show(Assembly2.Class1.GetIDValue("I001G12"));
            // MessageBox.Show(Assembly1.Class1.SayHello("User"));
            // MessageBox.Show(Assembly1.Class1.MyData("I001G12"));
        }
    }
}

The code for AssemblyInfo.cs:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] 
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

Once you are done try executing the application, well it will work without any issues. But now the question is: Are these assemblies secured? Just signing it with a strong name key doesn’t make it secure as any one can use this assembly in his application.

Let's take a second step where we use CAS - declarative approach for securing our classes. Now add the following code above your class1 code in Assembly1:

[StrongNameIdentityPermissionAttribute(SecurityAction.LinkDemand, PublicKey = 
 "0024000004800000940000000602000000240000525341310004000001000100c39f40c9fccb7d"+
 "cca6519852af805844bea03b3ddd6fa74677f7eba4687205045c47d72b95e08a91e666d7a2f01d"+
 "483915cc87eb183f0a05b27900f1cfd7858d83da244dfffc021e9310f6049ac717ea89956bd22f"+
 "439082e9822125b48c072dc6a487d2ce9e49931434023593b3d10a54f12b86591cb58919400102"+
 "904e19d0")]

After adding the above code, try re-executing the application. The first message box is displayed but the second message box will throw a security exception to the user as the user is using a different key pair (key1.snk) to strong name Assembly2 (check the code of AssemblyInfo.cs). Now, change the Assembly2 key pair to Key.snk in assemblyinfo.cs and execute the code again. Now, everything will work the way it has to.

Let me explain what I did by adding the StrongNameIdentityPermissionAttribute: this attribute helps in protecting class' public key pair of a strong name key used which is key.snk. And whenever an assembly, say X, references it to use the methods then X should have the same key available and used in the code, otherwise he won't be able to use our assembly.

In the above code, I have specified SecurityAction.LinkDemand with a public key. LinkDemand helps in walking down the stack walk and checking each assembly's evidence. If it matches it will allow and if not it throws an exception. I would have used SecurityAction.Demand but Demand checks the evidence of the immediate caller and in the above case it’s winform.dll which doesn’t have this evidence so it's going to fail. Well, you can check this by replacing LinkDemand with Demand and executing the Win Form application.

Let’s change the above example. Take out the intermediate assembly Assembly2 and use Assembly1 in your application and on the click of a button comment the first code and add these two lines:

MessageBox.Show(Assembly1.Class1.SayHello("User"));
MessageBox.Show(Assembly1.Class1.MyData("I001G12"));

Now execute the code. What do you see in the result? As soon as you click the button a security exception pops up as your application doesn’t provide the right evidence. Now in the assemblyInfo.cs add the key file as:

@"..\..\..\key.snk"</CODE>

And then execute the code again. Now it will work the way it has to. Well, I think I have cleared the point of making assemblies secure.

Try creating a console application with the same assemblies and instead of LinkDemand use Demand. Check the results, it will work perfectly. The reason is when we execute a Win Form application the winform.Dll works as an intermediate between the form and the assembly which creates the problem but for the console application it’s a straight way compilation and so there is no intermediate DLL to interact with. That’s why it works perfectly.

In the end, I would like to say "Happy coding"!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

AtulMalhotra
Web Developer
United States United States
Currently i am working as a Technical Architect Microsoft .Net Practices in Sogeti USA, NY. I am one of the winner of Microsoft Architect Contest 2004. I have 8 years of experience in the IT industry. Doing R&D on new technologies and reading about them is one of my big time interests.

Comments and Discussions

 
GeneralCan't get it working with visual studio 2005 Pinmemberkvetter2-Oct-06 2:35 
GeneralRe: Can't get it working with visual studio 2005 PinmemberAtulMalhotra2-Oct-06 4:27 
QuestionRe: Can't get it working with visual studio 2005 PinmemberWalter Gandra20-Jun-07 14:42 
AnswerRe: Can't get it working with visual studio 2005 Pinmemberphuso24-Jul-07 5:37 
QuestionIs the public key should be the same Pinmemberkrishpuma26-Sep-06 9:59 
AnswerRe: Is the public key should be the same PinmemberAtulMalhotra2-Oct-06 4:26 
GeneralSugestion PinmemberHarkos25-Oct-05 1:18 
GeneralRe: Sugestion PinmemberAtulMalhotra25-Oct-05 2:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 21 Oct 2005
Article Copyright 2005 by AtulMalhotra
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid