Implementing Security Concepts


The security services provided by the .NET Framework work together to fulfill three important functions: user authentication, user authorization, and data encryption. User authentication validates the credentials supplied by the user. User authorization determines the data and services, if any, with which the user is permitted to interact. Data encryption encodes sensitive data into an unreadable format for storage or transport.

Validating Specific Users

The first step every security implementation must undertake is user authentication. This process validates user credentials against a specified authority, such as a directory service, a database table, or a configuration file. The three most common implementations of user authentication include Windows Authentication, Forms Authentication, and Passport Authentication.

Applying Basic Windows Authentication

The simplest form of user authentication to implement is Windows Authentication. It is the simplest implementation because the file server does all of the validation work. When a user requests a resource, such as a specific Web page, the Web server determines if the user has been authenticated. If not, they are prompted as shown in Figure 13-2.


Figure 13-2: The Windows Authentication login prompt

The user credentials are validated against the server account for authentication as defined by the Internet Information Services (IIS) directory security options. As Figure 13-3 illustrates, you can configure IIS to perform a variety of security functions, such as basic authentication, digest authentication, or Kerberos/NTLM Windows authentication.


Figure 13-3: Configuring Windows authentication options in IIS

Although this is simplest method of authentication to implement, it is also the least flexible. To validate user accounts, the network administrator must first add them to the server. This might work out within a small- sized or medium-sized enterprise, but it is virtually impossible to open up to any Web accessible user.

Because Windows Authentication is the default user authentication implementation for applications built within Visual Studio .NET, it is already specified in the Web.config file created for new projects. Listing 13-1 displays an excerpt from the default Web.config file generated for a Web application.

Listing 13-1: Web.config File Set to Windows Authentication Mode
start example
 <configuration>     <system.web>         <authentication mode="Windows" />       </system.web> </configuration> 
end example
 

When a new Web application starts, however, it does not prompt a connected user automatically for login credentials. This is because the Web.config file does not explicitly demand authentication. To add this functionality, add the additional Extensible Markup Language (XML) tags as outlined in Listing 13-2.

Listing 13-2: Web.config File Set to Refuse Anonymous Connections
start example
 <configuration>     <system.web>         <authentication mode="Windows" />  <authorization>   <deny users="?" />   </authorization>  </system.web> </configuration> 
end example
 

No additional coding is necessary. The updated Web.config file specifies that any user requesting a page must have a login account on the hosting Web server. Users who are authenticated by the server are permitted to access the resource. Otherwise, all other users must be denied access. The ? value in the <deny> tag specifies that anonymous users are not permitted.

The permissions defined within the Web.config file apply to all files within the same directory. You can set permissions in multiple Web.config files located in different subdirectories or within the same Web.config file using the <location> tag, as outlined in Listing 13-3.

Listing 13-3: Spanning the Web.config Directory Permissions Across Multiple Directories
start example
  <location path="Pages">  <! Apply security settings to all files located in the 'Pages' directory >     <system.web>         <authorization>             <deny users="?" />         </authorization>         </system.web>     </location> 
end example
 

Behind the scenes, after the server validates the login credentials, a WindowsAuthenticationModule provider object creates and populates a WindowsPrincipal object and a WindowsIdentity object. Both objects manage the identity of the connected user as well as his or her assigned roles. They are assigned to the local HttpContext (see Listing 13-4).

Listing 13-4: Viewing a User's Login Identity
start example
 private void Page_Load(object sender, System.EventArgs e) {     string strMessage = "";     if(Request.IsAuthenticated)     {         string strUsername = HttpContext.Current.User.Identity.Name;         strMessage = "<h2>User: " + HttpContext.Current.User.Identity.Name;         strMessage += "<p>Administrator: ";         if(HttpContext.Current.User.IsInRole("Administrators"))             strMessage += "Yes";         else             strMessage +="No";         strMessage += "</h2>";         Response.Write(strMessage);     }     else         Response.Write("<h1>Not authenticated.<h1>");     return; } 
