Invoking the CryptoAPI

The CryptoAPI is a Windows CE API for performing common cryptographic functions such as computing hashes and encrypting or decrypting data. The .NET Compact Framework has no support for cryptographic functions that are accessible to the outside user. Thus, .NET Compact Framework developers must invoke the native CryptoAPI from their managed applications in order to perform cryptographic functions.

The CryptoAPI is a brittle API that will cause confusion for developers who don't understand some basic principles behind its use. The following sections review those basics before the rest of the chapter jumps into using the CryptoAPI.

Using Handles to Interact with CryptoAPI

Like many other functional areas in the Windows operating system, the CryptoAPI is driven by the concept of a handle. For example, encrypting data requires a handle to the cryptographic provider and a handle to the specific encryption key to be used. Programming against the CryptoAPI is really just the process of acquiring the correct handles from some functions and passing them in as arguments to other functions to achieve the desired results. However, the rules for using the handles correctly are very specific, and function calls fail if the handles are used incorrectly.

Understanding the CryptoAPI Context

The CryptoAPI architecture provides a standard interface for calling into cryptography functions, regardless of what the underlying algorithms are. Groups of algorithms are bundled together into packages called cryptographic service providers, or CSPs. The CSP used in the ManagedCryptoAPI wrapper class is the PROV_RSA_FULL provider. PROV_RSA_FULL provides encryption and hashing algorithms that are patented by the RSA and provided by Microsoft under license. It is a good general-purpose provider, and it is ubiquitous on Windows CE devices.

Other commonly used CSPs are shown in Table 14.1. Additionally, developers can write their own CSPs, and then anyone can access their algorithms through CryptoAPI.

Not all of the CSPs shown in Table 14.1 are included by default with Windows CE and Pocket PC devices. It is because the PROV_RSA_FULL provider is so common that we use it exclusively in the ManagedCryptoAPI wrapper.

Table 14.1. Cryptographic Service Providers Commonly Used with CryptoAPI
PROV_RSA_FULLPROV_RSA_SIG
PROV_DSSPROV_DSS_DH
PROV_SSLPROV_EC_ECDSA_SIG
PROV_EC_ECNRA_SIGPROV_EC_ECDSA_FULL
PROV_SPYRUS_LYNKSPROV_FORTEZZA
PROV_MSEXCHANGEPROV_RSA_CHANNEL

To perform any cryptographic function with the CryptoAPI, you must first acquire a context by calling CryptAcquireContext. Acquiring a context serves two purposes. First, it communicates to the CryptoAPI which CSP you want to use. Second, it tells the CryptoAPI which key container to use. A key container is a location where encryption keys are internally stored by CryptoAPI. As a developer, you usually interact with a key through its handle, and you let the CryptoAPI store the bytes of the key internally.

The DllImport definition for CryptAcquireContext in ManagedCryptoAPI looks like this:

C#
[DllImport("coredll.dll")]
private static extern bool CryptAcquireContext(ref IntPtr phProv,
         string pszContainer, string pszProvider, Int32 dwProvType,
         uint dwFlags);

VB
Declare Function CryptAcquireContext Lib "coredll.dll" (ByRef phProv As IntPtr,
         ByVal pszContainer As String, ByVal pszProvider As String, ByVal
         dwProvType As UInt32, ByVal dwFlags As UInt32) As Boolean

When acquiring a context, you may choose to use the default key container, or you may pass in a string name for a key container. It is strongly recommended that you pass in a name for the key container because other software on your device could already be using the default key container and you could inadvertently clobber the keys used by the other software. Specifically, the Pocket PC driver software for several brands of wireless network cards uses the default key container. If you also use the default container, you risk clobbering the keys that the driver software uses to encrypt wireless traffic, which would cause your wireless card to stop working suddenly.

ManagedCryptoAPI has two methods for acquiring a context. AcquireDefaultContext returns an IntPtr, which holds the handle to the default key container for the PROV_RSA_FULL provider. AcquireNamedContext returns an IntPtr, which holds the handle to a named key container that you pass in as a string. Programs retain the returned handle and pass it in to other methods that are discussed later in this chapter. The code for AcquireDefaultContext is shown in Listing 14.1.

