Brought to you by the

Application Programmer's Guide

Microsoft CryptoAPI

Preliminary

Version 0.9
January 17, 1996

Microsoft Corporation


Note This documentation is an early release of the final product documentation and is only for limited distribution outside of Microsoft and not for redistribution. It is meant to accompany software still in development. Some of the information in the documentation may be inaccurate or may not be an accurate representation of the functionality in the final released product. Microsoft assumes no responsibility for any damages that might occur either directly or indirectly from these inaccuracies.

This is a preliminary document and may be changed substantially prior to final commercial release. This document is provided for informational purposes only and Microsoft Corporation makes no warranties, either express or implied, in this document. The entire risk of the use or the results of the use of this document remains with the user. Companies, names, and data used in examples herein are fictitious unless otherwise noted. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 1995 Microsoft Corporation. All rights reserved.

Microsoft, MS, MS-DOS, Visual Basic, Win32, and Windows are registered trademarks, and Visual C++ and Windows NT are trademarks of Microsoft Corporation in the U.S.A. and other countries.

TrueType is a registered trademark is a registered trademark of Apple Computer, Inc.

Unicode is a trademark of Unicode, Incorporated.

All other trademarks are the property of their respective owners.

Printed in the United States of America.

January 17, 1996

Contents

  • Chapter 0 Introduction 1Audience 1Document Overview 1Document Conventions 2Related Documentation 3Chapter 1 Overview of Cryptography 5Data Encryption 5Symmetric Versus Public-Key Encryption 6Symmetric Algorithms 6Public-Key Algorithms 6Digital Signatures 7Chapter 2 The CryptoAPI Programming Model 9System Architecture 9Cryptographic Service Providers (CSPs) 10Key Databases 11Session Keys 11Public/Private Key Pairs 12Using Cryptography in your Applications 12Chapter 3 Interfacing with a Cryptographic Service Provider (CSP) 13Different Types of CSPs 14Provider Types 14Predefined Provider Types 15PROV_RSA_FULL Provider Type 16PROV_RSA_SIG Provider Type 16PROV_DSS Provider Type 16PROV_FORTEZZA Provider Type 17PROV_MS_MAIL Provider Type 17PROV_SSL Provider Type 17The Microsoft RSA Base Provider 17Connecting to a Cryptographic Service Provider 17Chapter 4 Generating Cryptographic Keys 19Cryptographic Key Overview 19Using Keys With CryptoAPI 20Session Keys 20Public/Private Key Pairs 20Chapter 5 Exchanging Cryptographic Keys 23Storing Session Keys 23Alternatives to Storing Session Keys 24Exchanging Public Keys 25Certificates and Certification Authorities 25Exchanging Public Keys Manually 26Exchanging Session Keys 26Sending an Encrypted Session Key 27Key Blobs Explained 28Simple Key Blobs 29Public Key Blobs 29Sample Three-Phase Exchange Protocol 29Overview of the Sample Protocol 30Phase 1 31Phase 2 31Phase 3 32Protocol Conclusion 32Sender Code Example 32Receiver Code Example 36Chapter 6 Encrypting and Decrypting Data 41Introduction to Encryption Techniques 41Stream Ciphers 42Block Ciphers 42Padding 42Cipher Modes 43Initialization Vectors 45Salt Values 46Common Encryption Algorithms 47RC2 Block Cipher 47RC4 Stream Cipher 47RSA Public-Key Cipher 47Encrypting Files and Messages 48Encrypting Messages Using CryptoAPI 48Structure of an Encrypted File 49Encryption Example 49Decrypting Messages Using CryptoAPI 52Decryption Example 53Encrypting and Decrypting Simultaneously 55Chapter 7 Hashes and Digital Signatures 57How Digital Signatures Work 57Signing and Verifying Messages 58Signing Data 59Verifying Signatures 59Obtaining the Hash Value 59Hashing and Signature Algorithms 60MD2, MD4, and MD5 60Secure Hash Algorithm (SHA) 60Message Authentication Code (MAC) 60Chapter 8 System Administration 61Installing a New Provider 61Outline of CryptoAPI Registry Usage 61Chapter 9 Service Provider Functions 63CryptAcquireContext 63CryptGetProvParam 69CryptReleaseContext 75CryptSetProvider 76CryptSetProvParam 78Chapter 10 Key Generation and Exchange Functions 81CryptDeriveKey 81CryptDestroyKey 85CryptExportKey 86CryptGenKey 89CryptGenRandom 92CryptGetKeyParam 93CryptGetUserKey 97CryptImportKey 99CryptSetKeyParam 102Chapter 11 Data Encryption Functions 107CryptDecrypt 107CryptEncrypt 110Chapter 12 Hashing and Digital Signature Functions 113CryptCreateHash 113CryptDestroyHash 115CryptGetHashParam 116CryptHashData 120CryptHashSessionKey 121CryptSetHashParam 124CryptSignHash 126CryptVerifySignature 129Chapter 13 Data Types and Constants 133ALG_ID 133HCRYPTHASH 134HCRYPTKEY 134HCRYPTPROV 134MAXUIDLEN 135MS_DEF_PROV 135
  • CHAPTER 0

    Introduction

    The Microsoft Cryptographic API (CryptoAPI) provides services that enable application developers to add cryptography to their Win32 applications. Applications can use the functions in CryptoAPI without knowing anything about the underlying implementation, in much the same way that an application can use a graphics library without knowing anything about the particular graphics hardware configuration.

    Audience

    This document is intended to be used by developers familiar with the Microsoft Windows programming environment. Previous experience with cryptography or other security related subjects is helpful, but not absolutely necessary.

    Document Overview

    This guide presents general information about how to incorporate cryptography into applications and offers specific information about the function data types of the Microsoft Cryptographic API (CryptoAPI). Following the introduction, this guide is divided into the following sections:

    Chapter 1, Overview of Cryptography

    This explains the basics of cryptography and focuses on the techniques and functionality supported by CryptoAPI.

    Chapter 2, The CryptoAPI Programming Model

    This introduces each major functional area and provides general guidelines for writing a secure application.

    Chapter 3, Interfacing with a Cryptographic Service Provider (CSP)

    This explains how applications interface with a CSP. The capabilities of the Microsoft RSA Base Provider are also explained.

    Chapter 4, Generating Cryptographic Keys

    This explains how to create, configure, and destroy cryptographic keys.

    Chapter 5, Exchanging Cryptographic Keys

    This explains how to export and import cryptographic keys from the CSP. The authenticated key exchange protocol is also covered.

    Chapter 6, Encrypting and Decrypting Data

    This explains some of the more common encryption techniques and how to use CryptoAPI to perform encryption and decryption of data.

    Chapter 7, Hashes and Digital Signatures

    This explains how to use CryptoAPI to sign data and keys.

    Chapter 8, System Administration

    This outlines how different CSPs are installed onto the system.

    Chapter 9, Service Provider Functions

    This explains each of the functions used by applications to connect to a cryptographic service provider.

    Chapter 10, Key Generation and Exchange Functions

    This explains each of the functions used to generate and exchange cryptographic keys. The material in this section directly relates to the procedures discussed in Chapter 4 and Chapter 5.

    Chapter 11, Data Encryption Functions

    This explains each of the functions used to encrypt and decrypt data.

    Chapter 12, Hashing and Digital Signature Functions

    This explains each of the functions used to create digital signatures as well as the ones used to create message digests (hashes) from data.

    Chapter 13, Data Types and Constants

    This explains some of the data types that are unique to CryptoAPI.

    Document Conventions

    The type conventions used in this document are as follows:

    Bold               Bold letters or text indicate a         
                       specific term to use, as in functions   
                       (for example, CryptGenKey), and         
                       structure fields. Data types such as    
                       HCRYPTHASH are shown in both bold and   
                       uppercase. You must enter these terms   
                       exactly as shown.                       
    
    ALL CAPS           The names of constants such as          
                       CALG_RC2, CRYPT_EXPORTABLE, and NULL    
                       are given in all upper-case letters,    
                       not in boldface type. These terms must  
                       be entered exactly as shown.            
    
    Italic              In introductory and explanatory text,  
                       italicized words indicate that a key    
                       term or concept is being introduced.    
                       In describing functions and messages,   
                       words in italic indicate a place        
                       holder where you need to provide the    
                       actual value. For example, the          
                       following syntax for the                
                       CryptGenRandom function indicates that  
                       you must substitute values for the      
                       hProv, dwLen, and pbBuffer parameters:  
                                                               
                       CryptGenRandom(HCRYPTPROV hProv, DWORD  
                       dwLen, BYTE *pbBuffer)                  
    
    Monospaced text     Monospaced type is used to display     
                       code samples and data structure         
                       definitions.                            
    
    
    

    Related Documentation

    Additional documents that will help you understand cryptography and the associated security issues include:

    Bruce Schneier, Applied Cryptography, John Wiley & Sons, 1996.

    Microsoft Cryptographic Service Provider Programmer's Guide, Microsoft, 1995.

    D. R. Stinson, Cryptography: Theory and Practice, CRC Press, 1995.

    R. Anderson, "Why Cryptosystems Fail," Communications of the ACM, v. 37, n. 11, November 1994, pp. 32-40.

    RSA Laboratories, Public-Key Cryptography Standards, RSA Data Security, November 1993.

    Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Introduction to Algorithms, MIT Press, 1990.

    D.W. Davies and W.L. Price, Security for Computer Networks, John Wiley & Sons, 1989.

    Dorothy E. Denning, Cryptography and Data Security, Addison-Wesley, 1982.

    CHAPTER 1

    Overview of Cryptography

    Cryptography provides a set of techniques for encoding data and messages such that the data and messages can be stored and transmitted securely. This section introduces the basic terminology of cryptography and explains some of the common methods used.

    Cryptography can be used to achieve secure communications, even when the transmission media (for example, the Internet) is untrustworthy. You can also use cryptography to encrypt your sensitive files, so that an intruder cannot understand them.

    Cryptography can be used to ensure data integrity as well as maintain secrecy.

    Using cryptography, it becomes possible to verify the origin of data and messages. This is done using digital signatures, which are explained a little later in this chapter.

    When using cryptographic methods, the only part that must remain secret is the cryptographic keys. The algorithms, the key sizes, and file formats can be made public without compromising security.

    Data Encryption

    In using data encryption, a plaintext message can be encoded so it appears like random gibberish and is very difficult to transform back to the original message, without a secret key. In this document, the term message is used to refer to any piece of data. This message can consist of ASCII text, a database file, or any data you want to store or transmit securely. Plaintext is used to refer to data that has not been encrypted, while ciphertext refers to data that has.

    Once a message has been encrypted, it can be stored on nonsecure media or transmitted on an nonsecure network, and still remain secret. Later, the message can be decrypted into its original form. This process is shown in the following illustration:

    Encryption/Decryption Process

    When a message is encrypted, an encryption key is used. This is analogous to the physical key that is used to lock a padlock. To decrypt the message, the corresponding decryption key must be used. It is very important to properly restrict access to the decryption key, because anyone who possesses it will be able to decrypt all messages that were encrypted with the matching encryption key. Note that the encryption and decryption keys are often the same key.

    This may come as a surprise, but data encryption/decryption is pretty straight-forward. The really difficult part is keeping the keys safe and transmitting them securely to other users. This is discussed further in Chapter 5.

    Symmetric Versus Public-Key Encryption

    There are two main classes of encryption algorithms: symmetric algorithms and public-key algorithms (also known as asymmetric algorithms). Systems that use symmetric algorithms are sometimes referred to as conventional.

    Symmetric Algorithms

    Symmetric algorithms are the most common type of encryption algorithm. They are known as symmetric because the same key is used for both encryption and decryption. Unlike the keys used with public-key algorithms, symmetric keys are frequently changed. For this reason, they are referred to here as session keys.

    Compared to public-key algorithms, symmetric algorithms are very fast and, thus, are preferred when encrypting large amounts of data. Some of the more common symmetric algorithms are RC2, RC4, and the Data Encryption Standard (DES).

    Public-Key Algorithms

    Public-key (asymmetric) algorithms use two different keys: the public key and the private key. The private key is kept private to the owner of the key pair, and the public key can be distributed to anyone who requests it. If one key is used to encrypt a message, then the other key is required to decrypt the message.

    Public-key algorithms are very slow, on the order of 1000 times slower than symmetric algorithms. Consequently, they are normally used only to encrypt session keys. They are also used to digitally sign messages, as discussed in the next section.

    One of the most common public-key algorithms is the RSA Public-Key Cipher.

    Digital Signatures

    Digital signatures can be used when you have a message that you plan to distribute in plaintext form, and you want the recipients to be able to verify that the message comes from you and that it hasn't been tampered with since it left your hands. Signing a message does not alter the message, it simply generates a digital signature string you can bundle with the message or transmit separately.

    Digital signatures are generated using public-key signature algorithms. A private key is used to generate the signature, and the corresponding public key is used to validate the signature. This process is shown in the following illustration:

    Signature Generation/Validation Process

    On a network, there is often a trusted application running on a secure computer that is known as the Certification Authority. This application knows the public key of each user. Certification Authorities dispense messages known as certificates, each of which contains the public key of one of its client users. Each certificate is signed with the private key of the Certification Authority. A certificate containing the public key of the signer is often bundled with a signed message to make it easier to verify the signature. (Certificates are described in more detail in Chapter 5.)

    CHAPTER 2

    The CryptoAPI Programming Model

    The Microsoft Cryptographic Application Program Interface (CryptoAPI) is a set of 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). One CSP, the Microsoft RSA Base Provider, is bundled with the operating system.

    Each CSP provides a different implementation of the CryptoAPI. Some provide stronger cryptographic algorithms while others contain hardware components such as smartcards. In addition, some CSPs may occasionally communicate with users directly, such as when digital signatures are performed using the user's signature private key.

    The CryptoAPI programming model can be compared to the Windows GDI model in that the CSPs are analogous to graphics device drivers, and the cryptographic hardware (optional) is analogous to graphics hardware. Just as well-behaved applications are not allowed to communicate with graphics device drivers and hardware, well-behaved applications cannot directly access the CSPs and cryptographic hardware.

    System Architecture

    The Microsoft cryptographic system is composed of a number of different components, as shown in the following illustration. The three executable portions are the application itself, the operating system, and the CSP.

    Applications communicate with the operating system through a set of functions known as the cryptographic application program interface (CryptoAPI). The operating system, in turn, communicates with CSPs through a set of functions known as the cryptographic service provider interface (CryptoSPI).

    Cryptographic System Architecture

    Note that applications do not communicate with CSPs directly. Instead, all cryptographic function calls are routed through the operating system. A parameter in each CryptoAPI function indicates to the operating system which CSP to use to perform the actual cryptographic operation.

    Cryptographic Service Providers (CSPs)

    As mentioned above, CSPs are independent modules that perform the real cryptographic work. Ideally, they are written to be completely independent of any particular application, so that any given application will run with a variety of CSPs. In reality however, some applications may have very specific requirements that require a custom CSP.

    The physical manifestation of a CSP consists of, at a minimum, a dynamic-link library (DLL) and a signature file. The signature file is necessary to ensure that the operating system recognizes the CSP. The operating system validates this signature periodically to ensure that the CSP has not been tampered with.

    Some CSPs may implement a fraction of their functionality either in an address separated service called through local RPC, or in hardware called through a system device driver. Isolating global key state and central cryptographic operations in hardware or in a service keeps keys and operations safe from tampering within the application data space.

    Applications should not take advantage of attributes particular to a specific CSP. For example, the Microsoft RSA Base Provider currently uses 40-bit session keys and 512-bit public keys. When applications manipulate these, they should be careful not to make assumptions about the amount of memory needed to store them. Otherwise, the application is likely to fail when the user loads a different CSP onto the system. You should take care to write applications that are as well-behaved and flexible as possible.

    Key Databases

    Each CSP has a key database in which it stores its persistent cryptographic keys. Each key database contains one or more key containers, each of which contain all the key pairs belonging to a specific user (or CryptoAPI client). Each key container is given a unique name, which applications provide to the CryptAcquireContext function when acquiring a handle to the key container. Following is an illustration of the contents of a key database:

    Contents of a Key Database

    The CSP stores each key container from session to session, including all the public/private key pairs it contains. However, session keys are not preserved from session to session.

    Generally, a default key container is created for each user. This key container takes the user's logon name as its own name which is then used by any number of applications. It is also possible for an application to create its own key container (and key pairs) which it usually names after itself.

    Session Keys

    Session keys are used when encrypting and decrypting data. They are created by applications using either the CryptGenKey or the CryptDeriveKey function. These keys are kept internal to the CSP for safekeeping.

    Unlike the key pairs, session keys are volatile. Applications can save these keys for later use or transmission to other users by exporting them from the CSP into application space in the form of an encrypted "key blob" using the CryptExportKey function. (This procedure is discussed in Chapter 5.)

    Public/Private Key Pairs

    Each user generally has two public/private key pairs. One key pair is used to encrypt session keys and the other to create digital signatures. These are known as the key exchange key pair and the signature key pair, respectively. (These keys pairs are discussed later in detail.)

    Note that the while key containers created by most CSPs will contain two key pairs, this is not required. Some CSPs do not store any key pairs while others store additional ones.

    Using Cryptography in your Applications

    The Microsoft Cryptographic Application Program Interface (CryptoAPI) specifies functions in a number of different areas:

    Context Functions

    These functions are used by applications to connect to a CSP. These functions enable applications to choose a specific CSP by name, or get one with a needed class of functionality. (See Chapter 3 and Chapter 9.)

    Key Generation Functions

    These functions allow applications to generate and customize cryptographic keys. Full support is included for changing chaining modes, initialization vectors, and other encryption features. (See Chapter 4 and Chapter 10.)

    Key Exchange Functions

    These functions allow applications to exchange or transmit keys. These functions can also be used to implement fully authenticated three-leg key exchange. (See Chapter 5 and Chapter 10.)

    Data Encryption Functions

    These functions allow applications to encrypt or decrypt data. Support is also included for simultaneously encrypting and hashing data. (See Chapter 6 and Chapter 11.)

    Hashing and Signature Functions

    These functions allow applications to compute cryptographically secure digests of data and also enable digital signing of data. (See Chapter 7 and Chapter 12.)

    CHAPTER 3

    Interfacing with a Cryptographic Service Provider (CSP)

    The CSP architecture provides a safe way for multiple applications to access cryptographic and signature services. Instead of being passive sets of encryption routines, CSPs are independently functioning cryptographic modules capable of authenticating the user and checking for user assent to actions.

    For example, some CSPs will require a PIN to be entered before a digital signature is generated, while some require a smart card, and still others have no authentication at all. The quality of protection for keys within the system is a design parameter of the CSP itself and not the system as a whole. This lets the same applications run in a variety of security contexts without modification.

    The amount of access that applications have to the cryptographic internals has been carefully restricted. This was done to facilitate writing applications that are both secure and portable. The following three design rules apply:

    Applications cannot directly access keying material. Because all keying material is generated within the CSP and used by the application through opaque handles, there is no risk of an application or its associated DLLs either divulging keying material or choosing keying material from poor random sources.

    Applications cannot specify the details of cryptographic operations. The CSP interface only allows applications to specify broad actions to take (for example, encrypt data using algorithm X and sign data). The actual implementation of the cryptographic operations is the responsibility of the CSP. This limits the scope of the API because esoteric protocols require application intervention, but make a basic set of operations readily available to all applications.

    Applications do not handle user authentication data. User authentication is done by the CSP. In this way, CSPs that have better authentication capabilities (for example, biometric inputs and data keys) will function without needing to change the application's authentication model. It also prevents applications from divulging user secrets.

    Different Types of CSPs

    Each provider has both a name and a type. For example, the name of the CSP currently shipped with the operating system is "Microsoft Base Cryptographic Provider v1.0," and its type is PROV_RSA_FULL. The name of each provider is unique; the provider type is most definitely not.

    Provider Types

    The field of cryptography is very large. There are dozens of different "standard" data formats and protocols. These are generally organized into groups or "families," each of which has its own set of data formats and way of doing things. Even if they use the same algorithm (for example, the RC2 block cipher), two families will often use a different padding scheme, different key lengths, and different default modes. CryptoAPI has been designed so each CSP type represents a particular family.

    When an application connects to a CSP of a particular type, each of the CryptoAPI functions will, by default, operate in a way prescribed by the "family" that corresponds to the CSP type. Among other things, an application's choice of provider type specifies the following items:

    Key exchange algorithm - Each provider type specifies one and only one key exchange algorithm. Every CSP of a particular type must implement this algorithm. The only way applications can specify which key exchange algorithm is used is by selecting a CSP of the appropriate provider type.

    Digital signature algorithm - This is the same as with the key exchange algorithm. Each provider type specifies one and only one digital signature algorithm.

    Key blob format - When a public key or session key is exported out of a CSP, the format of the resulting "key blob" is specified by the provider type.

    Digital signature format - The provider type prescribes a particular digital signature format. This ensures that a signature produced by a CSP of a given provider type can be verified by any CSP of the same provider type.

    Session key derivation scheme - When a key is derived from a hash, the method used is specified by the provider type.

    Key length - Some provider types will specify that the public/private key pairs or the session keys be of a certain length.

    Default modes - The provider type will often specify a default mode for various options, such as the block encryption cipher mode or the block encryption padding method.

    Each application will generally work only with a single type of CSP. (However, an ambitious application can connect to more than one CSP at a time.) When writing an application, you will often need to obtain all the documentation that relates to the CSP type you are using. For example, it is not recommended that you try to write an application using the PROV_RSA_FULL provider type without obtaining the Public-Key Cryptographic Standards (PKCS) from RSA Data Security, Inc. The relevant third-party documentation for each provider type is listed later on in this section.

    Predefined Provider Types

    A number of provider types have already been defined. The following table lists these provider types, along with the algorithms that each type must support. A CSP of a given type is free to support other algorithms in addition to the ones listed.

    Provider Type   Key         Signature  Encryption Hashing      
                    Exchange                                       
    
                                                               
    
    PROV_RSA_FULL   RSA         RSA        RC2, RC4   MD5, SHA     
    
    PROV_RSA_SIG    n/a         RSA        n/a        MD5, SHA     
    
    PROV_DSS        n/a         DSS        n/a        SHA          
    
    PROV_FORTEZZA   KEA         DSS        Skipjack   SHA          
    
    PROV_MS_MAIL    RSA         RSA        CAST       MD5          
    
    PROV_SSL        RSA         RSA        varies     varies       
    
    
    

    If two or more applications plan to exchange keys and encrypted messages, they should both use CSPs of the same type, however, some CSP types may be partially compatible with others.

    Anyone writing a custom CSP can define a new provider type. However, this person is then responsible for distributing the new provider type to the authors of any applications that are to use it.

    In the event that the previous table mentioned algorithms you are not familiar with, the following table provides a brief description of each.

    Algorithm Description                                       
                                                                
    
                                                                
    
    CAST      This is a 64-bit symmetric block cipher           
              developed by C. M. Adams and S. E. Tavares. This  
              algorithm is somewhat similar to DES (Data        
              Encryption Standard).                             
    
    DES       National Institute of Standards and Technology    
              (NIST) Data Encryption Standard. This is a 64bit  
              symmetric block cipher that has a fixed key       
              length of 56-bits.                                
    
    DH        Diffie-Hellman. This is a public-key algorithm    
              used for secure key exchange. It cannot be used   
              for data encryption.                              
    
    DSS       Digital Signature Standard. This standard uses    
              the Digital Signature Algorithm (DSA), which is   
              a public-key cipher used to generate digital      
              signatures. It cannot be used for data            
              encryption.                                       
    
    KEA       Key Exchange Algorithm. This is an improved       
              version of Diffie-Hellman.                        
    
    MD2       MD2. This is a hashing algorithm that produces a  
              128bit hash value.                                
    
    MD4       MD4. This is a hashing algorithm that produces a  
              128bit hash value.                                
    
    MD5       MD5. This is an improved version of MD4. It also  
              produces a 128bit hash value.                     
    
    RC2       RC2 Block Cipher. This is a 64bit symmetric       
              block cipher.                                     
    
    RC4       RC4 Stream Cipher. This is a symmetric stream     
              cipher.                                           
    
    RSA       RSA Public-Key Cipher. This is a popular          
              public-key cipher used for both encryption and    
              signatures.                                       
    
    SHA       Secure Hash Algorithm. This is a hashing          
              algorithm that produces a 160bit hash value.      
    
    Skipjack  This is the algorithm used by the Clipper and     
              Capstone chips. It is a symmetric block cipher    
              with a fixed key length of 80 bits.               
    
    
    

    PROV_RSA_FULL Provider Type

    This provider type supports both digital signatures and data encryption, and considered general purpose. The RSA public-key algorithm is used for all public-key operations.

    This provider type has been thoroughly defined by Microsoft and RSA Data Security. It is described in the following documents:

    Microsoft Cryptographic Service Provider Programmer's Guide, Microsoft, 1995.

    RSA Laboratories, Public-Key Cryptography Standards, RSA Data Security, November 1993.

    PROV_RSA_SIG Provider Type

    This provider type is a subset of PROV_RSA_FULL. Only those functions and algorithms required for hashes and digital signatures are supported.

    PROV_DSS Provider Type

    This provider type is similar to PROV_RSA_SIG in that it only supports hashes and digital signatures, but the signature algorithm specified by the PROV_DSS provider type is the Digital Signature Algorithm (DSA).

    The DSA algorithm was proposed by the National Institute of Standards and Technology (NIST). A description of the algorithm can be found in many books on cryptography or from the following government reference:

    "Proposed Federal Information Processing Standard for Digital Signature Standard (DSS)," Federal Register, v. 57, no. 21, 31 Jan 1992, pp. 3747-3749.

    PROV_FORTEZZA Provider Type

    The set of cryptographic protocols and algorithms known as "Fortezza" is "owned" by the National Institute of Standards and Technology (NIST). More information is available directly from them or from other sources.

    PROV_MS_MAIL Provider Type

    CSPs of this type are designed to cater to the cryptographic needs of the Microsoft Mail application, as well as other applications that are compatible with MS Mail. This provider type is preliminary.

    PROV_SSL Provider Type

    CSPs of this type support the Secure Sockets Layer (SSL) protocol. A specification explaining the SSL protocol is available from Netscape Communications Corp.

    The Microsoft RSA Base Provider

    The Microsoft RSA Base Provider is supplied by Microsoft and is included with the operating system (either Windows NT or Windows 95).

    The Microsoft RSA Base Provider consists of a software implementation of the PROV_RSA_FULL provider type. The RSA public-key cipher is used for both key exchange and digital signatures, with a key length of 512 bits. The RC2 and RC4 encryption algorithms are implemented with a key length of 40 bits. The MD2, MD5, and SHA hashing algorithms are also provided.

    Connecting to a Cryptographic Service Provider

    Each time an application is run, the first CryptoAPI function an application calls is the CryptAcquireContext function. This function returns to the application a handle to a particular CSP. In addition, this handle specifies a particular key container within the CSP. If the CSP has just been installed and no key containers yet exist, the CryptAcquireContext function can also be used to create a new one.

    When an application uses CryptAcquireContext to obtain a CSP handle, it specifies a provider type and, optionally, a provider name. If both a type and a name are specified, then the function looks for a CSP with precisely the same type and name, loads it into memory, and returns a handle to the application.

    When an application calls CryptAcquireContext specifying a provider type but no provider name, the function tries to find the provider name, first on a list of default providers associated with the logged-on user and, if that fails, from a list of default providers associated with the computer.

    Once the provider name has been determined successfully, the CryptAcquireContext function searches for the CSP, loads it into memory, and returns a handle to the application.

    CHAPTER 4

    Generating Cryptographic Keys

    Keys lie near the center of almost every cryptographic operation. It is important to keep these secret because whoever possesses a given key then has access to any data that key is associated with. For example, if a key is used to encrypt a file, then anyone with a copy of that key can easily decrypt the file. Furthermore, if the key is used to sign messages, then anyone possessing that key can forge the signatures.

    Cryptographic Key Overview

    There are two types of cryptographic keys: session keys and public/private key pairs.

    Session Keys

    Session keys are primarily used for data encryption/decryption and are used with symmetric encryption algorithms. That is, the same key is used for both encryption and decryption.

    Most of the activity involving session keys relates to keeping them secret. It is important to keep the number of people who possess a particular session key as small as possible (one or two people is recommended).

    Public/Private Key Pairs

    Key pairs are composed of two components: the public key and the private key. The public key is distributed far and wide while the private key, on the other hand, is kept secret. Only the owner of the key pair is allowed to possess the private key.

    If one of the keys (the public key) is used to encrypt a message, then the other key is required to decrypt it. Thus, if you want to send someone a message, you can encrypt the file using their public key and be confident that no one else will be able to read the file.

    If the private key is used to sign a message, then the other key must be used to validate the signature. For example, if you want to send someone a digitally signed message, you would sign the message with your private key, and the other person could verify your signature using your public key.

    Unfortunately, public-key algorithms are incredibly slow and it is impractical to use them to encrypt large amounts of data. In practice, symmetric algorithms are used for encryption/decryption, while the public-key algorithms are used merely to encrypt the session keys. Similarly, it is not practical to use public-key signature algorithms to sign large messages. Instead, a hash is made of the message and the hash value is signed.

    Using Keys With CryptoAPI

    All keys are stored within cryptographic service providers (CSPs). CSPs are also responsible for creating the keys, destroying them, and using them to perform a variety of cryptographic operations. (Exporting keys out of the CSP so they can be sent to other users is discussed in Chapter 5.)

    Session Keys

    Applications can create any number of session keys, which can be used to encrypt messages. However, these keys are not preserved by the CSP from session to session. If you want to use a key for long periods, you need to export the key out of the CSP and into your application for long-term storage. (The procedure for doing this is discussed in Chapter 5.)

    Session keys are created using either the CryptGenKey or the CryptDeriveKey function. When these keys are generated, it is necessary to specify the algorithm to use for any subsequent encryption/decryption operations. This algorithm must be one of the symmetric algorithms supported by the CSP being used.

    Public/Private Key Pairs

    For each user, the CSP usually maintains two public/private key pairs. These keys are maintained from session to session.

    The key exchange key pair (also known as the exchange key) is used to encrypt session keys so that they can be safely stored and exchanged with other users. (This is discussed in detail in Chapter 5.)

    The digital signature key pair (also known as the signature key) is used to sign either hashes of data. This is discussed in detail in Chapter 7.)

    There are a number of reasons for having two separate key pairs. For example, some CSPs may opt to use one algorithm for key exchange and another for digital signatures. This is because some digital signature algorithms are unsuitable for encryption or key exchange, and vise versa. Also, if some data (for example, a session key) is both signed and encrypted with the same public key pair, subtle weaknesses could be introduced that make the data vulnerable.

    The exchange key and the signature key pairs are created by calling the CryptGenKey function and specifying either AT_KEYEXCHANGE or AT_SIGNATURE. The CSP implements these keys in an application-independent manner. Applications are not permitted to know the details about the algorithm used.

    CHAPTER 5

    Exchanging Cryptographic Keys

    This section discusses those situations when you must export keys from the secure environment of the cryptographic service provider (CSP) into your application's data space. Keys that have been exported are stored in encrypted data structures known as key blobs. These are discussed in the "Key Blobs Explained" section.

    There are two specific situations when it is necessary to export keys:

    You want to save a session key for later use by your application. For example, if your application has just encrypted a database file and you want your application to decrypt this file at a later time, your application is responsible for storing the encryption key. This is necessary because CSPs do not preserve symmetric keys from session to session.

    You want to send a key to someone else. This would be much easier (for your application) if the respective CSPs could communicate directly, but they cannot. This means the key has to be exported from your CSP, transmitted by your application to the destination application, and then imported into the destination CSP. If you don't trust the communication path, this can become somewhat complicated. However, this is covered in the next few sections.


    Note

    This section assumes that users (or CryptoAPI client) already possess their own set of public/private key pairs. Instructions for creating these can be found in Chapter 4.

    Storing Session Keys

    This section discusses how you use CryptoAPI to store a session key for later use. This is useful in those situations where you have encrypted a file using a key and want to decrypt the file at a later time. Another possible situation is one in which you have shared the session key with another user and you want to use the key at a later time to send the other user encrypted messages.

    In either case, your application will have to store the session key outside of the CSP for a certain period. Following is the procedure for storing a session key.

    1. Create a simple key blob using the CryptExportKey function. This will transfer the session key from the CSP to your application's memory space. Specify that your own key exchange public key be used to encrypt the key blob.

    2. Store the signed key blob to disk. The assumption is made here that all disks are nonsecure.

    3. Later, when you need to use the key, read the key blob from disk.

    4. Import the key blob back into the CSP using the CryptImportKey function.

    If the session key is just to be bundled with an encrypted file (so that you can later decrypt the file), and the key is not going to be used to encrypt any more data, then the above procedure provides adequate security.

    If you plan to use the session key for encryption at a later time, then the key blob should be signed with your key exchange key before the key is stored to disk. When you later read the key blob back from disk, you should validate the signature to make sure the key blob is intact. If these steps are omitted, then someone with access to your storage media can create their own session key, encrypt it with your key exchange public key, and substitute it for your key blob. You could then unknowingly use their session key to encrypt files and messages, which the unscrupulous user could then easily decrypt. (Digital signatures are discussed in detail in Chapter 7.)

    Alternatives to Storing Session Keys

    Instead of storing a random session key blob, a derived key can be used. Derived session keys are created from a password using the CryptDeriveKey function. In this way, instead of storing a particular derived key, an application can create a derived key as needed by prompting the user for the password.

    Stored key blobs are dependent on the stability of the public/private key pairs stored within the CSP. If these key pairs are somehow lost, (for example, through a hardware or software incident), you will be unable to decrypt your key blobs. This means that any data that has been encrypted using these keys will also be lost. For this reason, it is recommended that you use a backup authority when storing long-term archival data.

    A backup authority is a trusted application running on a secure computer which provides storage for the session keys of its clients. All session keys stored there are encrypted in the form of key blobs with the backup authority's public key. An application using a backup authority typically follows these steps:

    1. Encrypt the file normally.

    2. Export the session key used to encrypt the file into a simple key blob, specifying that your own key exchange public key be used to encrypt the key blob. Store this key blob with the encrypted file.

    3. Export the session key again, this time specifying that the backup authority's public key be used to encrypt the key blob. Send this key blob to the backup authority, along with the key's description, serial number, etc.

    If, at a later time, you lose your key pairs, you can retrieve the session keys from the backup authority. (You will first have to establish your identity to the backup authority, but this procedure falls outside the scope of CryptoAPI.)

    Exchanging Public Keys

    Exchanging public keys is the first step that two users contemplating encrypted communication need to do. Once this has been done, the users can send encrypted and signed data to each other in a straightforward manner.

    There are two fundamental ways to obtain each other's public keys:

    The users can obtain each other's keys in the form of certificates, from a certification authority. This is the most secure way to exchange public keys which does not require user interaction.

    The users can read their public keys to each other over the phone, use certified mail to send them to each other, or use another method that is reasonably tamperproof. Note that your public key is not secret, so that it doesn't matter if it is overheard by a third party.

    This method can also be used to validate the public key values that have been exchanged in some other manner.

    Certificates and Certification Authorities

    A certificate is a packet of data that contains a user's public key in addition to the data that serves to identify the user in the real world (for example, the user's name). Every certificate is created and signed by a trusted application known as a certification authority.

    Because certificates are signed, they can be transmitted over a nonsecure network or stored on nonsecure media. Each time you receive a certificate, you should verify the signature using the certification authority's public key.

    Certain details relating to certificates are beyond the scope of CryptoAPI. These include:

    The actual binary format of certificates is not specified by CryptoAPI, although ISO X.509 is recommended.

    The manner in which each client communicates his or her name and public keys to the certification authority is not specified.

    The manner in which the certification authority's public key is distributed is not specified.


    Warning

    If you use the Microsoft RSA Base Provider to create a certification authority, your license to issue certificates is limited to certificates intended for use in the context of your particular application or service.

    Exchanging Public Keys Manually

    If a certification authority is not available, or if one or more of the users has not registered their public keys with it, then the users need to exchange their public keys in some other manner. This can also be done if the certification authority is not considered trustworthy by one or more of the users.

    When transferring keys or messages from one user to another, one of the users is designated the sending user (or sender) and the other the destination user (or receiver).

    The first step is for the sender to export his public key from the CSP into a public key blob, using the CryptExportKey function. Next, the key blob must be sent to the destination user in some secure manner. Although secrecy is not necessary, both users must be confident that the integrity of the key blob remains untarnished during the transfer. (The mechanics of how this is done are completely independent of the CryptoAPI.)

    Public key blobs are not encrypted. Thus, it would not be difficult for the sending application to convert the key blob to a human-readable format, so that the sender could read the public key to the receiver over the phone. Furthermore, it would not be difficult for the receiving application to reconstruct the public key blob.

    Once the receiver has received the key blob data from the sender, it imports the key blob into its own CSP. This is done using the CryptImportKey function.

    Exchanging Session Keys

    To send another user an encrypted message, it becomes necessary to send that user the session key that was used to perform the encryption. There are two ways this can be approached:

    The sending user can create a random session key, encrypt it using the receiver's public key, and send the encrypted key (key blob) to the receiver. The sender can then send messages encrypted with this session key to the receiver. This approach is discussed in the following section.

    The sending and receiving users can mutually agree on a session key by exchanging several messages back and forth. The users can then use this session key to send encrypted messages back and forth. The "Sample Three-Phase Exchange Protocol" section describes a sample three-phase key exchange protocol that can be used for this purpose. Designing one of these protocols (and getting it right!) is fairly difficult and should only be attempted by an experienced cryptographer.


    Note

    This section assumes that the users (or CryptoAPI clients) already possess their own set of public/private key pairs and have also obtained each other's public keys.

    Sending an Encrypted Session Key

    The easiest way to send encrypted messages to another user is to send the message (encrypted with a random session key) along with the session key (encrypted with the receiver's exchange public key). These are the steps for sending an encrypted session key:

    1. Create a random session key using the CryptGenKey function.

    2. Encrypt the message using the session key. (This procedure is discussed in the section "Encrypting and Decrypting Data.")

    3. Export the session key into a key blob with the CryptExportKey function, specifying that the key be encrypted with the destination user's key exchange public key.

    4. Send both the encrypted message and the encrypted key blob to the destination user.

    5. The destination user should then import the key blob into his or her CSP using the CryptImportKey function. This will automatically decrypt the session key, provided the destination user's key exchange public key was specified in step 3.

    6. The destination user can then decrypt the message using the session key, following the procedure discussed in Chapter 6.

    The following illustration shows how to send an encrypted message using this procedure:

    Sending an Encrypted Message with Key Blob

    This approach is vulnerable to at least one common form of attack. An eavesdropper can acquire copies of one of more encrypted messages and the encrypted keys. Then, at some later time, the eavesdropper can send one of these messages to the receiver and the receiver will have no way of knowing the message did not come directly from the original sender. This risk can be reduced by timestamping all messages or by using serial numbers. Using a three-phase key exchange protocol will eliminate this problem entirely. See the "Sample Three-Phase Exchange Protocol" section.

    Key Blobs Explained

    Key blobs provide a way to store keys outside of the CSP. Chapter 4 stated that keys are always kept inside of the provider for safekeeping and applications are only allowed access to the key through a handle. Well, key blobs are the one exception to this rule.

    Key blobs are created by exporting an existing key out of the provider, using the CryptExportKey function. Later, the key blob can be imported into a provider (often a different provider on a different computer), using the CryptImportKey function. This will create a key in the provider that is a duplicate of the one that was exported. In this way, key blobs are used as the medium for securely transferring keys from one provider to another.


    Note

    Private keys can be neither exported nor imported - they never leave the safety of the CSP module. When the handle to a public/private key pair is passed into CryptExportKey, only the public portion is placed into the key blob.

    Key blobs consist of a standard header followed by data that represents the key itself. If the key blob contains a session key, then this data is always kept encrypted. Applications generally do not access the internals of key blobs but, instead, treat them as opaque objects. This opaque quality was the inspiration for the name of "key blob."

    Key blobs are personalized in that they are encrypted with the key exchange public key of the intended recipient. This makes them fairly secure. To make them tamperproof, keys are sometimes signed with the key exchange private key of the originating user.

    There are currently two types of key blobs:

    Simple key blobs

    Public key blobs

    Simple Key Blobs

    A simple key blob (SIMPLEBLOB) is a session key encrypted with the public key exchange key of the destination user. This key blob type is used when storing a session key or transmitting a session key to another user.

    Public Key Blobs

    A public key blob (PUBLICKEYBLOB) contains the public key portion of a public/private key pair. Unlike simple key blobs, these are not encrypted.

    When communicating with someone who is not using CryptoAPI, your application may need to build one of these key blobs manually so the other user's public keys can be imported into your CSP. In addition, it is not unheardof for an application to display the value of a public key in plaintext form so the user can validate it by hand. (The format of public key blobs is fully documented in the Microsoft Cryptographic Service Provider Programmer's Guide.)

    Sample Three-Phase Exchange Protocol

    To generate an authenticated and encrypted connection between two parties on a nonsecure network, a set of messages can be exchanged which negotiate an encryption key between them. Both parties contribute a portion of the data that the negotiated session key is derived from. This protocol ensures that both the parties are currently active and are sending messages directly to each other.


    Note

    This section assumes that both parties involved already possess their own set of public/private key pairs and that they have also obtained each other's public keys.

    It is further assumed that the parties have already exchanged human-readable user names. This is generally done at the same time the public keys are exchanged, since the user name is included as part of each certificate. When necessary, the public key data can be used as the user name, although this is not recommended. All that really matters, though, is that each party's user name be tightly bound to their public key and that both parties agree on what their respective user names are.

    Overview of the Sample Protocol

    This protocol provides a standard way for two parties to create an authenticated, real-time connection between themselves. The end result of this protocol is a session key that is shared by both of the parties involved. This protocol is known as a three-phase protocol because it requires that the two parties exchange three packets of data in the process of creating the shared session key. This is shown in the following illustration, reading from top to bottom.

    Sample Three-phase Key Exchange Protocol

    A variety of key exchange protocols can be implemented using CryptoAPI. The protocol discussed here is just one of many possibilities. However, using this particular protocol will tend to increase your application's potential interoperability.

    Following is a description of this protocol. One of the parties is arbitrarily designated the sending user (or sender) and the other the destination user (or receiver).

    Phase 1

    In Phase 1, the sender creates a random session key, to be known as "session key A", using the CryptGenKey function. The sender then uses CryptExportKey to export this key into a simple key blob, specifying that the receiver's exchange public key be used to encrypt the key blob. This key blob is then sent to the receiver.

    The receiver accepts the key blob from the sender and imports it into its CSP, using the CryptImportKey function. This function returns a handle to session key A to the receiver.

    Phase 2

    In Phase 2, the receiver then creates a random session key of its own, to be known as "session key B." The receiver exports this key into a key blob and transmits it to the sender.

    The receiver then builds up a hash value containing session key A, the receiver's name, session key B, the sender's name, and the text "phase 2." This hash value is then sent to the sending user. (The details of this process are discussed in this chapter under "Receiver Code Example.")

    The data must be hashed in the standard sequence, so the sender will be able to properly validate it. The data formats used by the sender and receiver must also match, although a standard format is not specified here.

    The sending user accepts the "session key B" key blob from the receiver, and imports it into its CSP. The hash value is also received.

    The sending user then validates the receiver's hash value by creating a hash of its own containing the same data, and comparing the two hash values. If the hash values do not match, then either the destination user has not been forthright, or someone else is tampering with the data between the two parties. In either case, the protocol should be terminated and the communication link severed.

    If the two hash value do match, this tells the sender that the destination user is presently online and in real-time communication. This is primarily because the hash value contains session key A, which was sent out encrypted with the destination user's public key. Only the real destination user could have decrypted the session key and built the hash value. Including the human-readable user names in the hash makes it possible to involve the users in the process as an additional check.

    Phase 3

    In Phase 3, the sender builds up a hash value containing session key B, the sender's name, the receiver's name, and the text "phase 3." This hash value is then sent to the destination user. (The details of this process are discussed in this chapter under "Sender Code Example.")

    The destination user accepts the hash value from the sender and validates it by creating a hash of its own and comparing the two hash values. If the hash values do not match, then the protocol should be terminated and the communication link severed.

    If the two hash value do match, this tells the receiver that the sending user is presently online and in real-time communication. This is primarily because the hash value contains session key B, which was sent out encrypted with the sending user's public key. Only the real sending user could have decrypted the session key and built the hash value.

    Protocol Conclusion

    Once the two parties have exchanged session keys and hash values and the hash values have been properly validated, the protocol is complete. Each party can now independently create a shared session key that can be used to send encrypted messages to each other.

    To create the shared session key, each party must create a hash object with CryptCreateHash, add session key A and session key B to the hash with CryptHashSessionKey, and then derive the key using CryptDeriveKey. This procedure is shown in detail in the code examples.

    Sender Code Example

    This section shows the code needed on the sending user side to implement the three-phase key exchange protocol. The details of the communication between the sending user and the destination user are not shown, because these will be different for each implementation.

    For purposes of readability, this example and the following one blatantly avoid good programming practice in two major ways:

    No error checking is shown. A working program should always check the returned error codes and perform some appropriate action when an error is encountered.

    Fixed-length buffers are used to store key blobs and hash values. In practice, these buffers should be allocated dynamically, because this data will vary in size depending on the CSP used.

    #include <wincrypt.h>
    HCRYPTPROV hProv = 0;
    #define NAME_SIZE 256
    BYTE pbDestName[NAME_SIZE];
    DWORD dwDestNameLen;
    BYTE pbSendName[NAME_SIZE];
    DWORD dwSendNameLen;
    HCRYPTKEY hDestPubKey = 0;
    HCRYPTKEY hSharedKey = 0;
    HCRYPTKEY hKeyA = 0;
    HCRYPTKEY hKeyB = 0;
    #define BLOB_SIZE 256
    BYTE pbKeyBlob[BLOB_SIZE];
    DWORD dwBlobLen;
    #define HASH_SIZE 256
    BYTE pbHash[HASH_SIZE];
    DWORD dwHashLen;
    BYTE pbDestHash[HASH_SIZE];
    DWORD dwDestHashLen;
    HCRYPTHASH hHash = 0;
    // Get handle to the default provider.
    CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);
    // Obtain the destination user's exchange public key. Import it into
    // the CSP and place a handle to it in 'hDestPubKey'.
    ...
    CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hDestPubKey);
    // Obtain the destination user's name. This is usually done at the
    // same time as the public key was obtained. Place this in 
    // 'pbDestName' and set 'dwDestNameLen' to the number of bytes in 
    // the name.
    ...
    // Place the sending user's name in 'pbSendName' and set 
    // 'dwSendNameLen' to the number of bytes in it.
    ...
    // Create a random session key (session key A). Because this key will
    // be used solely for key exchange and not encryption, it 
    // does not matter which algorithm you specify here.
    CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hKeyA);
    // Export session key A into a simple key blob.
    dwBlobLen = BLOB_SIZE;
    CryptExportKey(hKeyA, hDestPubKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen);
    // Send key blob containing session key A to the destination user.
    ...
    // Wait for the destination user to respond.
    ...
    // Receive a key blob containing session key B from the destination 
    // user and place it in 'pbKeyBlob'. Set 'dwBlobLen' to the number 
    // of bytes in the key blob.
    ...
    // Receive a hash value from the destination user and place it in
    // 'pbHashValue'. Set 'dwHashLen' to the number of bytes in the hash 
    // value.
    ...
    // Import the key blob into the CSP.
    CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hKeyB);
    //
    // Verify hash value received from the destination user.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key A to hash.
    CryptHashSessionKey(hHash, hKeyA, 0);
    // Add destination user's name to hash.
    CryptHashData(hHash, pbDestName, dwDestNameLen, 0);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Add sending user's name to hash.
    CryptHashData(hHash, pbSendName, dwSendNameLen, 0);
    // Add "phase 2" text to hash.
    CryptHashData(hHash, "phase 3", 7, 0);
    // Complete the hash computation and retrieve the hash value.
    dwHashLen = HASH_SIZE;
    CryptGetHashParam(hHash, HP_HASHVALUE, pbHash, &dwHashLen, 0);
    // Destroy the hash object.
    CryptDestroyHash(hHash);
    //
    // Compare the hash value received from the destination user with 
    // the hash value that we just computed. If they do not match, then 
    // terminate the protocol.
    //
    if(dwHashLen!=dwDestHashLen || memcmp(pbHash, pbDestHash, dwHashLen)) {
        printf("Key exchange protocol failed in phase 2!\n");
        printf("Aborting protocol!\n");
        return;
    }
    //
    // Compute hash to be sent to the destination user.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Add sending user's name to hash.
    CryptHashData(hHash, pbSendName, dwSendNameLen, 0);
    // Add destination user's name to hash.
    CryptHashData(hHash, pbDestName, dwDestNameLen, 0);
    // Add "phase 3" text to hash.
    CryptHashData(hHash, "phase 3", 7, 0);
    // Complete the hash computation and retrieve the hash value.
    dwHashLen = HASH_SIZE;
    CryptGetHashParam(hHash, HP_HASHVALUE, pbHash, &dwHashLen, 0);
    // Destroy the hash object.
    CryptDestroyHash(hHash);
    // Send the hash value to the destination user.
    ...
    //
    // Create a shared session key to be used by both users for
    // exchanging encrypted messages. Both users must agree on the 
    // algorithm and parameters that this key is to use.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key A to hash.
    CryptHashSessionKey(hHash, hKeyA, 0);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Complete the hash computation and derive a session key from it. 
    // The CRYPT_EXPORTABLE flag is not specified here because the key 
    // usually is not exported out of the CSP.
    CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hSharedKey);
    // Destroy the hash object.
    CryptDestroyHash(hHash);
    //
    // Use the shared key to send encrypted messages to the other user.
    //
    ...
    // Destroy session key.
    CryptDestroyKey(hSharedKey);
    // Release provider handle.
    CryptReleaseContext(hProv, 0);
     
    

    Receiver Code Example

    This section illustrates the code needed on the destination user side to implement the three-phase key exchange protocol. The details of the communication between sending user and the destination user are not shown, because these will be different for each implementation.

    #include <wincrypt.h>
    HCRYPTPROV hProv = 0;
    #define NAME_SIZE 256
    BYTE pbDestName[NAME_SIZE];
    DWORD dwDestNameLen;
    BYTE pbSendName[NAME_SIZE];
    DWORD dwSendNameLen;
    HCRYPTKEY hSendPubKey = 0;
    HCRYPTKEY hSharedKey = 0;
    HCRYPTKEY hKeyA = 0;
    HCRYPTKEY hKeyB = 0;
    #define BLOB_SIZE 256
    BYTE pbKeyBlob[BLOB_SIZE];
    DWORD dwBlobLen;
    #define HASH_SIZE 256
    BYTE pbHash[HASH_SIZE];
    DWORD dwHashLen;
    BYTE pbSendHash[HASH_SIZE];
    DWORD dwSendHashLen;
    HCRYPTHASH hHash = 0;
    // Get handle to the default provider.
    CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0);
    // Obtain the sending user's exchange public key. Import it into the
    // CSP and place a handle to it in 'hSendPubKey'.
    ...
    // Obtain the sending user's name. This is usually done at the 
    // same time the public key was obtained. Place this in 
    // 'pbSendName' and set 'dwSendNameLen' to the number of bytes in 
    // the name.
    ...
    // Place the destination user's name in 'pbDestName' and set
    // 'dwDestNameLen' to the number of bytes in the name.
    ...
    // Receive a key blob containing session key A from the sending user
    // and place it in 'pbKeyBlob'. Set 'dwBlobLen' to the number of 
    // bytes in the key blob.
    ...
    // Import the key blob into the CSP.
    CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hKeyA);
    // Create a random session key (session key B). Because this key is
    // going to be used solely for key exchange and not encryption, it 
    // does not matter which algorithm you specify here.
    CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hKeyB);
    // Export session key B into a simple key blob.
    dwBlobLen = BLOB_SIZE;
    CryptExportKey(hKeyB, hSendPubKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen);
    // Transmit key blob containing session key B to the sending user.
    ...
    //
    // Compute hash value and transmit it to the sending user.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key A to hash.
    CryptHashSessionKey(hHash, hKeyA, 0);
    // Add destination user's name to hash.
    CryptHashData(hHash, pbDestName, dwDestNameLen, 0);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Add sending user name to hash.
    CryptHashData(hHash, pbSendName, dwSendNameLen, 0);
    // Add "phase 2" text to hash.
    CryptHashData(hHash, "phase 2", 7, 0);
    // Complete the hash computation and retrieve the hash value.
    dwHashLen = HASH_SIZE;
    CryptGetHashParam(hHash, HP_HASHVALUE, pbHash, &dwHashLen, 0);
    // Destroy the hash object.
    CryptDestroyHash(hHash);
    // Transmit the hash value to the sending user.
    ...
    // Wait for the sending user to respond.
    ...
    // Receive a hash value from the sending user and place it in
    // 'pbSendHashValue'. Set 'dwSendHashLen' to the number of bytes in
    // the hash value.
    ...
    //
    // Verify hash value received from the sending user.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Add sending user's name to hash.
    CryptHashData(hHash, pbSendName, dwSendNameLen, 0);
    // Add destination user's name to hash.
    CryptHashData(hHash, pbDestName, dwDestNameLen, 0);
    // Add "phase 3" text to hash.
    CryptHashData(hHash, "phase 3", 7, 0);
    // Complete the hash computation and retrieve the hash value.
    dwHashLen = HASH_SIZE;
    CryptGetHashParam(hHash, HP_HASHVALUE, pbHash, &dwHashLen, 0);
    // Destroy the hash object.
    CryptDestroyHash(hHash));
    //
    // Compare the hash value received from the sending user with the
    // hash value that we just computed. If they do not match, then 
    // terminate the protocol.
    //
    if(dwHashLen!=dwSendHashLen || memcmp(pbHash, pbSendHash, dwHashLen)) {
        printf("Key exchange protocol failed in phase 3!\n");
        printf("Aborting protocol!\n");
        return;
    }
    //
    // Create a shared session key to be used by the two users for
    // exchanging encrypted messages. Both users must agree on the 
    // algorithm and parameters that this key is to use.
    //
    // Create hash object.
    CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
    // Add session key A to hash.
    CryptHashSessionKey(hHash, hKeyA, 0);
    // Add session key B to hash.
    CryptHashSessionKey(hHash, hKeyB, 0);
    // Complete the hash computation and derive a session key from it. 
    // The CRYPT_EXPORTABLE flag is not specified here because the key 
    // is not generally exported out of the CSP.
    CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hSharedKey);
    // Destroy the hash object.
    CryptDestroyHash(hHash);
    //
    // Use the shared key to send encrypted messages to the other user.
    //
    ...
    // Destroy session key.
    CryptDestroyKey(hSharedKey);
    // Destroy handle to sending user's public key.
    CryptDestroyKey(hSharedKey);
    // Release provider handle.
    CryptReleaseContext(hProv, 0);
     
    

    CHAPTER 6

    Encrypting and Decrypting Data

    Encryption is the process in which data (plaintext) is translated into something that appears to be random and meaningless (ciphertext). Decryption is the process in which the ciphertext is converted back to plaintext.

    A symmetric encryption key (also known here as a session key) is used during both the encryption and decryption processes. In order to decrypt a particular piece of ciphertext, you must possess the key that was used to encrypt the data. Essentially, a session key merely consists of a random number, of approximately 40 to 2000 bits in length. The longer the key that is used, the more difficult it is to decrypt a piece of ciphertext without possessing the key.

    The goal of every encryption algorithm is to make it as difficult as possible to decrypt the generated ciphertext without using the key. If a really good encryption algorithm is used, then there is no technique significantly better than methodically trying every possible key. Even for a key size of just 40 bits, this works out to 240 (just over 1 trillion) possible keys.

    It is surprisingly difficult to determine just how good an encryption algorithm is. Algorithms that look promising sometimes turn out to be very easy to break, given the proper attack. When selecting an encryption algorithm, it is probably a good idea to choose one that has been around for a while, and successfully resisted all attacks thus far.

    Introduction to Encryption Techniques

    CryptoAPI can be used by applications to easily encrypt and decrypt messages and files. This section discusses the various options available for encrypting data. For a hands-on description of how to encrypt data using CryptoAPI, see "Encrypting Files and Messages" in this chapter.

    The encryption algorithms available to an application depend on the cryptographic service provider (CSP) being used. However, most CSPs share most of the attributes discussed here. All data encryption using CryptoAPI is performed with a symmetric algorithm, regardless of which CSP is installed.

    Stream Ciphers

    Stream cipher algorithms encrypt data one bit at a time. A stream of plaintext bits flows in one side, and a stream of encrypted ciphertext flows out the other. At least, this is the way it works mathematically; in practice, data is always encrypted in byte units.

    Stream ciphers are not generally considered as secure as block ciphers, although this will vary depending on the particular algorithm. On the other hand, they do tend to execute faster in software. Ciphertext encrypted with stream ciphers is always the same size as the original plaintext.

    Error propagation is usually less when stream ciphers are used. If a bit of ciphertext gets garbled, many stream cipher algorithms will produce only a single bit of garbled plaintext. When a block cipher is used and a ciphertext bit is garbled, at minimum an entire block's worth of plaintext will be garbled. This can be good or bad, depending on the application.

    The only stream cipher provided with the Microsoft RSA Base Provider is the RC4 stream cipher.

    Block Ciphers

    Block cipher algorithms encrypt data in block units, rather than a single bit at a time. The most common block size is 64 bits.

    Because each block is heavily "processed," block ciphers are generally considered more secure than stream ciphers. However, block cipher algorithms tend to execute quite a bit slower.

    All that the basic block encryption algorithm specifies is how to get a block of ciphertext from a block of plaintext and vice versa. All the other implementation details (for example, padding, initialization vectors, and cipher modes) are specified independently of the algorithm. These options are discussed in the next few sections.

    The only block cipher provided with the Microsoft RSA Base Provider is the RC2 block cipher. This algorithm has a block size of 64 bits.

    Padding

    Most plaintext messages will not consist of an even number of blocks. Often, the last block is short, making it necessary to add a padding string. For example, if the block length is 64 bits and the last block contains only 40 bits, then 24 bits of padding must be added.

    This padding string can consist of all zeros, alternating zeros and ones, or some other pattern. Some encryption standards specify a particular padding scheme, such as the one described in the next section.

    Applications using CryptoAPI need not add padding to their plaintext before it is encrypted, nor do they have to remove it after decrypting. This is all handled automatically by CryptoAPI.

    PKCS Padding

    This padding scheme is defined by RSA Data Security, Inc. and is documented in Public-Key Cryptography Standards (PKCS), PKCS #5, section 6.2.

    When this method is used, a padding string is always added, even if the plaintext message divides evenly into blocks. The padding string consists of a sequence of bytes, each of which is equal to the total number of bytes in the padding string. If 24 bits of padding need to be added, then the padding string is "03 03 03." If 64 bits of padding needs to be added, then the string is "08 08 08 08 08 08 08 08."

    Cipher Modes

    When a block cipher is used, any one of the following cipher modes can be specified via the CryptSetKeyParam function. If the application does not explicitly specify one of these modes, then the cipher block chaining (CBC) cipher mode is used.

    Electronic Codebook (ECB)

    When this cipher mode is used, each block is encrypted individually. No feedback is used. This means any blocks of plaintext that are identical and are either in the same message, or in a different message that is encrypted with the same key, will be transformed into identical ciphertext blocks.

    If the plaintext to be encrypted contains substantial repetition, then it is feasible for the ciphertext to be broken one block at a time. Furthermore, it is possible for an unscrupulous person to substitute and exchange individual blocks without detection.

    Initialization vectors cannot be used with this cipher mode.

    If a single bit of the ciphertext block is garbled, then the entire corresponding plaintext block will also be garbled.

    Cipher Block Chaining (CBC)

    This cipher mode introduces feedback. Before each plaintext block is encrypted, it is XOR'ed with the ciphertext of the previous block. This ensures that even if the plaintext contains many identical blocks, they will each encrypt to a different ciphertext block.

    The initialization vector is XOR'ed with the first plaintext block before the block is encrypted.

    As with the Codebook cipher mode, if a single bit of the ciphertext block is garbled, then the corresponding plaintext block will also be garbled. In addition, a bit in the subsequent plaintext block (in the same position as the original garbled bit) will be garbled. Synchronization errors are fatal. If there are extra or missing bytes in the ciphertext, the plaintext will be garbled from that point on.

    When the Microsoft RSA Base Provider is used, this is the default cipher mode.

    Cipher Feedback Mode (CFB)

    The cipher feedback mode lets you process small increments of plaintext into ciphertext, instead of processing an entire block at a time. This can be is useful, for example, when encrypting a stream of data that originates at a keyboard. Each keystroke can be encrypted and transmitted without the need to wait for an entire block to be typed.

    This mode uses a shift register which is one block size in length and divided up into sections. For example, if the block size is 64 bits with 8 bits processed at a time, then the shift register would be divided up into 8 sections.

    This is the procedure for each encryption cycle:

    1. The block in the shift register is encrypted normally.

    2. The leftmost 8 bits in the encrypted shift register are XOR'ed with the next 8 bits of plaintext and sent off as 8 bits of ciphertext.

    3. The shift register is shifted 8 bits to the left.

    4. The 8 bits of ciphertext generated in step 2 is placed in the rightmost 8 bits of the shift register.

    In CryptoAPI, the number of bits processed at one time is specified by setting the encryption key's KP_MODE_BITS parameter using the CryptSetKeyParam function. This parameter typically defaults to 8.

    Depending on the value of the KP_MODE_BITS parameter, this cipher mode is substantially slower than the Cipher Block Chaining mode. For example, if the block size is 64 bits with 8 bits are processed at a time, this cipher mode is 64/8 or 8 times slower.

    Before the encryption process begins, the shift register is filled with the initialization vector.

    If a bit in the cipher text is garbled, one plaintext bit is garbled and the shift register is corrupted. This results in the next several plaintext blocks being garbled until the bad bit is shifted out of the shift register. In the preceding example, 9 bytes of plaintext would be garbled. This is the same amount of error propagation as with the Cipher Block Chaining mode. Synchronization errors are not fatal, provided that the slip is a multiple of KP_MODE_BITS. Thus, if KP_MODE_BITS is 8 and there are extra or missing bytes from the ciphertext, then 9 bytes of plaintext are garbled and the plaintext will have the same number of extra or missing bytes.

    Output Feedback Mode (OFB)

    This mode is similar to the cipher feedback mode. The only difference between the two modes is how the shift register is filled.

    The output feedback (OFB) cipher mode uses the following encryption cycle:

    1. The block in the shift register is encrypted normally.

    2. The leftmost 8 bits in the encrypted shift register are XOR'ed with the next 8 bits of plaintext and sent off as 8 bits of ciphertext.

    3. The shift register is shifted 8 bits to the left.

    4. The leftmost 8 bits of the encrypted shift register used in step 2 is placed in the rightmost 8 bits of the shift register.

    As with the Cipher Feedback mode, the shift register is filled with the initialization vector before the encryption process starts.

    If a bit in the cipher text is garbled, the corresponding bit of plaintext will also be garbled. This is much better than the Cipher Feedback mode. However, synchronization errors are fatal. If there are extra or missing bits from the ciphertext, then the plaintext will be garbled from that point on.


    Note

    According to Gait (see reference below), the OFB block cipher mode has a weakness when the number of bits fed back is different than the block size. It is thus recommended that the KP_MODE_BITS parameter be set to the block size when this cipher mode is used.

    *J. Gait, "A New Nonlinear Pseudorandom Number Generator," IEEE Transactions on Software Engineering, v. SE-3, n. 5, Sep 1977, pp. 359-363.

    Initialization Vectors

    An initialization vector is a random number, usually the same number of bits as the block size, that is used as a starting point when encrypting a set of data. Initialization vectors are only used with those cipher modes that make use of feedback. This ensures that the effect of the initialization vector is propagated throughout the entire plaintext message being encrypted.

    If initialization vectors are not used, then when two identical plaintext messages are encrypted with the same key, two identical ciphertext messages are generated. However, if each plaintext message is encrypted with a different initialization vector, the ciphertext messages generated are completely different.

    You should always encrypt each message with a different initialization vector, particularly when the messages contain a large amount of duplication.

    Applications using CryptoAPI are responsible for transmitting the initialization vector along with the encrypted message. There is no need to encrypt this vector.

    Salt Values

    Salt values make up a portion of many session keys, as shown.

    Structure of a session key.

    As with the key bits, the salt bits also consist of random data. The difference is that the key bits must be kept secret at all costs, while the salt values are made public. When exchanging keys using the CryptoAPI, the key bits are transmitted inside of encrypted key blobs. The salt bits, on the other hand, are transmitted in plaintext form.

    The size of the salt values will vary, depending on the CSP used. For example, the Microsoft RSA Base Provider uses salt values of 88 bits and key values of 40 bits, for a total key size of 128 bits. Even though the salt bits make up part of each encryption key, they are usually ignored when discussing keys and key sizes. That is, when talking about Microsoft RSA Base Provider encryption keys, we refer to them as 40 bit keys.

    Salt values are most useful when transmitting or storing large amounts of nearly identical packets using the same encryption key. Normally, two identical packets would encrypt into two identical ciphertext packets. However, this would indicate to an eavesdropper that the packets are identical and, thus, could be attacked simultaneously. But, if the salt value is changed with every packet sent, then a completely different ciphertext packet will always be generated, even if the plaintext packets are the same.

    Because salt values need not be kept secret and can be transmitted in plaintext form bundled with each ciphertext packet, it is much easier to change salt values once per packet than it would be to change the key value itself.

    Applications should generate salt values with the CryptGenRandom function. It is important that each salt value be completely different than the other ones, particularly when using stream ciphers.

    Common Encryption Algorithms

    This section briefly describes each of the encryption algorithms supplied with the Microsoft RSA Base Provider. The internal details of these algorithms are well beyond the scope of this document. Refer to "Related Documentation" at the beginning of this guide for a list of additional reading material.

    The following table lists several encryption algorithms along with some performance benchmarks. This table was generated by an application using CryptoAPI on a 120-MHz, Pentium-based computer. These figures are for comparison purposes only, your mileage may vary.

    Cipher    Cipher Type    Key Setup Time    Encryption Speed  
                             (microseconds)    (bytes/second)    
    
                                                                 
    
    DES       64-bit block   460               1,138,519         
    
    RC2       64-bit block   40                286,888           
    
    RC4       stream         151               2,377,723         
    
    
    

    RC2 Block Cipher

    The RC2 block cipher algorithm was developed by RSA Data Security, Inc. The details of this algorithm have not been published.

    RC2 is a variable-key-length cipher. However, when using CryptoAPI with the Microsoft RSA Base Provider, the key length is hard-coded to 40 bits. The block size is fixed at 64 bits.

    RC4 Stream Cipher

    The RC4 stream cipher was developed by RSA Data Security, Inc. The details of this algorithm have not been published.

    RC4 is a variable-key-length cipher. However, when using CryptoAPI with the Microsoft RSA Base Provider, the key length is hard-coded to 40 bits.

    RSA Public-Key Cipher

    The RSA public-key cipher was developed by (and named after) Ron Rivest, Adi Shamir, and Leonard Adleman, in the late 1970's. This algorithm is very well known; you can read about its internal details in any book on cryptography.

    RSA is used by many CSPs to encrypt/decrypt keys and to generate/verify digital signatures. This algorithm is used when operations are performed using either the key exchange or digital signature key pair. When using CryptoAPI, this algorithm cannot be used to encrypt bulk data.

    RSA is a variable-key-length cipher. However, when using CryptoAPI with the Microsoft RSA Base Provider, the key length is hard-coded to 512 bits.

    Encrypting Files and Messages

    To encrypt a file so only the current user can access its data, the file is bulk encrypted with a symmetric cipher. The key to this cipher is then kept in an access block (key blob) that can only be opened with the user's private key. Note that this technique also works for encrypting messages for specific recipients.

    Encrypting Messages Using CryptoAPI

    To encrypt a message, a session key must first be generated using the CryptGenKey function. Making this call generates a random key and returns a handle so the key can be used to encrypt and decrypt data. The encryption algorithm to use is also specified at this point. Because the CryptoAPI does not permit applications to use public-key algorithms to encrypt bulk data, you should specify a symmetric algorithm such as RC2 or RC4, with the CryptGenKey call.

    Alternatively, if an application needs to encrypt the message in such a way that anyone with a given password can decrypt the data, the CryptDeriveKey function should be used to transform the password into a key suitable for encryption. Note that, in this case, this function is called instead of the CryptGenKey function and the subsequent CryptExportKey calls are not needed.

    Once the key has been generated, extra cryptographic properties of the key can be set with the CryptSetKeyParam function. For example, this function allows different sections of the file to be encrypted with different key salts and provides a way to change the cipher mode or initialization vector of the key. These parameters can be used to make the encryption conform with a particular data encryption standard.

    Encrypt the data in the file with the CryptEncrypt function. The CryptEncrypt function takes a session key, which was generated in the previous step, and encrypts a buffer of data. Note that as the data is encrypted, the data may be slightly expanded by the encryption algorithm. The application is responsible for remembering the length of the encrypted data so the proper length can later be given to the CryptDecrypt function.

    To allow the current user to decrypt the data in the future, the CryptExportKey function is used to save the decryption key in an encrypted form (a key blob) that can only be decrypted with the user's private key. This function requires the user's key exchange public key for this purpose, which can be obtained by using the CryptGetUserKey function. The CryptExportKey function will return a key blob that must be stored by the application for use in decrypting the file.

    Note that if the application has certificates (or public keys) for other users, it can permit other users to decrypt the file by performing CryptExportKey calls for each user it wants to give access. The returned key blobs must be stored by the application, as in the previous step.

    Structure of an Encrypted File

    There are a number of standard formats for encrypted files and messages. These are designed to make it easier for different applications to communicate. An explanation of these formats falls outside the scope of this document. Refer to "Related Documentation" at the beginning of this guide for a list of additional reading material.

    Once a file or message has been encrypted, the following data must be stored by the application and is usually kept bundled together. This is the data:

    The encrypted data. When a block cipher is used, the data is padded out to a multiple of the cipher's block size. Padding is often added even when the original message is already an even multiple. When a stream cipher is used, the encrypted data is generally the same size as the original plaintext.

    One or more key blobs, each containing the session key used to encrypt the message. Each of these key blobs is encrypted with the key exchange public key of a user who is to later decrypt the data. Note that these are not stored if the key was derived from a password. Instead, when it is time to decrypt the message, the session key is recreated from the password. The password itself must be remembered by the user, of course.

    Any salt values that were specified as the data was being encrypted. When the data is decrypted, these values will have to be specified (using the CryptSetKeyParam function) in the same manner as when the data was encrypted.

    Any initialization vectors that were specified as the data was being encrypted. These values are handled in much the same way as the salts.

    All parameters that were specified with the CryptSetKeyParam function as the message was being encrypted must also be specified as the message is decrypted. It may be appropriate to store some of these parameters with the encrypted message as well.

    Encryption Example

    This example reads data from a text file (test1.txt), encrypts it using the RC2 block cipher, and writes out the encrypted data to another file (test1.xxx). A random session key is generated to perform the encryption and is stored to the output file along with the encrypted data. Note that this session key is encrypted with our own public key exchange key by the CryptExportKey function.

    #include <wincrypt.h>
    FILE *hSource = NULL;
    FILE *hDest = NULL;
    int eof = 0;
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    HCRYPTKEY hXchgKey = 0;
    #define BLOCK_SIZE 160
    #define BUFFER_SIZE (BLOCK_SIZE+16) // Give buffer 16 bytes of extra
                                        // room for padding, etc.
    BYTE pbBuffer[BUFFER_SIZE];
    DWORD dwCount;
    BYTE *pbKeyBlob = NULL;
    DWORD dwBlobLen;
    // Open source file.
    if((hSource=fopen("test1.txt","rb"))==NULL) {
        printf("Error opening source file!\n");
        goto done;
    }
    // Open destination file.
    if((hDest=fopen("test1.xxx","wb"))==NULL) {
        printf("Error opening destination file!\n");
        goto done;
    }
    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }
    // Get handle to key exchange key.
    if(!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hXchgKey)) {
        printf("Error %x during CryptGetUserKey!\n", GetLastError());
        goto done;
    }
    // Create a random block cipher session key.
    if(!CryptGenKey(hProv, CALG_RC2, CRYPT_EXPORTABLE, &hKey)) {
        printf("Error %x during CryptGenKey!\n", GetLastError());
        goto done;
    }
    // Determine size of key blob and allocate memory.
    if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, NULL, &dwBlobLen)) {
        printf("Error %x computing blob length!\n", GetLastError());
        goto done;
    }
    if((pbKeyBlob = malloc(dwBlobLen)) == NULL) {
        printf("Out of memory!\n");
        goto done;
    }
    // Export key into a simple key blob.
    if(!CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen)) {
        printf("Error %x during CryptExportKey!\n", GetLastError());
        free(pbKeyBlob);
        goto done;
    }
    // Write size of key blob to destination file.
    fwrite(&dwBlobLen, sizeof(DWORD), 1, hDest);
    if(ferror(hDest)) {
        printf("Error writing header!\n");
        free(pbKeyBlob);
        goto done;
    }
    // Write key blob to destination file.
    fwrite(pbKeyBlob, 1, dwBlobLen, hDest);
    if(ferror(hDest)) {
        printf("Error writing header!\n");
        free(pbKeyBlob);
        goto done;
    }
    // Free memory.
    free(pbKeyBlob);
    // Encrypt source file and write to destination file.
    do {
        // Read up to BLOCK_SIZE bytes from source file.
        dwCount = fread(pbBuffer, 1, BLOCK_SIZE, hSource);
        if(ferror(hSource)) {
            printf("Error reading data!\n");
            goto done;
        }
        eof=feof(hSource);
        // Encrypt data
        if(!CryptEncrypt(hKey, 0, eof, 0, pbBuffer, &dwCount, BUFFER_SIZE)) {
            printf("Error %x during CryptEncrypt!\n", GetLastError());
            goto done;
        }
        // Write data to destination file.
        fwrite(pbBuffer, 1, dwCount, hDest);
        if(ferror(hDest)) {
            printf("Error writing data!\n");
            goto done;
        }
    } while(!feof(hSource));
    done:
    // Destroy session key.
    if(hKey != 0) CryptDestroyKey(hKey);
    // Destroy key exchange key.
    if(hXchgKey != 0) CryptDestroyKey(hXchgKey);
    // Release provider handle.
    if(hProv != 0) CryptReleaseContext(hProv, 0);
    // Close source file.
    if(hSource != NULL) fclose(hSource);
    // Close destination file.
    if(hDest != NULL) fclose(hDest);
     
    

    Decrypting Messages Using CryptoAPI

    If a message was encrypted for a particular user, then the CryptGenKey function was used to create a random session key, before the encryption was performed. This means that before the message can be decrypted, the key blob containing the session key needs to be imported into the CSP with the CryptImportKey function. This function will use your key exchange private key to decrypt the key blob. This means that the key blob must have been originally created using the matching key exchange public key.

    If the message was encrypted so that any password holder can access the data, the CryptImportKey function is not used. Instead, you create the decryption session key with the CryptDeriveKey function. You will also need to supply the function with the password (or other access token).

    The session key's parameters need to be configured in the same way as when the encryption was performed. These parameters can be specified using the CryptSetKeyParam function. For example, if the salt value was changed one or more times during the encryption process, then it must also be changed during the decryption process in exactly the same manner.

    The message is decrypted using the CryptDecrypt function. If the message is too large to fit comfortably in memory, it can be decrypted in sections, through multiple calls to CryptDecrypt.

    When the decryption is complete, be sure to destroy the session key, using the CryptDestroyKey function. In addition to destroying the key, this will free up CSP resources.

    Decryption Example

    This example reads the encrypted data from the file created by the "Encryption Example" (test1.xxx), decrypts it using the RC2 block cipher, and writes out the plaintext data to another file (test1.txt). The session key used to perform the decryption is read from the ciphertext file.

    #include <wincrypt.h>
    FILE *hSource = NULL;
    FILE *hDest = NULL;
    int eof = 0;
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    #define BLOCK_SIZE 160
    BYTE pbBuffer[BLOCK_SIZE];
    DWORD dwCount;
    BYTE *pbKeyBlob = NULL;
    DWORD dwBlobLen;
    // Open source file.
    if((hSource=fopen("test1.xxx","rb"))==NULL) {
        printf("Error opening source file!\n");
        goto done;
    }
    // Open destination file.
    if((hDest=fopen("test1.txt","wb"))==NULL) {
        printf("Error opening destination file!\n");
        goto done;
    }
    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }
    // Read key blob length from source file and allocate memory.
    fread(&dwBlobLen, sizeof(DWORD), 1, hSource);
    if(ferror(hSource) || feof(hSource)) {
        printf("Error reading file header!\n");
        goto done;
    }
    if((pbKeyBlob = malloc(dwBlobLen)) == NULL) {
        printf("Out of memory!\n");
        goto done;
    }
    // Read key blob from source file.
    fread(pbKeyBlob, 1, dwBlobLen, hSource);
    if(ferror(hSource) || feof(hSource)) {
        printf("Error reading file header!\n");
        goto done;
    }
    // Import key blob into CSP.
    if(!CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hKey)) {
        printf("Error %x during CryptImportKey!\n", GetLastError());
        goto done;
    }
    // Decrypt source file and write to destination file.
    do {
        // Read up to BLOCK_SIZE bytes from source file.
        dwCount = fread(pbBuffer, 1, BLOCK_SIZE, hSource);
        if(ferror(hSource)) {
            printf("Error reading data from source file!\n");
            goto done;
        }
        eof=feof(hSource);
        // Decrypt data.
        if(!CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount)) {
            printf("Error %x during CryptDecrypt!\n", GetLastError());
            goto done;
        }
        // Write data to destination file.
        fwrite(pbBuffer, 1, dwCount, hDest);
        if(ferror(hDest)) {
            printf("Error writing data to destination file!\n");
            goto done;
        }
    } while(!feof(hSource));
    done:
    // Free memory.
    if(pbKeyBlob) free(pbKeyBlob);
    // Destroy session key.
    if(hKey != 0) CryptDestroyKey(hKey);
    // Release provider handle.
    if(hProv != 0) CryptReleaseContext(hProv, 0);
    // Close source file.
    if(hSource != NULL) fclose(hSource);
    // Close destination file.
    if(hDest != NULL) fclose(hDest);
     
    

    Encrypting and Decrypting Simultaneously

    When encrypting or decrypting two streams of data simultaneously with the same cryptographic key, a certain amount of care must be taken. The same physical session key must not be used for both operations, because every session key contains internal state information and it will get mixed up if used for more than one operation at a time. A fairly simple solution to this problem is to make a copy of the session key. In this way, the original key can be used for one operation and the copy used for the other.

    Copying a session key is done by exporting the key with CryptExportKey and then using CryptImportKey to import it back in. When the key is imported, the CSP will give the "new" key its own section of internal memory, as if it were not related at all to the original key.

    The following code fragment shows how a copy of a session key can be obtained.

    HCRYPTPROV hProv;    // Handle to a CSP.
    HCRYPTKEY hKey;      // Handle to a session key.
    HCRYPTKEY hCopyKey = 0;
    HCRYPTKEY hPubKey = 0;
    BYTE pbBlob[256];
    DWORD dwBlobLen;
    // Get a handle to our own key exchange public key.
    CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hPubKey);
    // Export the session key into a key blob.
    dwBlobLen = 256;
    CryptExportKey(hKey, hPubKey, SIMPLEBLOB, 0, pbBlob, &dwBlobLen);
    // Import the session key back into the CSP. This is stored separately
    // from the original session key.
    CryptImportKey(hProv, pbBlob, dwBlobLen, 0, 0, &hCopyKey);
    // Use 'hKey' for one set of operations and 'hCopyKey' for the other.
    ...
     
    

    Note that this technique should not be used with stream ciphers, as stream cipher keys should never be used more than once. Instead, separate transmit and receive keys should be used.

    CHAPTER 7

    Hashes and Digital Signatures

    Using the functions described in this section, a user can "digitally sign" a piece of data such that any other user can easily verify that the data has not been changed since it was signed. The identity of the user that signed the data can also be easily verified.

    A digital signature consists of a small amount of binary data, typically less than 256 bytes. This signature can be bundled with the signed message or stored separately; this is up to the individual application.

    The Microsoft RSA Base Provider creates digital signatures that conform to the RSA Public-Key Cryptography Standard (PKCS) #6.

    How Digital Signatures Work

    There are two steps involved in creating a digital signature from a message. The first step involves creating a hash value (also known as a message digest) from the message. This hash value is then signed, using the private key of the signer. Following is an illustration of the steps involved in creating a digital signature:

    Creating a Digital Signature

    To verify a signature, both the message and the signature are required. First, a hash value must be created from the message, in the same way as when the signature was created. This hash value is then verified against the signature, using the public key of the signer. If the hash value and the signature match, you can be confident that the message is indeed the one the signer originally signed and that it has not been tampered with. The following diagram illustrates the process involved in verifying a digital signature.

    Verifying a Digital Signature

    A hash value consists of a small amount of binary data, typically around 160 bits. This is produced using a hashing algorithm. A number of these algorithms are listed later in this section.

    All hash values share the following properties, regardless of the algorithm used:

    The hash value is of a fixed length, regardless of the size of the message. The message can be several kilobytes or several gigabytes, it doesn't matter. Depending on the algorithm used, the hash value length will generally be either 128 or 160 bits.

    Every pair of nonidentical messages will translate into a completely different hash value, even if the two messages differ only by a single bit. Using today's technology, it is not feasible to discover a pair of messages that translate to the same hash value without breaking the hashing algorithm.

    All hashing algorithms are fully deterministic. That is, each time a particular message is hashed using the same algorithm, the exact same hash value will be produced.

    All hashing algorithms are one-way. Given a hash value, it is not possible to recover the original message. In fact, none of the properties of the original message can be determined given the hash value alone.

    Signing and Verifying Messages

    To apply a digital signature to a piece of data, a secure hash function is used to build a digest of the data (for example, a 160-bit hash value) which is then transformed with the private key of the signer. Other users can then check the authenticity of the signature by reconstructing the hash value, and checking it against the "inverse" of the digital signature data. CryptoAPI abstracts out the actual method of doing the signature, so that applications need not be aware of the signature mechanics.

    Signing Data

    In order to sign data, a hash object must first be created using the CryptCreateHash function. This object will accumulate the data to be signed. Next, the data is added to the hash object with the CryptHashData function.

    After the last block of data is added to the hash, the CryptSignHash function is used to sign the hash. A description of the data can also be added to the hash object at this point. Once the digital signature data has been obtained, the hash object should be destroyed with the CryptDestroyHash function.

    Hashes can be signed with either the signature private key or the key exchange private key. The signature key should be used when the user who owns the signature key is signing some of his or her data. The key exchange key should be used when signing data that does not directly belong to the user. The classic example of this is when the exchange key is used to sign session keys during a key exchange protocol.

    Verifying Signatures

    To verify a signature, a hash object must first be created using the CryptCreateHash function. This object will accumulate the data to be verified. The data is then added to the hash object with the CryptHashData function.

    After the last block of data is added to the hash, the CryptVerifySignature function is used to verify the signature. The signature data, a handle to the hash object, and the description string must all be supplied to CryptVerifySignature. A handle to the key pair that was used to sign the data must also be specified.

    Once the signature has been verified (or has failed the verification) the hash object should be destroyed with the CryptDestroyHash function.

    Obtaining the Hash Value

    To obtain the hash value, a hash object must first be created using the CryptCreateHash function. This object will accumulate the data to be verified. The data is then added to the hash object with the CryptHashData function.

    After the last block of data is added to the hash, the CryptGetHashParam function is used to obtain the hash value.

    Once the hash value has been obtained, the hash object should be destroyed with the CryptDestroyHash function.

    Hashing and Signature Algorithms

    This section lists several algorithms used to compute hashes and digital signatures. Each of these algorithms is supported by the Microsoft RSA Base Provider. However, the internal details of these algorithms are well beyond the scope of this document. Refer to "Related Documentation" at the beginning of this guide for a list of additional sources.

    MD2, MD4, and MD5

    The MD2, MD4, and MD5 hashing algorithms were all developed by RSA Data Security, Inc. These algorithms were developed in sequential order, with the later algorithms generally being better (more secure) than the earlier ones. All three generate 128-bit hash values. Of the three algorithms, MD5 is recommended.

    These algorithms are well known and can be reviewed in detail in any reference on cryptography.

    Secure Hash Algorithm (SHA)

    The SHA hashing algorithm was developed by the National Institute of Standards and Technology (NIST) and by the National Security Agency (NSA). This algorithm was developed for use with DSA (Digital Signature Algorithm) or DSS (Digital Signature Standard).

    This algorithm generates a 160-bit hash value.

    Message Authentication Code (MAC)

    Message Authentication Codes (MACs) are similar to hash values, but are computed using a session key. Because of this difference, you must possess the session key in order to recompute the hash value to verify that the base data has not changed.

    The MACs implemented by the Microsoft RSA Base Provider are of the most common sort. That is, they are block cipher MACs. This method encrypts the base data with a block cipher and then uses the last encrypted block as the hash value. The encryption algorithm used to build the MAC is the one that was specified when the session key was created.


    Warning

    The same session key should not be used for both message encryption and MAC generation. Doing so greatly increases the risk of your messages being decoded.

    CHAPTER 8

    System Administration

    This section discusses how multiple cryptographic service providers (CSPs) can be installed on a computer and the default providers specified. The structure of the system Registry is also mentioned.

    Installing a New Provider

    New providers are installed by running their Setup program. This copies the CSP files to the appropriate directories and makes all needed changes to the system Registry.

    Outline of CryptoAPI Registry Usage

    CryptoAPI uses the system Registry to store a database of the CSPs that have been installed on the computer. Both the machine default providers and the user default providers are also recorded here.


    Warning

    This section is included for informational purposes only. The details of CryptoAPI's Registry usage may change at any time. Under no circumstances should an application read from or alter the Registry directly.

    The following is a partial outline of the portions of the system Registry used by CryptoAPI. Some sample entries are also shown.

    HKEY_LOCAL_MACHINE
    SOFTWARE
    Microsoft
    Cryptography
    Defaults
    Provider
    Microsoft Base Cryptographic Provider v1.0
    >Image Path:REG_SZ:rsabase.dll
    >Signature:REG_BINARY:<digital signature>
    >Type:REG_DWORD:0x1
    John's Provider
    >Image Path:REG_SZ:johncsp.dll
    >Signature:REG_BINARY:<digital signature>
    >Type:REG_DWORD:0x2a
    Provider Types
    Type 001
    >Name:REG_SZ:Microsoft Base Cryptographic Provider v1.0
    Type 042
    >Name:REG_SZ:John's Provider

    HKEY_CURRENT_USER
    Software
    Microsoft
    Cryptography
    Providers
    Type 001
    >Name:REG_SZ:Microsoft Base Cryptographic Provider v1.0

    Entries under the HKEY_LOCAL_MACHINE\...\Provider key contain information about all the CSPs that have been installed on the computer. These entries are created by the Setup program used to install a new CSP. Note that these entries are organized under subkeys whose names indicate the provider name.

    Entries under the HKEY_LOCAL_MACHINE\...\Provider Types key contain the name of the machine default CSP for each provider type. These entries are also created by the Setup program used to install a new CSP. Note that these entries are organized under subkeys whose names indicate the provider type (in decimal format).

    Entries under the HKEY_CURRENT_USER\...\Providers key contain the name of the current user default CSP for each provider type. These entries are created/modified by the CryptSetProvider function. Note that these entries are also organized under subkeys whose names indicate the provider type.

    CHAPTER 9

    Service Provider Functions

    The functions described in this section are used by applications to connect to and disconnect from cryptographic service providers (CSPs). The following table briefly describes each function:

    Function            Description                            
    
                                                               
    
    CryptAcquireContext Acquires a handle to the current       
                        user's key container within a          
                        particular CSP.                        
    
    CryptGetProvParam   Retrieves attributes of a CSP.         
    
    CryptReleaseContext Releases the handle acquired by        
                        CryptAcquireContext.                   
    
    CryptSetProvider    Specifies the user default CSP for a   
                        particular CSP type.                   
    
    CryptSetProvParam   Specifies attributes of a CSP.         
    
    
    


    CryptAcquireContext

    The CryptAcquireContext function is used to acquire a handle to a particular key container within a particular CSP. This returned handle can then be used to make calls to the selected CSP.

    This function performs two operations. It first attempts to find a CSP with the characteristics described in the dwProvType and pszProvider parameters. If the CSP is found, then the function attempts to find a key container within the CSP matching the name specified by the pszContainer parameter.

    This function can also be used to create and destroy key containers, depending on the value of the dwFlags parameter.

    BOOL CRYPTFUNC CryptAcquireContext(
    HCRYPTPROV *
    phProv, // out
    LPCTSTR pszContainer, // in
    LPCTSTR pszProvider, // in
    DWORD dwProvType, // in
    DWORD dwFlags) // in

    Parameters

    phProv

    The address to which the function copies a handle to the CSP.

    pszContainer

    The key container name. This is a zero-terminated string that identifies the key container to the CSP. This name is independent of the method used to store the keys. Some CSPs will store their key containers internally (in hardware), some will use the system Registry, and others will use the file system.

    If this parameter is NULL, then a default key container name will be used. For example, if the Microsoft RSA Base Provider is being used, then the current user's logon name will be used as the name of the key container. Other CSPs may also have default key containers that can be acquired in this way.

    An application can obtain the name of the acquired key container at a later time by reading the PP_CONTAINER parameter from the CryptGetProvParam function.

    pszProvider

    The provider name. This is a zero-terminated string that specifies the CSP to be used.

    If this parameter is NULL then the user default provider is used. This situation is discussed in detail in Chapter 3.

    An application can obtain the name of the acquired CSP at a later time by reading the PP_NAME parameter from the CryptGetProvParam function.

    dwProvType

    The type of provider to acquire. The following provider types are predefined. These are discussed in detail in Chapter 3.

    PROV_RSA_FULL

    PROV_RSA_SIG

    PROV_DSS

    PROV_FORTEZZA

    PROV_MS_MAIL

    dwFlags

    The flag values. This parameter is normally set to zero, but some applications will set one (and only one) of the following flags:

    CRYPT_VERIFYCONTEXT

    If this flag is set, then the application will have no access to the key container's private keys. In fact, if pszContainer is NULL and no default key container is present, the application will have no access to a key container at all.

    This option is intended to be used by applications whose only cryptographic need is to verify digital signatures. The only operations normally needed in this case are public key import, hashing, and signature verification.

    When CryptAcquireContext is called, many CSPs will require input from the owning user before granting access to the private keys in the key container. For example, the private keys may be encrypted, requiring a password from the user before they can be used. However, if the CRYPT_VERIFYCONTEXT flag is specified, access to the private keys is not required and the user interface can be bypassed.

    CRYPT_NEWKEYSET

    If this flag is set, then a new key container will be created with the name specified by pszContainer. If pszContainer is NULL, then a key container with the default name will be created.

    Note that when key containers are created, most CSPs will not automatically create any public/private key pairs. These keys must be created as a separate step with the CryptGenKey function.


    Important

    This flag should only be set by administrative applications. Normal applications should not create key containers.

    CRYPT_DELETEKEYSET

    If this flag is set, then the key container specified by pszContainer is deleted. If pszContainer is NULL, then the key container with the default name is deleted. All key pairs in the key container are also destroyed.

    When the CRYPT_DELETEKEYSET flag is set, the value returned in phProv is undefined and, thus, the CryptReleaseContext function need not be called afterwards.


    Important

    This flag should only be set by administrative applications. Normal applications should not destroy key containers.

    Return Value

    If the function succeeds, the return value is TRUE. If it does not succeed, the return value is FALSE. To retrieve extended error information, use the GetLastError function.

    The following table lists the error codes most commonly returned by the GetLastError function.

    Error                       Description                    
    
                                                               
    
    ERROR_INVALID_PARAMETER     One of the parameters          
                                contains an invalid value.     
                                This is most often an illegal  
                                pointer.                       
    
    ERROR_NOT_ENOUGH_MEMORY     The operating system ran out   
                                of memory during the           
                                operation.                     
    
    NTE_BAD_FLAGS               The dwFlags parameter has an   
                                illegal value.                 
    
    NTE_BAD_KEYSET              The Registry entry for the     
                                key container could not be     
                                opened and may not exist.      
    
    NTE_BAD_KEYSET_PARAM        The pszContainer or            
                                pszProvider parameter is set   
                                to an illegal value.           
    
    NTE_BAD_PROV_TYPE           The value of the dwProvType    
                                parameter is out of range.     
                                All provider types must be     
                                from 1 to 999, inclusive.      
    
    NTE_BAD_SIGNATURE           The provider DLL signature     
                                did not verify correctly.      
                                Either the DLL or the digital  
                                signature has been tampered    
                                with.                          
    
    NTE_EXISTS                  The dwFlags parameter is       
                                CRYPT_NEWKEYSET, but the key   
                                container already exists.      
    
    NTE_KEYSET_ENTRY_BAD        The Registry entry for the     
                                pszContainer key container     
                                was found (in the              
                                HKEY_CURRENT_USER window),     
                                but is corrupt. See Chapter 8  
                                for details about CryptoAPI's  
                                Registry usage.                
    
    NTE_KEYSET_NOT_DEF          No Registry entry exists in    
                                the HKEY_CURRENT_USER window   
                                for the key container          
                                specified by pszContainer.     
    
    NTE_NO_MEMORY               The CSP ran out of memory      
                                during the operation.          
    
    NTE_PROV_DLL_NOT_FOUND      The provider DLL file does     
                                not exist or is not on the     
                                current path.                  
    
    NTE_PROV_TYPE_ENTRY_BAD     The Registry entry for the     
                                provider type specified by     
                                dwProvType is corrupt. This    
                                error may relate to either     
                                the user default CSP list or   
                                the machine default CSP list.  
                                See Chapter 8 for details      
                                about CryptoAPI's Registry