end example
 

This listing creates a simple test page to check the status of a login and report if the user has been authenticated. First, it calls the Request object's IsAuthenticated method. If it returns true, the code constructs a string that displays the username as captured by the WindowsIdentity object. Next , the string construction continues as the IsInRole method evaluates whether this user is a member of the Administrators role. (Determining a user membership within a role is another critical security concept covered later in the "Authorizing User Capabilities" section.) Finally, the listing returns the constructed string to the browser for display.

Again, this is a simple method for user validation that might work within the enterprise, but not for applications exposed to the Web. Also, this approach places all the trust into the hands of the file server. If the file server security is breached, the enterprise application is not insulated from that breach.

Applying Web Forms Authentication

An even better approach to user authentication is through Forms Authentication. With this approach, applications manage their own set of credentials rather than referencing the file server or Active Directory. Forms Authentication captures user credentials from a user interface form and validates them against an application-defined authority such as a database table or configuration file. Any user attempting to request access to part of the application is redirected to a central login page, as shown in Figure 13-4.

click to expand
Figure 13-4: The default login form specified for Forms Authentication

The application tracks an authenticated user with the help of a client-side cookie. This enables the user to explore the Web application without being constantly prompted to log in. To enable Forms Authentication, edit the Web.config file as shown in Listing 13-5.

Listing 13-5: Enabling Forms Authentication in the Web.config File
start example
 <authentication mode="Forms">     <forms name=".ASPXAUTH" loginUrl="pages/app_login.aspx"         protection="Encryption" timeout="30">         <credentials passwordFormat="Clear">             <user name="John" password="openup" />         </credentials>     </forms> </authentication> <location path="Pages">     <system.web>         <authorization>             <deny users="*" />         </authorization>     </system.web> </location> 
end example
 

The changes to the Web.config file include setting the authentication mode to Forms Authentication. The listing also adds an XML tag, <forms>, along with a few custom attributes. The optional name attribute specifies the name of the cookie file created on the client computer. The loginUrl attribute specifies the location of the login page where unauthorized requests should be redirected. The protection attribute specifies how the authentication cookie should be protected. Protection methods include None, Encryption, Validation, and All. Finally, a timeout attribute specifies the number of minutes of inactivity that must elapse before the user is automatically logged out.

Another XML tag, <credentials>, identifies authenticated users and their passwords. Also included is the format used to encrypt the password. In this case, the user John has been added with a clear text password of openup . Two additional encoding formats are available to hash the password into an unreadable string: SHA1 and MD5. You could represent the same credentials in the following unreadable format:

 <credentials passwordFormat="SHA1">     <user name="John" password="3F36690145A773B6B6968827D5A6F19AE819205B" /> </credentials> 

To obtain an SHA1 hashed representation of a string, the .NET Framework provides a helpful method. The following method generates a hashed string from a clear text string and might be helpful if creating hashed values:

 private string HashPassword(string strPassword) {     return FormsAuthentication.HashPasswordForStoringInConfigFile(strPassword,         "SHA1"); } 

In addition to the changes in the Web.config file, you must also implement the login form to interact with the FormsAuthentication object. Listing 13-6 implements the login button event handler for the login page.

Listing 13-6: Processing the FormsAuthentication Object
start example
 private void btnLogin_Click(object sender, System.EventArgs e) {     if(FormsAuthentication.Authenticate(edtEmail.Text, edtPassword.Text))         FormsAuthentication.RedirectFromLoginPage(edtEmail.Text, false);     else         lblWarning.Visible = true;     return; } 
end example
 

The event handler invokes the static Authenticate method belonging to the FormsAuthentication object, passing the username and password retrieved from the user. If the method returns true, the user is redirected to their originally requested page. If Forms Authentication fails, a label indicates a failed login attempt.

Note  

