智能卡CSP 的設計與實現方法
隨著智能卡功能的不斷完善,卡片運算速度和存貯功能的不斷加強,在對安全性要求較高的領域,智能卡的應用開始越來越廣泛。其中作為硬件數字證書使用,也是智能卡的一項重要功能。智能卡作為硬件級的加密設備,如何同當前使用最為廣泛的windows操作系統進行無縫連接,需要開發智能卡讀卡器硬件、讀卡器驅動程序、智能卡CSP 等一系列軟硬件設施,本文將主要針對其中的智能卡CSP 開發。
1 CSP 簡介
加密服務提供者Cryptographic Service Provider(簡稱CSP)是Windows 操作系統加密體系的重要組成部分,它提供了一組標準API 函數(CryptoAPI)供應用程序調用,如IE 使用SSL 訪問網站、Outlook 發送加密郵件等,均會調用到CryptoAPI 函數。智能卡作為一種硬件級的加密設備,要實現和windows 操作系統的無縫連接,使應用程序能夠通過CryptoAPI 這套標準函數使用智能卡設備, 就必定要針對該種設備開發CSP 服務程序。智能卡設備CSP 在系統中的位置如圖1 所示。
.jpg)

2 CSP 中的容器
CSP 使用容器來管理密鑰,以RSA 密鑰為例,一個容器中可以存在一對RSA 交換密鑰和一對RSA 簽名密鑰。一個智能卡中可以有多個容器。結構如圖2 所示。Windows 系統中一般會存在多個CSP,既有微軟自己的純軟件型CSP,也可能有數個不同廠商的軟硬件加密設備的CSP。應用程序可以通過CryptoAPI 函數的來指定使用哪個CSP 以及該CSP 中的哪個容器。
3 CSP 在智能卡中的密鑰存貯結構
3.1 智能卡中私鑰的特點
在CSP 中私鑰的作用主要是用來做解密或簽名。智能卡這種設備的一個重要特點是私鑰可以設定為讀禁止,私鑰不能被從智能卡中讀出。當需要用私鑰進行解密或者簽名時,被解密或簽名的數據必須先送入智能卡,由智能卡中的處理器對數據做解密或簽名,解密或簽名后的數據再出智能卡返回計算機中。整個過程中私鑰不能被計算機讀出,解密或簽名的過程是在智能卡中進行的,保證了私鑰的不可復制特性,避免了黑客攻入計算機,將私鑰遠程拷貝走的可能。
3.2 私鑰、公鑰和證書的不同保護級別
使用私鑰時,智能卡需要驗證保護該私鑰的PIN 碼,只有PIN 碼驗證正確的情況下才能使用私鑰。但智能卡中的證書和公鑰則一般不需要PIN 碼保護,以保證使用過程中的靈活性。在CryptoAPI 的SILENT 模式中,公鑰可以隨時被讀出。另外當智能卡插入到連接計算機的讀卡器中時,一般都需要將智能卡中的證書導入到windows 系統的證書庫中,因IE 瀏覽器不能直接識別智能卡中的證書,它需要從windows 系統的證書庫中去讀證書。這些情況下均需要讓智能卡不經過PIN 碼驗證,就能使智能卡中的公鑰和證書被讀出。
3.3 CSP 密鑰容器的存貯結構設計
3.3.1 CSP 密鑰容器存貯結構圖

