Developing a Secure Desktop Application Installation


This chapter has already demonstrated a number of ways to make your application more secure. For example, you can use zone security to ensure that no one can execute the code outside its intended environment. The following sections contain two additional ways to make your desktop applications more secure. The first section describes the StrongNameIdentityPermission class, which requires the caller to provide a specific public key to access an external module. This technique is extremely useful if you want to ensure that only those with proper identification can access your classes from a remote location. The second section describes the System.Reflection.Assembly.Evidence property in detail. This property will appear several times throughout the book, so you need to know more about it.

Using the StrongName IdentityPermission Class

The StrongNameIdentityPermission class is one of the better innovations for the .NET Framework. You generate a public/private key set that you use to sign all of your components and controls (or at least those that you want to access using this technique). Someone who wants to use the component or control, presents the public key as evidence that they’re granted access. Once CLR determines the public key is valid, the caller accesses the code as usual. This technique works exceptionally well when you know the parties you want to access your code. Given that many distributed applications fall into this category, this technique will work for a broad range of needs. Of course, you have to exercise care in handing out the keys to your code. Even this technique won’t work if a malicious third party gains access to your key and knows how to use it to access the code. Read the “StrongName versus Publisher Evidence” sidebar for additional information on this topic. The following sections take you through the process of using this technique.

start sidebar
StrongName versus Publisher Evidence

A strong name consists of a public/private key pair that a developer can generate on the local machine using the SN (Strong Name) utility. The developer applies the certificate using the [assembly: AssemblyKeyFile(“”)] entry in the AssemblyInfo file. This kind of security relies on your ability to determine the public key in advance. A trusted source would need to send the public key to you or you could use a key pair generated for your own use. A strong name doesn’t provide verification, nor can you determine who owns a key based on the information in the assembly alone. The StrongName evidence type is useful for personal or shared projects, but it isn’t something you could trust for projects with someone you don’t know.