It's useful to record the number of consecutive failed login attempts. If three or so consecutive attempts fail, the event should be logged and the account should be inactivated.

After Forms Authentication validate the user credentials, the code creates a new FormsIdentity object assigns it to a GenericPrincipal object within the local HttpContext. You can extract additional information, such as role membership, from the principal object via the HttpContext.User object.

Although Forms Authentication does not require validation against a server, it appears that validating against a Web.config file is even worse . An even better approach to Forms Authentication is to write a custom authentication method that validates user credentials against an application database (see Listing 13-7).

Listing 13-7: Validating Forms Authentication Against a Database
start example
 public bool DatabaseAuthenticate(string strUsername, string strPassword) {     SqlCommand command;     SqlParameter parameter;     bool boolAuthenticated = false;     //open connection     SqlConnection conn = new SqlConnection("server=jkanalakis;" +         "database=IssueTracker;uid=sa;pwd=");     try     {         conn.Open();         //initialize the command         command = new SqlCommand("app_ValidateLogin", conn);         command.CommandType = CommandType.StoredProcedure;         //add the email address parameter         parameter = new SqlParameter("@EmailAddress", SqlDbType.Char);         parameter.Direction = ParameterDirection.Input;         parameter.Value = strUsername;         command.Parameters.Add(parameter);         //add the password parameter         parameter = new SqlParameter("@Password", SqlDbType.Char);         parameter.Direction = ParameterDirection.Input;         parameter.Value = strPassword;         command.Parameters.Add(parameter);         //execute query         if((int)command.ExecuteScalar() > 0)             boolAuthenticated = true;     }     catch(Exception x)     {         EventLog systemLog = new EventLog();         systemLog.Source = "IssueTracker";         systemLog.WriteEntry(x.Message, EventLogEntryType.Error, 0);     }     finally     {         conn.Close();         conn.Dispose();     }     return boolAuthenticated; } 
end example
 

This method receives a username and a password from the login page. Next, a stored procedure executes to validate the supplied credentials. The stored procedure uses an odd-looking query that performs a case-sensitive comparison by casting the password into a VARBINARY data type. If any records match the query, the login credentials are presumed valid and the method returns true:

 CREATE PROCEDURE [dbo].[app_ValidateLogin] (@EmailAddress char(256),     @Password char(16)) AS SET NOCOUNT ON; SELECT COUNT(*) FROM Dat_User WHERE EmailAddress = @EmailAddress AND CAST (RTRIM (Password) AS VARBINARY) = CAST (@Password AS VARBINARY); GO 

After validating the user credentials, all that needs to change is the login button's event handler. In Listing 13-8, the updated handler invokes the custom database authentication method instead of the stock Authenticate method.

Listing 13-8: Changes to the Login Event Handler to Support Database Validation
start example
 private void btnLogin_Click(object sender, System.EventArgs e) {     if(DatabaseAuthenticate(edtEmail.Text, edtPassword.Text))  FormsAuthentication.RedirectFromLoginPage(edtEmail.Text, false);  else         lblWarning.Visible = true;     return; } 
end example
 

The last step in the process is to handle the user logout. Because user logout should occur from just about any location within the application, it makes sense that it should be implemented as part of the application framework. A useful way to implement this is with another Web user control. In the IssueTracker solution, add a new Web user control with a single link button control. Set the link button's Label attribute to Logout and its Name attribute to lnkLogout. Finally, implement the link button's event handler as outlined in Listing 13-9.

Listing 13-9: Implementing a Logout Web User Control
start example
 private void lnkLogout_Click(object sender, System.EventArgs e) {     FormsAuthentication.SignOut();     Session.Clear();     Response.Redirect("Pages/app_login.aspx");     return; } 
end example
 

This short method simply invokes the SignOut method belonging to the FormsAuthentication object. Next, the Session object is cleaned up, and the user is redirected to the application's login page. You can add this user control to each application page header for consistent logout functionality.

Applying Passport Authentication

Web applications can benefit from the rich credential validation provided by Passport's single sign-on service. This enables users to maintain a single login account valid for a variety of Web services and applications. Because the Passport service also captures user profiles, it reduces unnecessary complexity behind new account creation. Rather than creating new account entry forms to capture user contact information, Passport-enabled Web sites and applications can subscribe to the Passport service to retrieve user profiles based on their login credentials. Figure 13-5 shows the Passport version of the application start page.

click to expand
Figure 13-5: Default Passport sign-in page for IssueTracker

In this form, two functional elements are visible to the user: a status label that currently reads Click 'Sign In' to get started and a Passport Sign In button. On the Web form, both elements are implemented as simple label controls with Text attributes based upon the Passport login. To enable Passport Authentication, modify the Web.config file to specify Passport as the authentication mode, as outlined in Listing 13-10.

Listing 13-10: Enabling Passport Authentication in the Web.config File
start example
 <configuration>     <system.web>  <authentication mode="Passport"/>  </system.web> </configuration> <authorization>     <allow users="passport_account@msn.com" />     <deny users="*" /> </authorization> 
end example
 

In this case, the only user allowed access to the application is <passport_account@msn.com.> When an unauthorized user requests a Web page, a prompt for Passport Authentication appears, as illustrated in Figure 13-6.


Figure 13-6: The Passport Authentication login prompt

The user credentials are submitted securely to the Passport authority site where they are authenticated. The only other changes necessary are to the application start page, which must display the Sign In button. Listing 13-11 outlines the new application start page, protected by Passport Authentication.

Listing 13-11: Rendering the Passport Sign In/Sign Out Buttons and Status
start example
 private void Page_Load(object sender, System.EventArgs e) {     int intDefault = -1;     int intTimeWindow = 7200;     bool boolForceLogin = false;     string strSrvPath = "http://" + Request.Url.Host + Request.Url.AbsolutePath;     PassportIdentity passport = new PassportIdentity();     if(passport.GetFromNetworkServer)         Response.Redirect(strSrvPath);     if(passport.GetIsAuthenticated(intTimeWindow, boolForceLogin, false))         lblStatus.Text = "Click 'Sign Out' to logout.";     else if(passport.HasTicket)         passport.LoginUser(HttpUtility.UrlEncode(strSrvPath),         intTimeWindow, boolForceLogin, null, intDefault, null, intDefault,         false, null);     else         lblStatus.Text = "Click 'Sign In' to get started.";     lblPassportBtn.Text = passport.LogoTag2(HttpUtility.UrlEncode(strSrvPath),         intTimeWindow, boolForceLogin, null, intDefault, false, null, intDefault,          false);     return; } 
end example
 

This method begins by setting default values required by Passport. Next, it instantiates the PassportIdentity object. Then, the GetFromNetworkServer property indicates true if the Passport server returns the active page. If so, you can clear extra cookie data appended to the URL by redirecting to the active page. The LoginUser method requires an encoded URL that indicates which page the Passport server should redirect to after authenticating the user. Next, a time window indicates how many minutes have elapsed since the last known login. Another parameter indicates if the Passport server should apply the time window of the last manual login or the refresh of the passport ticket. The LoginUser method forces a redirect to the Passport server. If the browser contains a passport ticket, it refreshes. Otherwise, a login on the Passport server is required. In either case, the browser redirects to the active page:

 passport.LoginUser(HttpUtility.UrlEncode(strSrvPath),     intTimeWindow, boolForceLogin, null, intDefault, null, intDefault,     false, null); 

This method returns a string pointing to a Sign In button image on the Passport server. When the user clicks the image button, the same steps take place. Users are redirected to the Passport server, returning them to the active page. If the user is already signed in and has an authentication ticket, the image changes to a Sign Out button.

On the server side, Passport is deployed in binary form and must be first downloaded and installed. Also, enabling Passport requires registration and an expensive subscription to the Passport network.

Note  

You can download the Passport Software Developer Kit (SDK) from Microsoft at http://www.microsoft.com/netservices/passport .

Authorizing the User Capabilities

After determining if a user can access an application, the next step is to determine what data and services the user is able to access. User authorization determines what system resources an authenticated user may access based upon their defined roles.

Creating Role-Based Security

Role-based security expands upon basic authorization services. As mentioned, basic authorization determines whether a user has sufficient permissions to access an application. Role-based security, however, determines what level of data can be accessed by a user. The process begins with the definition of discrete roles that categorize user profiles. Next, it determines what data each role is permitted to view and whether the role is able to add, edit, or delete data.

The Web.config file specifies which roles are allowed and denied access to server resources within the <authorization> tag. When Windows Authentication mode is applied, the FileAuthorizationModule object performs user and role validation against the Windows NT File System (NTFS) security manager. Similarly, when Forms or Passport Authentication is applied, the UrlAuthorizationModule object performs role validation. You can define any number of roles within the Web.config file:

 <authorization>     <allow roles="Administrators" />     <allow roles="Managers" />     <allow roles="All" />     <deny users="*" /> </authorization> 

The Application_AuthenticateRequest method evaluates role settings in the Web.config file. You can assign roles to a User object by adding them to a string array and passing the object to the GenericPrincipal object. Next, replace the current principal assigned to the HttpContext.User property. Any page within the application can invoke the IsInRole method to match a specified role against one within the passed array, as outlined in Listing 13-12. The role-based permissions provided by the .NET Framework rely largely on the identity and principal objects. Identities represent users within the software or operating system. Principals, however, represent the security context or Windows group of a user. A PrincipalPermission demands a role from a user. If the user has the necessary principal assigned, the request will succeed. PrincipalPermission is therefore an easy way to check role assignments.

Listing 13-12: Assigning a User to a Specific Set of Roles
start example
 protected void Application_AuthenticateRequest(Object sender, EventArgs e) {     HttpApplication application = (HttpApplication)sender;     if(application.Request.IsAuthenticated &&         application.User.Identity is FormsIdentity)     {         FormsIdentity identity = (FormsIdentity)application.User.Identity;         if(identity.Name == "demo")         {             application.Context.User = new GenericPrincipal(identity,                 new string[]                 { "Managers", "All" });         }     }     return; } 
end example
 

In the Web application code, user authorization is determined by referencing the HttpContext.User object and evaluating the user's role by checking the IsInRole property. Once the specific role is determined, a user interface element can decide to display data. In the case of IssueTracker, the DynamicMenu Web user control comprises three other user Web controls: MenuAdmin, MenuAll, and MenuManager. Each submenu displays commands that apply to a specific role. When the DynamicMenu control loads, it determines the login role of the user and displays the appropriate collection of menus (see Listing 13-13).

Listing 13-13: Dynamically Rendering Application Menus Based on User Role
start example
 private void Page_Load(object sender, System.EventArgs e) {     if(HttpContext.Current.User.IsInRole("Administrator"))         menuAdministrator.Visible = true;     if(HttpContext.Current.User.IsInRole("Manager"))         menuManager.Visible = true;     if(HttpContext.Current.User.IsInRole("All"))         menuAll.Visible = true;     return; } 
end example
 

When the form loads, the current user is evaluated for membership within the Administrator, Manager, and All roles. If so, then the respective menu Web user control becomes visible. This lets a form contain all necessary menus and only display the appropriate menu to the user with sufficient role-based permissions. Figure 13-7 illustrates the use of role-based menus within the IssueTracker application. Two different Web user controls display side by side, one for the Manager role and one for the All users role.

click to expand
Figure 13-7: Displaying role-based menus in the IssueTracker issues summary view

Using Code Access Security

Code access security protects access to system resources by granting or denying access at the code level. Code access security is applied as an attribute to a class, method, or property. As an object is instantiated or executed, permissions are checked against the current client's principal before being allowed to continue. The PrincipalPermission attribute specifies code access permissions for role-based security. Setting the attribute on an object, method, or attribute can prohibit access to that code (see Listing 13-14).

Listing 13-14: Enabling Code Access Security for Specific Methods and Roles
start example
 using System; using System.Security.Permissions; public class IssueManager {     [PrincipalPermission(SecurityAction.Demand, Role="All")]     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public IssueManager() {}     [PrincipalPermission(SecurityAction.Demand, Role="All")]     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public BusinessRules.IssueCollection GetAllIssues(){}     [PrincipalPermission(SecurityAction.Demand, Role="All")]     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public BusinessRules.Issue GetSpecificIssue(int argIntIssueID){}     [PrincipalPermission(SecurityAction.Demand, Role="All")]     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public bool InsertIssue(BusinessRules.Issue argIssue){}     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public bool UpdateIssue(BusinessRules.Issue argIssue){}     [PrincipalPermission(SecurityAction.Demand, Role="Manager")]     public bool DeleteIssue(BusinessRules.Issue argIssue){} } 
end example
 

This listing illustrates how you can modify the IssueManager object defined within the Business Facade project to include code access security. In this case, it has been determined that all users are able to view and insert issues. However, only managers are permitted to update or delete issues. The PrincipalPermission attribute specifies the code-level permissions. The SecurityAction.Demand action must verify the user's role before allowing the method to be invoked.

Protecting Data with Encryption

Cryptography secures data that is kept persistent within a file or database by scrambling a readable string into an unreadable string. The .NET Framework provides services either to hash a string or to encrypt a string into an unreadable format using single key or public/private key cryptographic algorithms. The most common use for hashing and encryption is to scramble a password or credit card number for persistence within a file or database.

Applying Data Hashing

Hashing is the process of converting a binary string of variable length into a fixed-length value through an algorithm. The string is manipulated by using its binary representation. A hash is often smaller than the original string and is generally unreadable.

An ideal hash algorithm cannot be reversed to "unhash" a value. Also, any string passed through the same hash algorithm must always produce the same hashed output. If two input strings vary by only one character, they should each produce different hashed output. The only way to decipher a hashed value is to run countless combinations of characters through the hash algorithm until finding a match.

Again, the .NET Framework provides a helpful method for producing an SHA1 or MD5 hash representation of a string. The following method generates a hashed string from a clear text string:

 private string HashPassword(string strPassword) {     return FormsAuthentication.HashPasswordForStoringInConfigFile(strPassword,         "SHA1"); } 

Hashing compares two strings for equality without knowing the actual value of either string, only that they are equal or not. Passwords are perfect examples. Applications that require authentication manage each user's password in a database. When a user logs into the application, his or her password is entered and compared to an entry in the database. If the values match, the user is authenticated. Ideally, a password should be stored as a hashed value in the database. When the user enters his or her login, that value is also hashed before it is transmitted for verification. Instead of clear text passwords being intercepted by packet-sniffing tools, only unreadable strings are intercepted. The user will only be authenticated if the two hashed values match. Also, anyone who manages to obtain query permissions to the database is still unable to view the data.

Applying Symmetrical Encryption

Unlike hashing algorithms, encryption algorithms encode data in a method that enables them to be decrypted later. Encryption algorithms use keys to encrypt and decrypt data. Encryption keys are usually numeric values that are 32 to 256 bits long and that essentially randomize the encryption algorithm. Data encrypted with same encryption algorithm using two different keys should produce two different results. However, data encrypted with the same key and same algorithm should always produce the same results.

There are two different methods of data encryption: symmetric and asymmetric. Symmetric encryption uses a single key for data encryption and decryption. The encryption key exists for the time it takes to encrypt and exchange data and is then destroyed . It is important to keep these session keys secure. Once a session key has been compromised, information can no longer be securely exchanged because anyone with the key can decrypt it.

.NET implements a number of well-known symmetrical encryption algorithms. The .NET Framework provides services for generating random session keys or obtaining pass phrases. Pass phrases are useful when storing information such as a credit card number that might not be retrieved for some time. .NET creates a pass phrase, stores it in a secure location, and then uses it to regenerate the session key when needed to encrypt or decrypt data. Like pass phrases, randomly generated keys can also be saved for later use.

You can use the following methods to take an ordinary string and encrypt it into an unreadable format for storage or transport and later decrypt it for subsequent reading. The CreateKey method, shown in Listing 13-15, creates the private key used to encode and decode the data. The EncryptData method encodes the data and returns an unreadable string. The DecryptData method takes that same string and the private key, and it returns the data into a human-readable string.

Listing 13-15: Public Key Generation Method
start example
 public void CreateKey(string strPassword, int intSize, ref byte[] byteKey,     ref byte[] byteIV) {     int intIndex;     byte[] byteData;     byte[] byteHash;     byteData = Encoding.ASCII.GetBytes(strPassword);     SHA1CryptoServiceProvider providerCrypto = new SHA1CryptoServiceProvider();     int intLength = intSize / 8;     byteHash = providerCrypto.ComputeHash(byteData);     for(intIndex = 0; intIndex < intLength; intIndex++)         byteKey[intIndex] = byteHash[intIndex];     for(intIndex = intLength; intIndex < (2 * intLength); intIndex++)         byteIV[intIndex - intLength] = byteHash[intIndex];     return; } 
end example
 

This method creates a private key for use by the data encryption and decryption methods and leverages the .NET Framework's SHA1 hash creation functionality. The method initially accepts a password that is converted into a byte array. Next, this byte array runs through the SHA1CryptoServiceProvider to create a hashed value. The resulting value is properly sized and used to construct the Initialization Vector (IV). Both the hashed private key and initialization vector are required to perform the encryption and decryption.

The .NET Framework includes symmetric algorithms, such as the Data Encryption Standard (DES), to encrypt data with a single private key. Listing 13-16 presents the EncryptData method that takes a private key generated by the CreateKey method and writes an encrypted value to a memory stream.

Listing 13-16: Data Encryption Method
start example
 public string EncryptData(string strSource, string strPassword) {     byte[] byteIV;     byte[] byteKey;     byte[] byteData = Encoding.ASCII.GetBytes(strSource);     DESCryptoServiceProvider providerCrypto = new DESCryptoServiceProvider();     byteIV = new byte[providerCrypto.BlockSize/8];     byteKey = new byte[providerCrypto.BlockSize/8];     CreateKey(strPassword, providerCrypto.BlockSize, ref byteKey, ref byteIV);     providerCrypto.Key = byteKey;     providerCrypto.IV = byteIV;     MemoryStream streamMemory = new MemoryStream();     CryptoStream streamCrypto = new CryptoStream(streamMemory,         providerCrypto.CreateEncryptor(), CryptoStreamMode.Write);     for(int intIndex = 0; intIndex < byteData.Length; intIndex += 4096)     {         streamCrypto.Write(byteData, (intIndex*4096),             byteData.Length - (intIndex*4096));     }     streamCrypto.FlushFinalBlock();     byte[] byteResult = new byte[streamMemory.Length];     streamMemory.Seek(0, SeekOrigin.Begin);     streamMemory.Read(byteResult, 0, byteResult.Length);     streamMemory.Close();     streamCrypto.Close();     return Convert.ToBase64String(byteResult); } 
end example
 

This method begins by converting the string data into a byte array with the help of the Encoding object. Next, it creates the hashed value for the key and initialization vector. The key is 8 bytes long and determines how the encrypted output is generated. The initialization vector is appended to the encrypted output to add to the strength of the encryption. Next, a MemoryStream object is created to provide a protected buffer with which to work. The code also instantiates the CryptoStream object to perform the actual encryption. The MemoryStream object maintains the results of the encryption until read into a byte array. Finally, the ToBase64String method converts the byte array into a string.

The DecryptData method appearing in Listing 13-17 functions almost identically except that the CreateDecryptor method creates a DES decryptor. The encrypted data is converted from Base64 into a byte array before passing through the decryptor and finally converting into a normal string with the help of the Encoding object.

Listing 13-17: Data Decryption Method
start example
 public string DecryptData(string strSource, string strPassword) {     byte[] byteIV;     byte[] byteKey;     byte[] byteData = Convert.FromBase64String(strSource);     DESCryptoServiceProvider providerCrypto = new DESCryptoServiceProvider();     byteIV = new byte[providerCrypto.BlockSize/8];     byteKey = new byte[providerCrypto.BlockSize/8];     CreateKey(strPassword, providerCrypto.BlockSize, ref byteKey, ref byteIV);     providerCrypto.Key = byteKey;     providerCrypto.IV = byteIV;     MemoryStream streamMemory = new MemoryStream();     CryptoStream streamCrypto = new CryptoStream(streamMemory,         providerCrypto.CreateDecryptor(), CryptoStreamMode.Write);     for(int intIndex = 0; intIndex < byteData.Length; intIndex += 4096)     {         streamCrypto.Write(byteData, (intIndex*4096),             byteData.Length - (intIndex*4096));     }     streamCrypto.FlushFinalBlock();     byte[] byteResult = new byte[streamMemory.Length];     streamMemory.Seek(0, SeekOrigin.Begin);     streamMemory.Read(byteResult, 0, byteResult.Length);     streamMemory.Close();     streamCrypto.Close();     return Encoding.ASCII.GetString(byteResult); } 
end example
 

Encrypting data into a file stream is not very different from encrypting into a memory stream. With some small effort, you could modify these methods to write data straight to a data file. This can be a great advantage for applications that must write sensitive data to a persistent file for physical data exchange. This way, if you lose a floppy disk carrying sensitive data at the airport, you can still protect critical enterprise data.

Applying Asymmetrical Encryption

Although symmetric data encryption is effective in keeping sensitive data secure, everything can be easily compromised if the encryption key is exposed. Given that two entities must exchange an encryption key to exchange encrypted data, there exists a real possibility of the key being intercepted. Asymmetric data encryption addresses this situation directly. Unlike symmetric encryption, which uses the same key for encryption and decryption, asymmetric encryption uses two keys: a private key and a public key.

Information encrypted with the private key can only be decrypted with the public key, and information encrypted with the public key can only be decrypted with the private key. The public key is made available to anyone who asks for it, and the private key remains secret. Because the private key is never shared, asymmetric encryption is more secure than symmetric encryption.

The .NET Framework provides implementations of several well-known asymmetric encryption algorithms, such as those offered by RSA Security. The .NET Framework also simplifies the key exchange process. Public keys are exported as a Binary Large Object (BLOB) that can be saved to a file and sent to the client application. Once received, the client application uses the BLOB to reconstruct the public key and encrypt information. A single method call can generate an XML representation of a public key. The XML document can be transferred over a network to another application, and the client application can reconstruct the key with a single method call.

Symmetric and asymmetric encryption algorithms each have their advantages. Symmetric encryption is much faster than asymmetric encryption and is useful for quick encryption and decryption. Furthermore, there is little chance that someone might intercept the key. Asymmetric encryption is more secure because only one of the keys is shared and is ideal for communication over insecure environments, such as the Internet. Because of its relatively slow performance, asymmetric encryption should be reserved for small amounts of data, such as a password, a credit card number, or a symmetric key used for later symmetric encryption.




Developing. NET Enterprise Applications
Developing .NET Enterprise Applications
ISBN: 1590590465
EAN: 2147483647
Year: 2005
Pages: 119

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net