圖3 為CSP 密鑰容器存貯結構圖。
.3.2 公開目錄(DDF):如圖3 所示,公開目錄(DDF)下的ADF 子目錄下存放RSA 加密公鑰及相應證書、RSA 簽名公鑰及相應證書,容器名稱為ADF 目錄的名稱,可以同時存在多個容器。公開目錄(DDF)、容器目錄(ADF)、公鑰、證書都不設置PIN 碼保護,公鑰和證書可以隨時可以被從智能卡中讀出。
3.3.3 私鑰目錄(DDF):如圖3 所示,私鑰目錄(DDF)下的ADF 子目錄下存放RSA 加密密鑰對中的私鑰和RSA 簽名密鑰對中的私鑰,ADF 目錄名稱與對應公鑰所在的ADF 目錄名稱相同。私鑰目錄(DDF)設置PIN 碼保護,要使用該目錄的子目錄下的私鑰,必須首先通過私鑰目錄(DDF)的PIN 碼驗證。
3.3.4 容器名稱:圖3 中的私鑰目錄(DDF)下的容器目錄(ADF)名稱必須和公開目錄(DDF)下的容器目錄(ADF)名稱對應,比如私鑰目錄(DDF)下的
容器目錄1 和公開目錄(DDF)下的容器目錄1 的名稱必須相同,因為它們實際上是代表著同一個容器名。
3.3.5 容器索引文件:容器索引文件存放著智能卡中的所有容器名稱, 并且指明容器名稱和容器目錄(ADF) 之間的關系。每次調用CSP 的CPAcquireContext函數時,該函數都需要從這個文件中獲取智能卡中已有的所有容器名稱。容器索引文件的結構可以用如下方式表示:
## 容器名稱1# 容器目錄1(ADF)## 容器名稱2# 容器目錄2(ADF)##......#......##
4 CSP 軟件架構的設計與實現
4.1 CSP 軟件架構的種類
CSP 從整體上看主要有上下文環境對象、密鑰對象、哈希對象三種數據結構。在開發CSP 的過程有幾種方法來實現對這三種數據結構對象的管理,具體如下:
結構對象的管理,具體如下:
1) 上下文環境對象在CSP 中實現,密鑰對象和哈希對象交給微軟的純軟件型CSP 來管理。
2) 上下文環境對象和密鑰對象在CSP 中實現,哈希對象交給微軟的純軟件型CSP 來管理。
3) 上下文環境對象、密鑰對象和哈希對象都在CSP 中實現。
其中第3 種方法實現CSP 的復雜性最高,但也最為靈活,本文主要探討這種方法。由于在CSP 開發中一般都用C 語言或C++語言來實現,因此約定以下用到的數據結構定義均使用C++語言來表述。
4.2 CSP 中幾個基本的對象類型分析
通過分析微軟定義的CSP 25 個基本函數,可以發現CSP 的上下文環境對象、密鑰對象、哈希對象是以HCRYPTPROV、HCRYPTKEY和HCRYPTHASH 三種類型存在的。
HCRYPTPROV 對象類型的作用是串聯起整個CSP 的上下文環境。該對象一般由CPAcquireContext 函數產生,由CPReleaseContext函數終止。
HCRYPTKEY 對象類型起到密鑰句柄的作用。其存在周期一般是從密鑰的產生或者密鑰導入開始,經歷密鑰的使用,最后到密鑰句柄被釋放的過程。
HCRYPTHASH 對象類型起到哈希句柄的作用。其存在周期一般是從哈希的產生,到哈希的使用,最后是哈希句柄被釋放的過程。
{$page$}
4.3 三種對象類型的設計與實現