Listing 14.1. AcquireDefaultContext
C#
public IntPtr AcquireDefaultContext()
{
   IntPtr hProvider = IntPtr.Zero;

   if (!CryptAcquireContext(ref hProvider, null, "Microsoft Base Cryptographic
            Provider v1.0", PROV_RSA_FULL, 0))
   {
      // We might have to create a new keyset...
      if (!CryptAcquireContext(ref hProvider, null, "Microsoft Base
              Cryptographic Provider v1.0", PROV_RSA_FULL, CRYPT_NEWKEYSET))
      {
         // Big trouble, could not create a new
         // keyset and could not acquire default keyset
         throw new Exception("ManagedCryptoAPI cannot access default
                  keyset or create new default keyset!");
      }
   }
   return hProvider;
}

VB
   Public Function AcquireDefaultContext() As IntPtr
      Dim hProvider As IntPtr = IntPtr.Zero

      If (CryptAcquireContext(hProvider, Nothing, "Microsoft Base Cryptographic
               Provider v1.0", PROV_RSA_FULL, Convert.ToUInt32(0)) = False) Then
      Dim l_Failure As String = TranslateErrorCode(Convert.ToInt64
              (GetLastError()))
      ' We might have to create a new keyset...
      If (CryptAcquireContext(hProvider, Nothing, "Microsoft Base Cryptographic
               Provider v1.0", PROV_RSA_FULL, CRYPT_NEWKEYSET) = False) Then
         ' Big trouble, could not create a new keyset and could not acquire
         ' default keyset
         Throw New Exception("ManagedCryptoAPI cannot access default keyset or
                  create new default keyset!" + TranslateErrorCode
                  (Convert.ToInt64(GetLastError())))
      End If

   End If
   Return hProvider
End Function

The code for AcquireNamedContext is in Listing 14.2.

Listing 14.2. AcquireNamedContext
C#
public IntPtr AcquireNamedContext(string in_ContainerName)
{
   IntPtr hProvider = IntPtr.Zero;
   if (!CryptAcquireContext(ref hProvider, in_ContainerName,
           "Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL,
           0))
   {
      if (!CryptAcquireContext(ref hProvider, in_ContainerName,
              "Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL,
              CRYPT_NEWKEYSET))
      {
         throw new Exception("ManagedCryptoAPI cannot access default
         keyset or create new default keyset!");
      }
   }
   return hProvider;
}

VB
Public Function AcquireNamedContext(ByVal in_ContainerName As String) As IntPtr
   Dim hProvider As IntPtr = IntPtr.Zero

   If (CryptAcquireContext(hProvider, in_ContainerName, "Microsoft Base
           Cryptographic Provider v1.0", PROV_RSA_FULL,
           Convert.ToUInt32(0)) = False) Then
      Dim l_Failure As String = TranslateErrorCode(Convert.ToInt64
              (GetLastError()))
      ' We might have to create a new keyset...
      If (CryptAcquireContext(hProvider, in_ContainerName, "Microsoft Base
              Cryptographic Provider v1.0", PROV_RSA_FULL, CRYPT_NEWKEYSET) = False)
              Then
         ' Big trouble, could not create a new keyset and could not acquire
         ' default keyset
         Throw New Exception("ManagedCryptoAPI cannot access default keyset or
                 create new default keyset!" + TranslateErrorCode(Convert.ToInt64
                 (GetLastError())))
      End If
   End If

   Return hProvider
End Function

Avoiding Pitfalls Associated with CryptAcquireContext

CryptAcquireContext returns a handle to a key container within a CSP. If the context for the key container that is asked for has never been acquired in the past, then the key container does not exist yet. In this case the key container must be created. AcquireDefaultContext and AcquireNamedContext both check for failure when calling CryptAcquireContext and try to create the key container if the first call fails. If the attempt to create the key container fails, then the methods throw an exception.

It is not a good practice to always attempt to create a new key container when calling CryptAcquireContext, because if the key container already exists, then the call to CryptAcquireContext will fail. Thus, it is very important to try to access the key container, assuming it does exist, and to try to create it only if necessary.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.151.107