On the other hand, Publisher evidence relies on an X.509 certificate (digital signature) created by a third party or Certificate Authority (CA) such as VeriSign (http://www.verisign.com), Entrust (http://www.entrust.com), or Thawte (http://www.thawte.com). A CA verifies the identity of an application independently of the Internet. This technique ensures that you can trust the identify information because you trust the CA. You’d normally use this technique to validate code received from a third party that you don’t know. Microsoft also provides the MakeCert (Make X.509 Certificate) and Cert2SPC (X.509 Certificate to Software Publisher Certificate) utilities to create temporary, test certificates. The certificate generated by these utilities is only good for testing purposes because it doesn’t contain the CA information. No matter what certificate source you use, you apply the certificate to an assembly using the SignCode utility.

You may read some texts that appear to say that one or the other form of evidence is better (usually with a strong emphasis on the Publisher evidence). Both forms of evidence have specific uses and you should use the form of evidence that best meets your needs. The important consideration is keeping the certificate or key file in a secure location to ensure that no one can sign code that you create. Neither form of evidence is worth anything once a third party has access to your key file.

end sidebar

Writing the StrongName IdentityPermission Class Code

The signing takes place in the client code—that’s what makes this method of code access so perfect. If the client doesn’t present the correct signature when it accesses your component code, the call fails. You can also reverse this process to improve mutual trust, but the example concentrates on the client. Listing 4.6 shows the simple client for this example. (The listing for this example contains only the essential information. You can find the full listing for this code in the \Chapter 04\C#\SignedClient or \Chapter 04\VB\SignedClient folder of the source code located on the Sybex Web site.)

Listing 4.6 Signed Client Example Code

start example
private void btnTest_Click(object sender, System.EventArgs e) {    MathFuncs   MyComp;  // The test component.    // Examine the evidence for this client.    IEnumerator Enum =       Assembly.GetExecutingAssembly().Evidence.GetHostEnumerator();    // Get the strong name for this client.    StrongName SN = null;    while (Enum.MoveNext())       if (Enum.Current.GetType() == typeof(StrongName))          SN = (StrongName)Enum.Current;    // Create the required permission.    StrongNameIdentityPermission  ClientPer;    ClientPer =       new StrongNameIdentityPermission(       SN.PublicKey, SN.Name, SN.Version);    ClientPer.Assert();    // Try to access the secure component.    try    {       // Create the test component.       MyComp = new MathFuncs(SN.Name);       // Perform a math operation.       Int32 MyValue;       MyValue = MyComp.MyAdd(1, 2);       // Display the result.       MessageBox.Show(          "The calculated value is: " + MyValue.ToString(),          "Calculation Result",          MessageBoxButtons.OK,          MessageBoxIcon.Information);    }    catch (SecurityException SE)    {       // The client didn’t have permission.       MessageBox.Show(SE.Message,                       "Error Creating Component",                       MessageBoxButtons.OK,                       MessageBoxIcon.Error);    } }
end example

The code begins by gathering evidence about the executing assembly. You must present evidence to the component before it will grant permission for use. The code creates the StrongName object for this assembly and grants itself the StrongNameIdentityPermission permission. If you don’t take this step, the component will never grant the program access, even if you sign it with the correct key. The error message you receive will never indicate that this is the problem, so this particular error is nearly impossible to find and fix. The code looks like it should work, but always remember that you must have permission. The remainder of the code is a simple object creation and test.

Of course, you haven’t signed the client yet. To do that, you need to generate a key pair using the Strong Name (SN) utility. Simply type SN -k MyKey at the command line and press Enter. Once you generate the key pair, open the AssemblyInfo file and locate the AssemblyKeyFile attribute. Your entry should look like this:

 [assembly: AssemblyKeyFile(“..\\..\\MyKey”)]

Make sure you compile the program with the new key. Once you do, you can use the procedure in the “Using SecUtil to Extract the Public Key from an Assembly” section to extract the public key.

Note

The example includes a second key for testing purposes, MyKey2. This key won’t match the key expected by the component, so the example will fail. Make sure you use the MyKey key for your initial test so you can see the code succeed!

Using SecUtil to Extract the Public Key from an Assembly

The SecUtil tool helps you extract the public key from a signed assembly. The public key helps you validate the signed assembly. To use this utility, you type SecUtil -s SignedClient.exe >> Out.txt at the command line. The resulting file contains a public key similar to the one shown in Figure 4.9.

click to expand
Figure 4.9: Define the permissions for your new permission set carefully to avoid security breaches.

Notice that this file contains the public key and the version number. You must include both elements as part of the security check. This means that the version number of your client application must remain stable or the security check will fail. Always set the AssemblyVersion attribute to a stable number, as shown here:

[assembly: AssemblyVersion(“1.1.0.0”)]

Creating the Component

The job of the component is to check the credentials of anyone making a request. The best place to perform this task is in the constructor. Listing 4.7 shows a typical example of a component constructor that checks for specific credentials.

Listing 4.7 Using a Component Constructor to Check Credentials

start example
public MathFuncs(String ClientStrongName) {    // Create an array for the public key.    Byte[]   PublicKey = {       0, 36, 0, 0, 4, 128, 0, 0, 148, 0, 0, 0, 6, 2, 0, 0, 0, 36,       0, 0, 82, 83, 65, 49, 0, 4, 0, 0, 1, 0, 1, 0, 83, 12, 9,       107, 146, 83, 124, 20, 21, 86, 251, 134, 236, 238, 161,       253, 206, 142, 35, 243, 186, 79, 38, 30, 178, 10, 4, 92,       204, 156, 54, 242, 202, 193, 93, 134, 119, 0, 210, 166,       172, 82, 8, 75, 91, 111, 21, 220, 196, 237, 136, 41, 185,       196, 70, 179, 108, 206, 239, 254, 176, 196, 78, 78, 116,       129, 221, 119, 192, 87, 171, 167, 158, 74, 188, 21, 48, 207,       226, 106, 63, 227, 44, 243, 119, 5, 40, 155, 247, 54, 207,       18, 245, 89, 232, 128, 75, 14, 59, 120, 9, 68, 250, 206,       151, 24, 43, 168, 70, 116, 218, 59, 227, 28, 80, 85, 61,       175, 161, 70, 124, 19, 251, 129, 10, 213, 6, 223 };    // Create a public key blob using the public key.    StrongNamePublicKeyBlob PublicKeyBlob;    PublicKeyBlob = new StrongNamePublicKeyBlob(PublicKey);    // Create a version number.    System.Version Ver;    Ver = new Version("1.1.0.0");    // Create the required permission.    StrongNameIdentityPermission  ClientPer;    ClientPer =       new StrongNameIdentityPermission(          PublicKeyBlob, ClientStrongName, Ver);    // Determine if the client has these characteristics.    ClientPer.Demand(); }
end example

If you compare the public key in this constructor with the public key in Figure 4.9, you’ll notice they match. You know the public key of the client because you added that key when you built the application. Consequently, this key should match the public key you used.

The constructor builds a StrongNamePublicKeyBlob and a Version object. You could also designate your own client name, making it possible for the constructor to check three elements of the caller. These three elements appear as input to the StrongNameIdentityPermission constructor. The code then uses the Demand() method to check security. If the caller’s credentials don’t match, the code generates a security exception.

Testing the Program

This example requires two tests. After you make each change, be sure to recompile the example so the changes take effect. Both tests require changes to the AssemblyInfo file. For the first test, change the AssemblyVersion attribute. Changing this attribute means the version number will no longer match the one anticipated by the component and the security access will fail.

After you test the AssemblyVersion attribute, change the value back to 1.1.0.0 and change the AssemblyKeyFile attribute to MyKey2. Changing this attribute means the signature will no longer match and the security access will fail.

These two tests show that you can provide very explicit access to components on your server. Writing the code using this technique makes it very tough for a cracker to break in using falsified information. However, nothing is foolproof, so you need to change keys as needed and maintain vigilance on your system.

Using the System.Reflection. Assembly.Evidence Property

Working directly with the various permissions and evidence can provide a lot of information. However, you’ll probably find that you don’t need this detailed level of interaction very often, which is why I only include a few of the vast number of security classes in this chapter. In many cases, the only thing you need to check for security needs is the System .Reflection.Assembly.Evidence property. Listing 4.8 shows how you can use this property to get four of the most important types of information from any object. (Note that this listing isn’t complete—it only shows the vital code. You can find the complete listing in the \Chapter 04\C#\EvidenceCheck or \Chapter 04\VB\EvidenceCheck folder of the source code located on the Sybex Web site.)

Listing 4.8 Using Reflection to Obtain Evidence

start example
private void btnTest_Click(object sender, System.EventArgs e) {    // Create a text object.    StringBuilder SB;    SB = new StringBuilder();    // Get the evidence for this object.    Evidence EV;    EV = SB.GetType().Module.Assembly.Evidence;    // Define an enumerator for the evidence.    IEnumerator Enum;    Enum = EV.GetHostEnumerator();    // Create an output string containing security information.    while (Enum.MoveNext())    {       // Check for the Zone.       if (Enum.Current.GetType() == typeof(Zone))       {          // Define a Zone variable.          Zone  TheZone;          TheZone = (Zone)Enum.Current;          // Create an output string for it.          SB.Append("Zone: ");          SB.Append(TheZone.SecurityZone);          SB.Append("\r\n");          SB.Append(TheZone.ToString());          SB.Append("\r\n");       }       // Check for the URL.       if (Enum.Current.GetType() == typeof(Url))       {          // Define a Zone variable.          Url  TheURL;          TheURL = (Url)Enum.Current;          // Create an output string for it.          SB.Append("URL: ");          SB.Append(TheURL.Value);          SB.Append("\r\n");          SB.Append(TheURL.ToString());          SB.Append("\r\n");       }       // Check for the StrongName.       if (Enum.Current.GetType() == typeof(StrongName))       {          // Define a Zone variable.          StrongName  TheSN;          TheSN = (StrongName)Enum.Current;          // Create an output string for it.          SB.Append("Name: ");          SB.Append(TheSN.Name);          SB.Append("\r\n");          SB.Append("Version: ");          SB.Append(TheSN.Version);          SB.Append("\r\n");          SB.Append("Public Key: ");          SB.Append(TheSN.PublicKey);          SB.Append("\r\n");          SB.Append(TheSN.ToString());          SB.Append("\r\n");       }       // Check for the Hash.       if (Enum.Current.GetType() == typeof(Hash))       {          // Define a Zone variable.          Hash  TheHash;          TheHash = (Hash)Enum.Current;          // Create an output string for it.          SB.Append("MD5 Hash: ");          SB.Append(ReadArray(TheHash.MD5));          SB.Append("\r\n");          SB.Append("SHA1 Hash: ");          SB.Append(ReadArray(TheHash.SHA1));          SB.Append("\r\n");       }    }    // Display the results.    MessageBox.Show(SB.ToString(),                    "Object Evidence",                    MessageBoxButtons.OK,                    MessageBoxIcon.Information); }
end example

The code begins by creating a StringBuilder object to store the textual evidence. You literally can use this technique on any object, so the example uses SB as the source of evidence as well. Notice the use of the Evidence class for this example. The code places the evidence for SB into EV using the GetType().Module.Assembly.Evidence property.

Most objects have four kinds of evidence: Zone, URL, StrongName, and Hash. The code creates an IEnumerator object to hold the content—the pieces of evidence, obtained using the GetHostEnumerator method.

All four evidence types have different information to offer, so you must check for their type within the enumeration (there isn’t any guarantee of order) and create a variable based on the Enum.Current property. Once the code coerces the property into the correct type, it can look at the evidence. The code presents the various types of information you can obtain. Each evidence object also contains methods to perform various tasks such as creating a permission, so this technique normally provides everything you need to validate the evidence an object presents. Figure 4.10 shows the output from this program.

click to expand
Figure 4.10: Use the information gathered from the Evidence property to validate objects loaded by your program.




.Net Development Security Solutions
.NET Development Security Solutions
ISBN: 0782142664
EAN: 2147483647
Year: 2003
Pages: 168

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