The Smart Card Cryptographic Service Provider Cookbook
Microsoft Corporation
October, 2002 - Original version
May, 2014 - Signing CSPs subtopic revised
Applies to:
Microsoft® Windows®
Cryptographic Service Providers
Smart cards
Summary: This article provides background information for Cryptographic Service Provider (CSP) developers. It brings together information already available on MSDN about smart cards, cryptography, and CSPs, then goes on to detail the calls that are made to the CSPs in typical scenarios, important design considerations, and smart card-specific error codes. (24 printed pages)
Contents
Introduction to Smart Cards and CSPs
Writing CSPs
Testing CSPs
Signing CSPs
More Information
Introduction to Smart Cards and CSPs
This section provides background information about smart cards, the smart card subsystem architecture and CSP types, and the differences between hardware and software CSPs.
Smart Card Basics
Smart cards usually come in two forms. The most common takes the form of a rectangular piece of plastic with an embedded microchip. The second is as a USB token. It contains a built in processor and has the ability to securely store and process information. A "contact" smart card communicates with a PC using a smart card reader whereas a "contactless" card sends encrypted information via radio waves to the PC. This document focuses on cards supported under the PC/SC model for Microsoft® Windows®, which is a contact-based system.
The PC/SC workgroup set a standard for integrating smart cards and smart card readers into the mainstream computing environment. They aim to promote a standard specification, to ensure that smart cards, smart card readers and computers made by different manufacturers will work together and help to facilitate the development of smart card applications for PC and other computing platforms.
A key benefit of smart cards is in public key cryptography where the private key and public key certificate of the user can be stored on the smart card. Operations that require the use of the private key can be performed on the smart card itself thus isolating any security-critical computations. The credentials on the smart card are portable so for example, an employee would be able to sign their e-mail at any company workstation that has a smart card reader installed and not be limited to their own. In addition, smart cards are tamper resistant with built-in physical defenses.
Smart cards can provide strong two-factor authentication based on knowledge of a personal identification number [PIN] code and possession of a smart card.
Typical scenarios in which smart cards are used include interactive logon, e-mail signing, e-mail decryption and remote access authentication. However, smart cards are programmable and can contain programs and data for many different applications. For example smart cards may be used to store medical histories for use in emergencies, to make electronic cash payments or to verify the identity of a customer to an e-retailer.
Several different manufacturers make smart cards and readers. The Windows Smart Card Resource Manager supports only PC/SC compatible readers. PC/SC is a standard model for interfacing smart card readers and cards with computers. A non-PC/SC reader can be used with Microsoft Windows but would require the provision of their own ways of accessing the reader and cards in the reader.
Microsoft provides two device independent APIs to insulate application developers from differences between current and future implementations: CryptoAPI and Microsoft Win32® SCard APIs.
The Cryptography API contains functions that allow applications to encrypt or digitally sign data in a flexible manner, while providing protection for the user's sensitive private key data. All cryptographic operations are performed by independent modules known as cryptographic service providers (CSPs).
The Microsoft Win32 APIs are base-level APIs for accessing smart cards with the most flexibility for the application to control readers, cards and other components.
Figure 1. Smart card subsystem architecture
The smart card subsystem architecture consists of service providers, a resource manager, device drivers and a reader driver library.
A Windows-based application can access card-based services directly via the SCard APIs in Win32. This means that they do not need a service provider. However, if they access crypto services they need a Cryptographic Service Provider (CSP) for the card. This article will go into more detail about CSPs, what they are, how they can be written and integrated with Microsoft products.
The resource manager is responsible for managing and controlling all application access to the smart card. It identifies and tracks resources, it controls the allocation of readers and resources across multiple applications and it supports transaction primitives for accessing available services on a specific card. Transaction control allows multiple commands to be executed without interruption so that the information on the card is always in a consistent state.
A device driver for a specific reader maps the reader's functionality to the native services provided by the Windows OS and the smart card infrastructure.
Cryptographic Service Providers
CSP types
There are many different cryptographic algorithms and even when implementing the same algorithm there are many choices to make about key sizes and padding for example. For this reason, CSPs are grouped into types, in which each supported CryptoAPI function, by default, performs in a way particular to that type. For example, CSPs in the PROV_DSS provider type support DSS Signatures and MD5 and SHA hashing. CSPs in the PROV_RSA_FULL provider type support RSA Key Exchange and Signatures, RC2 and RC4 Encryption and MD5 and SHA hashing. More information can be found on an MSDN site titled Cryptographic Provider Types.
Key differences between hardware and software CSPs
The CryptoAPI can be implemented fully in software or in hardware using smart cards. This section will cover the main differences between hardware/smart card CSPs and software CSPs.
Table 1 lists the differences in CryptoAPI functionality for smart card and software CSPs.
Table 1. CryptoAPI Functionality for Smart Card and Software CSPs
CryptoAPI | Flag | Smart Card CSP Functionality | Software CSP Functionality | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
CryptAcquireContext | CRYPT_SILENT | It is used to suppress any and all UI, including the PIN user interface. The PIN must be presented programmatically for private key operations. | It is used to suppress UI requesting user confirmation on a password for protected keys. | |||||||||||
CryptAcquireContext | CRYPT_MACHINE_KEY_SET | A smart card CSP must not cache any information that it receives via the handle it gets by using this function. | This function is used to create keys that can be shared by many users on the same machine. Keys created in this manner are not stored in the user profile. | |||||||||||
CryptAcquireContext | CRYPT_VERIFYCONTEXT | This function only allows a smart card CSP to access public data on the smart card. | It gives software CSPs the ability to perform any actions that do not require access to persisted private keys. Any keys generated during the session are only temporary. | |||||||||||
CryptExportKey and CryptImportKey | PRIVATEKEYBLOB and PLAINTEXTKEYBLOB | This is not supported because it is not possible to transmit the private key from of the smart card. | This is supported, provided the private key was created as "Exportable." |
Number of containers, keys, and certificates
All hardware and software CSPs allow only a single key of each type in any one container. The two predefined types of keys are Signature Only and Key Exchange. The only other common limitation is that if a key is added to a container but a key of the same type is already there, the new key will overwrite the old key.
One can only perform interactive smart card logon using the key exchange key that is in the default container. There are no APIs to set the default container.
If there are one or more containers present, exactly one must be marked default. If the only container on the card is deleted, a new default container must be created for the card to be usable.
CSPs used for interactive smart card logon generally make an additional default container decision "under the covers" in response to the API call CryptSetKeyParam KP_CERTIFICATE. If the certificate includes the "Smart card Logon" or "Enrollment Agent" Enhanced Key Usage OIDs, the container associated with that certificate should be marked as the new default. This behavior will allow the new certificate to be usable during logon.
Due to the current limitations on the storage capacity of smart cards, smart card CSPs can only create a limited number of containers and key pairs. Also, each key can only be associated with one certificate. Software CSPs do not have this limitation.
Error codes
The error codes returned by software CSPs are well defined, making software CSPs easy to test. Smart card CSPs have a number of different error codes that can be used when the same error occurs and this can make testing difficult. For more information on error codes see the section Testing CSPs.
Writing CSPs
The first step towards writing a CSP is determining which cryptographic algorithms and data format the CSP is going to implement. Having completed this, the CSP developer then needs to write a CSP DLL. Detailed descriptions on how to write a DLL can be found on MSDN About Dynamic-Link Libraries under Dynamic-Link Library Creation.
The CSP DLL must expose the Cryptographic System Program Interface (CryptoSPI) functions detailed in the section to follow on Entry Points. If a CSP does not actually support a particular function, a call to that function must at least return the E_NOTIMPL error code. A CSP that does not perform all of the cryptographic processes can be created if desired, but may not behave reliably in all scenarios.
Custom CSPs may involve more than one DLL; however, this is discouraged because it can create run-time complications. For more information, see CPAcquireContext.
It is important to provide a CSP Setup program to make CSP installation simple. Essentially, this Setup program needs to:
- Copy the CSP DLL to a directory pointed to by the PATH environment variable.
- Create the appropriate registry entries.
For more details on how to do this, please see the article Writing the CSP Setup Program
Entry Points
Every custom CSP DLL must expose all of the Cryptographic System Program Interface (CryptoSPI) functions listed here:
- CPAcquireContext
- CPCreateHash
- CPDecrypt
- CPDeriveKey
- CPDestroyHash
- CPDestroyKey
- CPEncrypt
- CPExportKey
- CPGenKey
- CPGenRandom
- CPGetHashParam
- CPGetKeyParam
- CPGetProvParam
- CPGetUserKey
- CPHashData
- CPHashSessionKey
- CPImportKey
- CPReleaseContext
- CPSetHashParam
- CPSetKeyParam
- CPSetProvParam
- CPSignHash
- CPVerifySignature
All PROV_RSA_SCHANNEL and PROV_DH_SCHANNEL CSPs must also support all of the following DLL entry points. (Note that these entry points are optional for other custom CSPs.)
These functions compose the CryptoSPI system program interface. Each function corresponds directly to a CryptoAPI cryptographic function.
Note All of these functions must be declared with the WINAPI keyword.
Design Considerations
PIN caching
When an application requires access to a user's private key or other personal credentials on a smart card, it first needs to authenticate the user. This is done via a PIN request. When the user has been authenticated, the program is then allowed access to the user's data.
A common mistake in CSPs is to require the PIN during CryptGetUserKey. CryptGetUserKey should succeed without the PIN. However, some operations (such as writing to the smart card and private key operations) using the handle will require the PIN later on and some won't (CryptGetKeyParam with KP_CERTIFICATE).
For example, a mail program such as Outlook can authenticate the user using a PIN request and then use the private key on the smart card to sign e-mail messages. However, there is a risk here that if the user remains authenticated on the card, then a rogue program may have access to the user's private key(s) and may use them without the user's knowledge.
To prevent this, the above procedure needs to occur within a transaction. Before the end of the transaction, the card must forget the pin and the user should no longer be authenticated on the card. However, it would be impractical to expect the user to re-enter their PIN every time they wished to sign e-mail with the same program and so the CSP developer needs to implement temporary PIN caching.
The PIN should be stored internally within the CSP per process. There should be a different cache for each card and the CSP should know at all times whether it has the PIN for a specific card or not. There are two ways for the CSP to receive a PIN; CryptSetProvParam PP_KEYEXCHANGE_PIN and PIN request UI. All PIN presentations should go via the cache to ensure that the cache is always synchronized with the current state and the cached PIN must be forgotten when the card is removed from the reader.
A cached PIN is always associated with the Logon ID of the last user who added the PIN to the cache. If a PIN is currently cached, the cache has been "initialized". If the cache is empty, the cache is "not initialized."
CryptSetProvParam (PP_EXCHANGE_PIN) should be used by applications that need to control exactly when UI is shown to the user. The application obtains the PIN using a customized method and uses CryptSetProvParam (PP_EXCHANGE_PIN) to present the PIN and authenticate the user to the card. The alternative is for the CSP is prompt the user with UI whenever it requires the PIN for a private key operations. The first scenario is illustrated in Figure 2 and the second scenario is illustrated in Figure 3.
Figure 2. PIN caching with CryptSetProvParam
Figure 3. PIN caching with UI
Flushing the PIN cache
The PIN cache should be flushed when the card is removed or when the use of the cached PIN indicates that it is stale (it may have been changed from another program). It should also be flushed on CryptSetProvParam for PP_xx_PIN when the PIN is NULL.
Adding a PIN to the cache
The PIN obtained from CryptSetProvParam (PP_EXCHANGE_PIN) or from the user through UI should be cached after its validity has been checked with the card (i.e. only cache good PINs). When CryptSetProvParam (PP_EXCHANGE_PIN) is processed by the CSP, a PIN may already be present in the cache. When a PIN is obtained from UI, the cache should be "not initialized", otherwise the CSP should have used the PIN in the cache.
When a PIN is requested from the user through UI, the user may change their PIN. If the PIN is changed the new PIN should be cached. Otherwise the old PIN should be cached.
Adding a PIN to the cache should have the following behavior:
When the cache is not initialized After successful verification of the PIN with the card, the appropriate PIN (see above) should be added to the cache with the Logon ID for the security context of the current thread.
When the cache has been initialized If the cached PIN matches the current PIN and if the cached Logon ID is different from the current Logon ID then the current Logon ID is cached in place of the cached Logon ID (i.e. the current user becomes the new owner of this PIN) and CryptSetProvParam should succeed. If the cached PIN is different from the current PIN to be cached CryptSetProvParam should return a failure (SCARD_W_WRONG_CHV).
Retrieving a PIN from the cache
When the CSP needs the PIN for an operation, if the cache is not initialized, the CSP should prompt the user for a PIN with the PIN request UI. However, if the context was acquired using the CRYPT_SILENT flag, it will not be possible to display UI and so SCARD_W_CARD_NOT_AUTHENTICATED should be returned.
If the cache is initialized, the CSP should check the Logon ID of the current security context. If it matches the Logon ID for the cached PIN, then the CSP can use the cached PIN. Otherwise, the CSP should return SCARD_W_CARD_NOT_AUTHENTICATED.
SCARDCONTEXT
The context is a communication channel with the smart card resource manager and all calls to the resource manager must go through this link.
All functions that take a context as a parameter or a card handle as parameter, which is indirectly associated with a particular context, may be blocking calls. Examples of these are SCardGetStatusChange and SCardBeginTransaction, which takes a card handle as a parameter.
If such a function blocks then all operations wanting to use the context are blocked as well. So, it is recommended that a CSP using monitoring establishes at least two contexts with the resource manager; one for monitoring (with SCardGetStatusChange) and one for other operations.
If multiple cards are present, it is recommended that a separate context or pair of contexts be established for each card to prevent operations on one card from blocking operations on another.
Example one
The example below shows what can happen if a CSP using SCardGetStatusChange for monitoring does not establish two contexts with the resource manager. The context becomes unusable until SCardGetStatusChange unblocks.
In this example, there is one process running called P1.
- P1 calls SCardEstablishContext, which returns the context hCtx.
- P1 calls SCardConnect (with the hCtx context) which returns a handle to the card, hCard.
- P1 calls SCardGetStatusChange (with the hCtx context) which blocks because there are no status changes to report.
- Until the thread running SCardGetStatusChange unblocks, another thread in P1 trying to perform an operation using the context hCtx (or the card hCard) will also be blocked.
Example two
The example below shows how transaction control ensures that operations meant to be performed without interruption can do so safely within a transaction.
In this example, there are two different processes running; P1 and P2.
- P1 calls SCardEstablishContext, which returns the context hCtx1.
- P2 calls SCardEstablishContext, which returns the context hCtx2.
- P1 calls SCardConnect (with the hCtx1 context) which returns a handle to the card, hCard1.
- P2 calls SCardConnect (with the hCtx2 context) which returns a handle to the same card, hCard2.
- P1 calls SCardBeginTransaction (with the hCard 1 context).
- Until P1 calls SCardEndTransaction (with the hCard1 context), any operation using hCard2 will be blocked.
- Once an operation using hCard2 is blocked and until it's returning, any operation using hCtx2 (and hCard2) will also be blocked.
Containers
When deleting a container, the CSP should prompt the user for the PIN irrespective of whether the container contains any private data.
If the card has just one container then it should be default container. If the card has more than one then the container associated with certificate containing the Login OID or EA OID should be the default container. If more than one container with the Login OID or EA OID certificates is present then the last one created should be the default container.
The container name length is restricted by the record entry length on the card. The CSP should support container names with a minimum length of 37 bytes.
There are no restrictions on the characters that can be used in container names. The CSP should use the UUIDCreate function and UUIDtoString function to create default container names.
Terminal server
When using a CSP on a local machine, if the smart card subsystem stops, all the contexts and card handles become invalid and you can no longer track the state of the cards i.e. whether they are present or absent in the reader. When a Terminal Server session is disconnected, it is as though the smart card subsystem (scardsvr) has stopped and when the session is reconnected, as if scardsvr has restarted. Therefore, when a session is disconnected, the CSP must assume that a scardsvr shutdown has occurred and that all the cards have been removed.
The CSP must also handle the stopping and restarting of the smart card subsystem. The ScardAccessStartedEvent call is used to detect when the smart card subsystem is restarting.
In a terminal server session, calls from the CSP on the remote machine to the local smart card reader need to be redirected, as shown in Figure 4, below. WinScard.dll deals with the redirection and since all smart card subsystem calls must go through WinScard.dll there is no need for the CSP to deal with this directly.
Figure 4. Redirection with Terminal Server
Hibernation
The CSP should support hibernation. All key handles and provider handles should be valid after hibernation.
Importing a PUBLICKEYBLOB
The public key should only be stored in the verify context mode and not persisted. This means that an import of the PUBLICKEYBLOB should not overwrite the PublicKey of the key pair stored in the card.
Export Restrictions
Download the Cryptographic Service Provider Developer's Kit (CSPDK). The CSPDK is eligible for export from the U.S. to all customers worldwide, except to U.S. embargoed destinations. Other countries may exercise separate jurisdiction over the import, export, or use of encryption products. Users who download this product should observe any local regulations that may apply to the distribution or use of encryption products.
The CSPDK cannot be used with Microsoft Windows 2000 SRP1 and SP3 or Microsoft Windows XP and later. For testing purposes, you will need to use a kernel debugger. This is described in Using a Kernel Debugger.
CSP Calls for Typical Scenarios
This section describes the function calls that CSPs have to deal with to behave correctly in the following common scenarios:
- Interactive Logon (Windows 2000 and Windows XP)
- Email Signing
- Client Authentication
The traces for Interactive Logon have been edited to show the function name, the function's purpose and the flags used. All other traces have been provided in their original form.
Interactive logon
Windows 2000 interactive logon
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_MACHINE_KEYSET and CRYPT_SILENT flags
- Returns handle to default container context (#C1)
CryptSetProvParam
- Present the PIN to the card (#C1)
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C1)
CryptGetKeyParam
- Called to retrieve the size of the certificate on the card (#C1)
- Returns the certificate size in bytes
CryptGetKeyParam
- Called with a buffer of sufficient size to read the certificate off the card
- Returns the certificate data in the data buffer (#C1)
CryptSetProvParam
- Present the PIN to the card (#C1)
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the size of the name of the current container (#C1)
- Returns the size of the name of the current container in bytes
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the name of the current container (#C1)
- Returns the name of the current container
CryptGetProvParam
- Called with the PP_NAME flag to retrieve the size of the name of the CSP (#C1)
- Returns the size of the name of the CSP in bytes
CryptGetProvParam
- Called with the PP_NAME flag to retrieve the name of the CSP (#C1)
- Returns the name of the CSP
CryptDestroyKey
- Release the handle to the key exchange pair in the default container (#C1)
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_MACHINE_KEYSET and CRYPT_SILENT flags
- Returns handle to default container context (#C2)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptGetProvParam
- Called with the PP_ENUMALGS parameter and the CRYPT_FIRST flag to get information about the first algorithm supported by the CSP (card) (#C2)
- Returns a PROV_ENUMALGS structure filled out with information about the first algorithm
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptGetProvParam
- Called with the PP_ENUMALGS parameter to get information about the next algorithm supported by the CSP (card) (#C2)
- Returns a PROV_ENUMALGS structure filled out with information about the next algorithm
Repeat 15, 16 until all algorithms enumerated
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#1)
CryptHashData
- Hash the data passed in (#C1)
CryptSignHash
- Sign the hash (#C1)
CryptDestroyHash
- Release the handle to the hash object (#C1) (#1)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#1)
CryptHashData
- Hash the data passed in (#C1) (#1)
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the size of the hash value (#C1) (#1)
- Returns the size of the hash value in bytes
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the hash value (#C1) (#1)
- Returns the hash value
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#2)
CryptSetHashParam
- Called with the HP_HASHVAL parameter and no data (#C1) (#2)
CryptSignHash
- Call SignHash to get the size of the signature (#C1) (#2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C1) (#2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C1) (#2)
CryptDestroyHash
- Release the handle to the hash object (#C1) (#1)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C1)
CryptImportKey
- Import a key (#C1)
CryptSetKeyParam
- Set the initialization vector to all zeroes (#C1)
CryptDestroyKey
- Release the handle to the key exchange pair in the default container (#C1)
CryptDecrypt
- Decrypt a chunk of data with the imported key (#C1)
CryptDestroyKey
- Release the handle to the imported key (#C1)
CryptCreateHash
- Get a handle to an MD5 hash object (#C2)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2)
CryptDestroyHash
- Release the handle to the hash object (#C2)
CryptCreateHash
- Get a handle to an MD5 hash object (#C2) (#1)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2) (#1)
CryptImportKey
- Import a key (#C2)
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the size of the hash value (#C2) (#1)
- Returns the size of the hash value in bytes
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the hash value (#C2) (#1)
- Returns the hash value
CryptCreateHash
- Get a handle to an MD5 hash object (#C2) (#2)
- Returns a handle to a hash object
CryptSetHashParam
- Called with the HP_HASHVAL parameter and no data (#C2) (#2)
CryptVerifySignature
- Verify the signature on hash object (#C2) (#2)
CryptDestroyKey
- Release the handle to the imported key (#C2)
CryptDestroyHash
- Release the handle to the hash object (#C2) (#2)
CryptGenRandom
- Generate 32 random bytes (#C2)
CryptCreateHash
- Get a handle to an SHA hash object (#C2)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2)
CryptSignHash
- Call SignHash to get the size of the signature (#C2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C2)
CryptCreateHash
- Get a handle to an SHA hash object (#C2)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2)
CryptSignHash
- Call SignHash to get the size of the signature (#C2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C2)
CryptReleaseContext
- Release the handle to the default container (#C2) (#2)
CryptReleaseContext
- Release the handle to the default container (#C1) (#1)
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_SILENT flag
- Returns handle to default container context (#C1)
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the size of the name of the current container (#C1)
- Returns the size of the name of the current container in bytes
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the name of the current container (#C1)
- Returns the name of the current container
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C1)
CryptGetKeyParam
- Called to retrieve the size of the certificate on the card (#C1)
- Returns the certificate size in bytes
CryptGetKeyParam
- Called with a buffer of sufficient size to read the certificate off the card (#C1)
- Returns the certificate data in the data buffer
CryptDestroyKey
- Release the handle to the key exchange pair in the default container (#C1)
CryptGetUserKey
- Get a handle to the signature key pair in the default container (#C1)
CryptReleaseContext
- Release the handle to the default container (#C1)
Windows XP interactive logon
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_MACHINE_KEYSET and CRYPT_SILENT flags
- Returns handle to default container context (#C1)
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C1)
CryptGetKeyParam
- Called to retrieve the size of the certificate on the card (#C1)
- Returns the certificate size in bytes
CryptGetKeyParam
- Called with a buffer of sufficient size to read the certificate off the card
- Returns the certificate data in the data buffer (#C1)
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the size of the name of the current container (#C1)
- Returns the size of the name of the current container in bytes
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the name of the current container (#C1)
- Returns the name of the current container
CryptGetProvParam
- Called with the PP_NAME flag to retrieve the size of the name of the CSP (#C1)
- Returns the size of the name of the CSP in bytes
CryptGetProvParam
- Called with the PP_NAME flag to retrieve the name of the CSP (#C1)
- Returns the name of the CSP
CryptDestroyKey
- Release the handle to the key exchange pair in the default container (#C1)
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_MACHINE_KEYSET and CRYPT_SILENT flags
- Returns handle to default container context (#C2)
CryptGetProvParam
- Called with the PP_ENUMALGS parameter and the CRYPT_FIRST flag to get information about the first algorithm supported by the CSP (card) (#C2)
- Returns a PROV_ENUMALGS structure filled out with information about the first algorithm
Repeat 11 until all algorithms enumerated
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#1)
CryptHashData
- Hash the data passed in (#C1)
CryptSignHash
- Sign the hash (#C1)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#1)
CryptHashData
- Hash the data passed in (#C1) (#1)
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the size of the hash value (#C1) (#1)
- Returns the size of the hash value in bytes
CryptGetHashParam
- Called with HP_HASHVAL to retrieve the hash value (#C1) (#1)
- Returns the hash value
CryptCreateHash
- Get a handle to an MD5 hash object (#C1)
- Returns a handle to a hash object (#2)
CryptSetHashParam
- Called with the HP_HASHVAL parameter and no data (#C1) (#2)
CryptSignHash
- Call SignHash to get the size of the signature (#C1) (#2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C1) (#2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C1) (#2)
CryptDestroyHash
- Release the handle to the hash object (#C1) (#1)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptSetProvParam
- Present the PIN to the card (#C2)
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C1)
CryptImportKey
- Import a key (#C1)
CryptSetKeyParam
- Set the initialization vector to all zeroes (#C1)
CryptDecrypt
- Decrypt a chunk of data with the imported key (#C1)
CryptDestroyKey
- Release the handle to the imported key (#C1)
CryptGenRandom
- Generate 32 random bytes (#C2)
CryptCreateHash
- Get a handle to an SHA hash object (#C2)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2)
CryptSetKeyParam
- Set the initialization vector to all zeroes (#C2)
CryptSignHash
- Call SignHash to get the size of the signature (#C2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C2)
CryptCreateHash
- Get a handle to an SHA hash object (#C2)
- Returns a handle to a hash object
CryptHashData
- Hash the data passed in (#C2)
CryptSetKeyParam
- Set the initialization vector to all zeroes (#C2)
CryptSignHash
- Call SignHash to get the size of the signature (#C2)
- Returns the size of the signature in bytes
CryptSignHash
- Sign the hash (#C2)
- Returns the signed hash
CryptDestroyHash
- Release the handle to the hash object (#C2)
CryptAcquireContext
- Acquire a handle to the default container on the card
- The calling process specifies the CRYPT_SILENT flag
- Returns handle to default container context (#C3)
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the size of the name of the current container (#C3)
- Returns the size of the name of the current container in bytes
CryptGetProvParam
- Called with the PP_CONTAINER flag to retrieve the name of the current container (#C3)
- Returns the name of the current container
CryptGetUserKey
- Get a handle to the key exchange key pair in the default container (#C3)
CryptGetKeyParam
- Called to retrieve the size of the certificate on the card (#C3)
- Returns the certificate size in bytes
CryptGetKeyParam
- Called with a buffer of sufficient size to read the certificate off the card (#C3)
- Returns the certificate data in the data buffer
CryptDestroyKey
- Release the handle to the key exchange pair in the default container (#C3)
CryptGetUserKey
- Get a handle to the signature key pair in the default container (#C3)
CryptReleaseContext
- Release the handle to the default container (#C3)
Notes
For local smart card logon with cached credentials, there is no Certificate Revocation List (CRL) check. The CRL is only checked with there is a Domain Controller (DC) certificate and in the cached logon case there is no DC involved and therefore no CRL check.
An example scenario where this would be relevant is as follows:
- You require smart card logon and issue smart cards.
- A user uses their smart card in a machine and successfully logs on to the machine and the network.
- User does not use laptop for a long time—maybe 30 days.
- User attempts to use smart card logon after this period to log on to the machine, but the machine has not been put back on the network.
In this situation, the certificate on the smart card will be accepted for local logon even if the CRL has expired because no CRL check is performed.
Email signing
Windows XP email signing
- CryptAcquireContext
- Acquire a handle to the default container on the card
- Returns handle to default container context
- CryptCreateHash
- Get a handle to an SHA hash object
- Returns a handle to a hash object
- CryptHashData
- Hash the data passed in
- CryptGetHashParam
- Called with HP_HASHVAL to retrieve the size of the hash value
- Returns the size of the hash value in bytes
- CryptGetHashParam
- Called with HP_HASHVAL to retrieve the hash value
- Returns the hash value
- CryptHashData
- Hash the data passed in
- CryptSignHash
- Call SignHash to get the size of the signature
- Returns the size of the signature in bytes
- CryptSignHash
- Sign the hash
- Returns the signed hash
- CryptDestroyHash
- Release the handle to the hash object
- CryptReleaseContext
- Release the handle to the default container
Client authentication
Windows XP client authentication
- CryptAcquireContext
- Acquire a handle to the default container on the card
- Returns handle to default container context
- CryptCreateHash
- Get a handle to an SHA hash object
- Returns a handle to a hash object
- CryptSetHashParam
- Called with the HP_HASHVAL parameter and no data
- CryptSignHash
- Call SignHash to get the size of the signature
- Returns the size of the signature in bytes
- CryptSignHash
- Sign the hash
- Returns the signed hash
- CryptDestroyHash
- Release the handle to the hash object
Testing CSPs
Before a CSP can be tested, it needs to satisfy a signature check. This can be done in two ways. The first is test signing (described below), and the second is to use a kernel debugger.
When debugging the CSP, the process that you need to debug depends on the version of Windows you are testing the CSP on. In Windows 2000, the CSP is loaded in the LSA process whereas in Windows XP and .Net the CSP is loaded in the WinLogon process.
More information about test cases and the testing environment for CSPs can be found in the CSPDK.
Test Signing
The following information on how to test sign a CSP has been quoted directly from MSDN® Test Signing the CSP
A CSP DLL must be signed each time it is built. This includes each build of the CSP that is made for testing purposes. Prior to Windows 2000, the signature is placed appropriately in the registry. Windows 2000 and later introduces signing a CSP digitally in a resource in the CSP DLL. This eliminates problems with signatures in the registry getting out of synchronization with the CSP binaries on the system.
Old signature checking in the registry is still supported in Windows 2000 and later so CSPs with signatures in that form continue to work. To support the same CSP binary on all recent Windows computers, some CSPs may need to be signed both in the resource and in the registry using the old signature form.
Windows 2000 and later continue to support CSPs verifying signatures on loaded DLLs using the FuncVerifyImage callback function from the VTableProvStuc structure passed to the CSP with CPAcquireContext. To use the callback in this way, the pointer to the signature, the second parameter, must be set to NULL.
To support the new signature format, the CSP must provide a 144-byte binary resource numbered 0x29A (decimal 666). The signature is placed in this resource.
A makefile can be used for the creation process of a CSP to incorporate this signing and registering procedure so that no steps are forgotten.
Sign utility
The Sign.exe utility signs CSP DLLs. Given a DLL file, the utility produces a signature file whose contents can be placed into the registry, as discussed above, and in Setting the User Default CSP. Sign.exe takes three arguments:
sign {s|v} <filename> <signature file>
The first argument must be s if a signature file is to be generated and v if an existing signature file is to be verified against the DLL file. The second argument must be the fully qualified file name of the DLL file. The third argument must be the fully qualified file name of the signature file.
For the CSP DLL file Myxcsp.dll, the following command generates a signature file. The signature file will be named Myxcsp.sig.
sign s myxcsp.dll myxcsp.sig
Using a Kernel Debugger
An alternative to test signing is testing with a kernel debugger running on the system, which makes it possible to run an unsigned CSP. The kernel debugger only needs to be running to allow the test CSP to work.
It is recommended that you use a remote kernel debugger. This means that you have two machines, a Test machine and a Debugger machine. The machines are connected using a 9-pin to 9-pin female debug cable. It is best to attach the debug cable using defaults since it introduces fewer problems, but you can attach the debug cable to any com port on either machine—just make sure you know what com port (Com1, Com2 and so on) you are attaching to. Using defaults, attach one end of the debug cable to Com2 (or highest numbered port) on the Test machine and the other end of the debug cable to Com1 on the Debugger machine.
The most recent version of the kernel debugging tools for windows can be downloaded from the Microsoft Debugging Tools Web site.
Tools have been built which make it is possible to use the standard Microsoft kernel debugger on a live system without requiring two computers; these are discussed in "Inside Windows 2000" published by Microsoft Press.
Error Codes
Error codes for each CryptoAPI can be found on MSDN under the reference pages for cryptographic functions. Table 2 shows a list of general smart card specific error codes. Table 3 shows a list of error codes related to the CryptAcquireContext function.
Table 2. General Error Codes
General Error Code | Condition |
---|---|
SCARD_W_CANCELLED_BY_USER | The user pressed "Cancel" on a Smart Card Selection Dialog. (GetOpenCardName returned SCARD_E_CANCELED.) |
SCARD_W_CANCELLED_BY_USER | The user pressed "Cancel" on a Smart Card PIN prompt Dialog. |
SCARD_E_NO_SUCH_CERTIFICATE | A Certificate for the key has not been written to the card. |
SCARD_E_CERTIFICATE_UNAVAILABLE | The Certificate for the key could not be read from the card. |
SCARD_E_INVALID_CHV | The supplied PIN is not in the proper format for the card. For example, it is the wrong length, or contains invalid characters. |
SCARD_W_WRONG_CHV | PIN validation failed. Either the user entered an incorrect PIN, or the PIN supplied through a SetProvParam was incorrect. |
SCARD_W_CHV_BLOCKED | PIN validation has been blocked by the card due to excessive invalid PIN entries. |
NTE_TOKEN_KEYSET_STORAGE_FULL | No room exists on the smart card for additional containers. This should not be used for anything except containers. |
NTE_SILENT_CONTEXT | Any failure when UI cannot be displayed due to silent context mode. |
NTE_BAD_FLAGS | CSP should check for invalid flags and invalid flag combinations for all API's. |
ERROR_INVALID_PARAMETER | CSP should also check for all invalid parameters like NULL pointers. |
NTE_BAD_TYPE | Any operation with unsupported keyblobs (like PrivateKeyBlob). |
NTE_BAD_KEY | CryptGetUserKey when called with invalid dwKeySpec parameter. Also returned, if the CSP does not support direct encryption with Exchange keys. |
Table 3. General Smart Card Error Codes
CryptAcquireContext Error Code | Condition |
---|---|
NTE_BAD_KEYSET | If the container is not present with a silent context. |
NTE_SILENT_CONTEXT | No card or some other card in the reader with a silent context. If this situation arises with a regular context, then prompt the user to insert the card. |
SCARD_W_CANCELLED_BY_USER | When creating a new container the CSP should prompt the user to enter a PIN. Action cancelled by user. |
SCARD_W_REMOVED_CARD | When creating a new container the CSP should prompt the user to enter a PIN. Card removed during operation. |
NTE_TOKEN_KEYSET_STORAGE_FULL | When using the default container, if the CSP fails due to storage limitations of the card. |
NTE_EXISTS | When using the default container, if the CSP does not support creating default container when it already has one. |
Signing CSPs
CSP Signing Process
Prior to May, 2013, it was necessary to follow a process wherein you submitted your CSP to Microsoft to sign on your behalf. However, beginning May, 2013 the process was revised. Authenticode Signing of Third-party CSPs describes the current process.
More Information
Export Restrictions
The Smart Card Deployment Cookbook
This "cookbook" is a set of "recipes" for deploying smart cards in an enterprise that is deploying Microsoft Windows 2000 Active Directory™. The white papers in this series will help you understand the principal smart card concepts and guide you through the planning tasks.
This is one of many forums available to developers to discuss technical issues. If you have technical questions about either CryptoAPI or CSPs, you should direct your questions here to the CryptoAPI discussion group.