When the folks at Microsoft decided how they were going to build this cryptographic functionality into the operating system, they chose to make it very flexible and expandable. They realized that different applications would need different types and strengths of encryption. They also knew that new types of encryption would be released on a regular basis.
The question was how to make the API able to continuously expand and adapt as the state of the art for encryption changes, while not requiring constant changing and updating of all the applications that use the API. Microsoft's solution to this question was to make the API a front end that applications could use and call, while the back end was an open API for any encryption vendor to plug in its own encryption engine, as illustrated in Figure 2.1.
These plug-in encryption engines are called Cryptographic Service Providers (CSPs). Windows is shipped with some default CSPs that serve most basic encryption needs. Even if your application needs require a CSP that is not shipped with Windows, you build your appli cation no differently than if you could use a default one.
Note
The construction of Cryptographic Service Providers (CSP) is not covered in this book. This book covers only how to build applications that use the API.
With this design, the CryptoAPI does little more than pass messages back and forth between the applications using it and the CSPs being used by those applications. Consequently, all the responsibility for performing the encryption and decryption is placed on the CSP, not your application. You therefore can change what type of encryption you are using as the need arises. It also places the need to employ those number-speaking mathematicians on the vendors creating the CSPs.
One of the first steps that you have to take in an application that uses the CryptoAPI is to open a context handle to the appropriate CSP. The CSP context is opened with the CryptAcquireContext function. The declaration for the CryptAcquireContext function is as follows:
Public Declare Function CryptAcquireContext Lib "advapi32.dll" _ Alias "CryptAcquireContextA" (phProv As Long, _ ByVal pszContainer As String, ByVal pszProvider As String, _ ByVal dwProvType As Long, ByVal dwFlags As Long) As Long
Note
Microsoft has not packaged the CryptoAPI in an ActiveX or COM control (despite a lot of documentation from Microsoft mentioning such a control). As a result, all the function declarations in this book are given in the form of their API declarations so that Visual Basic can use the function.
Tip
In this and other function declarations that have string parameters, you have the option of declaring the alias with a trailing "W" instead of the trailing "A" if you know that you will be running on NT or Windows 2000. This will call the UNICODE versions of these functions, allowing you to use UNICODE strings. The other option that you have available to you is to declare all "ByVal String" parameters as "ByRef Byte" parameters, and pass in the first position in a byte array that you have copied the string into. If you will be working only with character sets that fit within the ANSI character set, or will be needing your applications to run on Windows 95/98, then leave all of the function alias declarations as they are specified in this book, using the ANSI version of the functions.
The first parameter to this function (phProv) is a long variable into which the handle for the CSP context will be placed. This variable will be used throughout your application.
The second variable (pszContainer) is the name of the key container to be opened. If you are using the default Microsoft CSP and pass a string consisting only of the NULL string (vbNullString), the CSP will use the login name of the current user for the name of the key container.
Note
If your application stores private key information in the key store, it is recommended that you do not use the default key store, no matter which CSP you are using. The reason for this is that it would be very easy for any other application to write over or otherwise alter the key information in the default key store. By creating an application-specific key store, you can store keys in it without worrying about other applications modifying them.
The third variable (pszProvider) is the name of the CSP to be used. Constant definitions can be used for any of the Microsoft CSPs. These constants are listed in Table 2.1. If you pass the NULL string (vbNullString) as the provider name, the default provider for the specified CSP type (see the next parameter) will be used.
The fourth parameter (dwProvType) is the provider type. A series of constants is defined for use with this parameter, as listed in Table 2.2. This list of types will expand as new encryption methods and formulas are introduced. It is important to choose a provider type that is supported by the provider selected in the previous parameter.
The fifth and final parameter (dwFlags) is used to specify options on how the CSP context needs to be opened. These options can be used to create a new key container or to prevent the application from having access to private keys. The available values that can be used in this flag are listed in Table 2.3. More often than not, you'll want to pass 0 for this parameter. Passing 0 signals that you want to open an existing keyset.
The CryptAcquireContext function returns a C language Boolean, which you cast as a Visual Basic Boolean by using the CBool function. You can check the resulting Boolean value, as follows, to determine whether the function was successful:
Dim sContainer As String Dim sProvider As String Dim lHCryptProv as Long '--- Prepare string buffers sContainer = vbNullChar sProvider = MS_DEF_PROV & vbNullChar '--- Attempt to acquire a handle to the default key container. If CBool(CryptAcquireContext(lHCryptProv, vbNullString, sProvider, _ PROV_RSA_FULL, 0)) Then '--- We were successful, continue on...
After you have finished all the encryption that your application needs to do, you need to close the key container context by using the CryptReleaseContext function. This function is defined as follows:
Public Declare Function CryptReleaseContext Lib "advapi32.dll" ( _ ByVal hProv As Long, ByVal dwFlags As Long) As Long
Calling this function is simple because it has only one parameter of any importance. The first parameter (hProv) is the handle to the key context that you received in the CryptAcquireContext function (the first parameter in that function). This parameter tells the CryptReleaseContext function which key container context to close. The second parameter (dwFlags) is always zero (0). Therefore, you can call this function as you are closing your application, as in the following code:
Dim lResult As Long '--- Do we have an open key context? If so, release it. If (lHCryptProv <> 0) Then _ lResult = CryptReleaseContext(lHCryptProv, 0)
The CryptReleaseContext function does return a Boolean result, but the only reason it might fail is if you pass it an invalid key container handle (or pass it something other than 0 for the second parameter).
So what do you do if you need a particular CSP, and you don't know whether it's available on the user's computer? In this case, you can enumerate the CSPs on the system by using the CryptEnumProviders function. This function is defined as follows:
Public Declare Function CryptEnumProviders Lib "advapi32.dll" _ Alias "CryptEnumProvidersA" (ByVal dwIndex As Long, _ ByVal pdwReserved As Long, ByVal dwFlags As Long, _ pdwProvType As Long, ByVal pszProvName As String, _ pcbProvName As Long) As Long
The first parameter (dwIndex) to this function is the index number of the CSP to list. The index starts with zero (0) for the first CSP and increments from there. So, as you are looping through all the available CSPs, you need to have a counter variable that you can increment for each CSP you enumerate.
The second (pdwReserved) and third (dwFlags) parameters should always be zero (0). These parameters are "reserved for future use," so at some point, this use may change, but for now, just pass 0 for both of them.
The fourth parameter (pdwProvType) is a variable into which the provider type will be placed. For a list of the current provider types, see Table 2.2.
The fifth parameter (pszProvName) is a string variable into which the name of the provider will be placed. You can pass the NULL string (vbNullString) in this parameter to determine the length of the string needed to hold the provider name. The length of the provider name will be copied into the variable passed as the sixth parameter (pcbProvName). After you have sized the string for the fifth parameter, the sixth parameter should be populated with the length of the string being passed for the fifth parameter.
As with most CryptoAPI functions, this one returns a Boolean value that can be checked (after passing through the CBool function) to determine whether the function succeeded. You might use this function as follows:
Dim lResult As Long Dim lIndex As Long Dim sNameBuffer As String Dim lNameLength As Long Dim lProvType As Long lIndex = 0 '--- Determine the size of the buffer needed lResult = CryptEnumProviders(lIndex, 0, 0, lProvType, vbNullString, _ lNameLength) '--- We expect the preceding function call to fail, so we don't need to '--- check the return value '--- Prepare a string buffer for the CryptEncrypt function sNameBuffer = String(lNameLength, vbNullChar) '--- Get the provider name If CBool(CryptEnumProviders(lIndex, 0, 0, lProvType, sNameBuffer, _ lNameLength)) Then '--- Continue on
If you want to know what the available provider types are on a particular system, you can use the CryptEnumProviderTypes function. This function looks basically the same as the CryptEnumProviders function, as in the following definition:
Public Declare Function CryptEnumProviderTypes Lib "advapi32.dll" _ Alias "CryptEnumProviderTypesA" (ByVal dwIndex As Long, _ ByVal pdwReserved As Long, ByVal dwFlags As Long, _ pdwProvType As Long, ByVal pszTypeName As String, _ pcbTypeName As Long) As Long
The only difference between this function and the CryptEnumProviders function is that the name of the provider type is returned in the fifth parameter (pszTypeName). Otherwise, all the parameters are the same, and the two functions work the same.
When you want to determine which CSP is the default provider for a specific CSP type, you can use the CryptGetDefaultProvider function. This function is defined as follows:
Public Declare Function CryptGetDefaultProvider Lib "advapi32.dll" _ Alias "CryptGetDefaultProviderA" (ByVal dwProvType As Long, _ ByVal pdwReserved As Long, ByVal dwFlags As Long, _ ByVal pszProvName As String, pcbProvName As Long) As Long
The first parameter (dwProvType) for this function is the CSP type for which you want to know the default CSP. These types are listed in Table 2.2. The second parameter (pdwReserved) is "reserved for future use" and should always be zero (0).
The third parameter (dwFlags) specifies whether to look up the user or machine default CSP. The available values are listed in Table 2.4.
The fourth parameter (pszProvName) is a string into which the name of the default provider will be copied. The fifth parameter (pcbProvName) is the size of the string into which the name will be copied and the length of the CSP name after it is copied into the string.
As with most CryptoAPI functions, the CryptGetDefaultProvider function returns a Boolean value that you can check by using the CBool function.
To set the default CSP, you have two options, depending on which platform your application runs. The first option is the CryptSetProvider function, defined as follows:
Public Declare Function CryptSetProvider Lib "advapi32.dll" _ Alias "CryptSetProviderA" (ByVal pszProvName As String, _ ByVal dwProvType As Long) As Long
This function takes two parameters. The first parameter (pszProvName) is the name of the CSP to be the default for the CSP type. The second parameter (dwProvType) is the CSP type for which to make the CSP specified as the default.
Note
Remember that if the NULL string is passed as the CSP name to the CryptAcquireContext function to open a CSP and key container, the default CSP is used.
The second option is the CryptSetProviderEx function, which is defined as follows:
Public Declare Function CryptSetProviderEx Lib "advapi32.dll" _ Alias "CryptSetProviderExA" (ByVal pszProvName As String, _ ByVal dwProvType As Long, ByVal pdwReserved As Long, _ ByVal dwFlags As Long) As Long
In this version, the first two parameters are the same as with the CryptSetProvider version. The first (pszProvName) is the name of the CSP to be used, and the second (dwProvType) is the CSP type to make it the default. The third parameter (pdwReserved) is reserved and should always be zero (0).
The fourth parameter (dwFlags) is the real difference between these two versions. This parameter is used to specify whether you want to set the user or machine default CSP. You can also specify to delete (unset) the current default CSP for a particular type. The possible values for this parameter are listed in Table 2.5. You can use OR to combine these values to delete either the user or machine default.
As usual, you can check the Boolean return value to determine whether these functions were successful by using the CBool function.
The various CSP types implement certain encryption algorithms for performing their various tasks. If you need to use a specific algorithm, you need to check which CSP type supports it. The current CSP types and the algorithms they support are listed in Table 2.6.
Note
Not all the CSP types listed in Table 2.6 may be present, or have the same capabilities as listed, if you are working with an older version of the CryptoAPI. If you are working with the CryptoAPI 1.0 version, you might want to check the documentation to see which CSP types are supported, and what capabilities they have.