在CSP 的具體設計與實現中,HCRYPTPROV 對象類型、HCRYPTKEY對象類型和HCRYPTHASH 對象類型之間的相互關系如圖4 所示。
ProvQueue 鏈表為CSP 上下文環境句柄鏈表,KeyQueue 鏈表為密鑰句柄鏈表,HashQueue 鏈表為哈希句柄鏈表。
CProvContext 為CSP 上下文環境類、CCryptKey 為密鑰句柄類、CCryptHash 為哈希句柄類,這幾個類的具體設計見下文。
4.3.1 CSP 上下文環境對象的實現
CSP 的上下文環境由HCRYPTPROV 對象類型實現,在實現過程中可以定義一個CProvContext 的類,具體定義如下:
.jpg)
該類包含有在當前上下文環境中使用的容器名ContainerName,通過SetProvParam 成員函數可以對當前上下文環境屬性進行設置,通過GetProvParam 成員函數可以得到當前上下文環境的屬性。將該類強制轉換成HCRYPTPROV 類型,即可實現CSP 上下文環境的數據類型。
如果在同一個進程中通過CryptoAPI 的CryptAcquireContext 函數同時打開多個不同的容器,此時就會有多個CProvContext 類實例,因此需要通過鏈表來管理多個實例。這里定義一個ProvQueue 的鏈表,在同一個進程中每當打開一個容器或新建一個容器時,就產生一個CProvContext 類的實例,將該實例加入到ProvQueue 鏈表中,通過prevContext 指針指向前一個實例, 通過nextContext 指針指向后一個實例。
CSP 25 個函數中都有CSP 的上下文環境句柄,25 個函數之間的關系可以通過這個句柄得到聯系。比如調用CPEncryt 函數時,首先檢查CPEncryt 傳入HCRYPTPROV 句柄是否在ProvQueue 中存在,如果存在,則ProvQueue 鏈表中對應的該條記錄即為當前的上下文環境句柄,該記錄中包含有當前的容器名、相關的密鑰隊列(KeyQueue)和哈希隊列(HashQueue),從密鑰隊列(KeyQueue)中可以獲取CPEncryt 函數需要使用的密鑰對象。對象之間的關系可參見圖4。
4.3.2 HCRYPTKEY 密鑰對象類型
HCRYPTKEY 密鑰對象類型可以定義一個CCryptKey 的類,在使用過程中將該類強制轉換成HCRYPTKEY 類型,該類具體定義如下:
.jpg)
該類包含有當前密鑰所對應的算法(KeyAlgid)、dwFlags 參數以及當前密鑰隸屬于的CSP 上下文環境對象(pProContext)。通過SetKeyParam 成員函數可以對當前密鑰屬性進行設置,通過GetKeyParam 成員函數可以得到當前密鑰的屬性。
在同一個CSP 上下文環境對象中可以同時產生多個不同的密鑰句柄,因此需要通過密鑰句柄鏈表來管理這些密鑰句柄。定義一個KeyQueue 的鏈表,在同一個CSP 上下文環境對象中每當產生一個新的密鑰或導入一個新的密鑰時,就會產生一個CCryptKey類的實例,將該實例加入到KeyQueue 鏈表中,通過prevCryptKey 指針指向前一個實例, 通過nextCryptKey 指針指向后一個實例。
通過圖4,可以看到密鑰與密鑰鏈表之間的關系,具體使用某個密鑰時需要先從KeyQueue 鏈表中找到對應的密鑰句柄。比如調用CPEncryt 函數時,先檢查CPEncryt 函數傳入的HCRYPTKEY 句柄是否在KeyQueue 鏈表中已經存在,如果存在,則KeyQueue 鏈表中對應的該條記錄即為當前的密鑰句柄,該記錄中包含有當前密鑰的算法(KeyAlgid)、dwFlags 等參數。
4.3.3 HCRYPTHASH 哈希對象類型
HCRYPTHASH 哈希對象類型的定義和HCRYPTKEY 密鑰對象類型的定義相似,定義一個CCryptHash 的類,在使用過程中將該類強制轉換成HCRYPTHASH 類型,該類具體定義如下:

該類中包含有當前哈希所對應的算法(HashAlgid)、dwFlags 參數以及當前哈希隸屬于的CSP 上下文環境對象(pProContext)。通過SetHashParam 成員函數可以對當前哈希屬性進行設置,通過GetHashParam 成員函數可以得到當前哈希的屬性。
在同一個CSP 上下文環境對象中可以同時產生多個不同的哈希句柄,因此需要通過哈希句柄鏈表來管理這些哈希句柄。定義一個HashQueue 的鏈表,在同一CSP 上下文環境中每當產生一個新的哈希時,就會產生一個CCryptHash 類的實例,將該實例加入到HashQueue 鏈表中,通過prevCryptHash 指針指向前一個實例, 通過nextCryptHash 指針指向后一個實例。
通過圖4,可以看到哈希與哈希鏈表之間的關系,具體使用某個哈希時需要先從HashQueue 鏈表中找到對應的哈希句柄。比如調用CPHashData 函數時, 先檢查CPHashData 函數傳入的HCRYPTHASH 句柄是否在HashQueue 鏈表中已經存在, 如果存在,則HashQueue 鏈表中對應的該條記錄即為當前的哈希句柄,該記錄中包含有當前哈希的算法(HashAlgid)、dwFlags 等參數。
5 綜述
綜上所述,由于微軟對于CSP 只是定義了基本的函數接口,具體在CSP 的設計與實現過程中有很強的靈活性,除了本文提到的幾種CSP 的設計和實現方法外,可能還會有其它的CSP 設計和實現方法,CSP 中密鑰存貯結構的設計方法也有很多。隨著智能卡技術的不斷發展,以及智能卡在各行各業中應用的普及,各行各業對智能卡CSP 功能上的要求可能會有不同,安全性方面的要求可能也會有差異,因此CSP 軟件架構如何設計,對應的密鑰存貯結構如何設計,主要還是看CSP 的具體用途 。