邵奇峰,龔雪容,吳保中
(解放軍信息工程大學,鄭州450005)
隨著信息技術的發展,基于數字證書的安全服務得到越來越廣泛的應用。證書解析是數字證書應用的基礎,用于獲取證書中各字段的信息。目前已知的證書解析方法有 Microsoft 的 CryptoApi[1]和.net[2]、開 源 的 OpenSSL[3-4]等,均是在操作系統應用層實現的,且 CryptoApi和.net只能在 Windows平臺中使用。內核層解析時,一般是將證書傳遞到應用層進行解析后再將結果送回,給證書驗證和應用帶來很大不便。盡管OpenSSL的證書解析方法具有較強的通用性,而且開源,但代碼復雜,占用系統資源較多,不適用于操作系統內核層及嵌入式系統等資源受限環境。本文在證書格式研究的基礎上,給出了一種適用于資源受限環境下證書解析的方法,并在 Windows和Linux內核層以及多款嵌入式設備中得到了應用。
目前廣泛應用的數字證書格式為X509v3[5],其基本內容包括基本證書域、簽名算法域和簽名值域三部分,如圖1所示,其中基本證書域又包括版本號、序列號等子域。

圖1 X509v3格式數字證書基本內容
數字證書的格式用ASN.1語法進行描述:


數字證書在頒發時采用了非典型編碼規則(DER),該編碼規則是基本編碼規則(BER)的一個子集,ASN.1值與DER編碼之間為一一對應關系[6]。DER編碼由TLV組成,即Tag、Length和Value,其中Tag為數據類型標簽,Length為數值的字節長度,Value是值。如果進行通用化解析,即在事先不知道內容格式的情況下解析出每個字段的類型、長度、值,則需要考慮ASN.1語法的各種情況,代碼復雜,占用資源多且處理難度大。在Windows和Linux內核層以及嵌入式設備等資源環境有限的情況下實現難度大且不必要。
在數字證書內容格式已知的前提下,證書解析的關鍵就在于如何確定各字段的層次關系以及同層各域的次序,進而快速定位各字段位置。因此,在資源受限環境下Tag字段可忽略,重點在于確定Length和Value字段的位置和值。
證書字段可分為兩類:次序固定字段和次序不固定字段。基本證書域中的擴展字段為次序不固定字段,其余字段均為次序固定字段,盡管基本證書域中的擴展字段為次序不固定字段,但擴展字段的集合在整個證書中的次序仍是固定的,即在AlgorithmIdentifier之前,在SubjectPublicKeylnfo之后。
在證書解析時,無論是次序固定字段還是次序不固定字段,都必須先定位字段位置。
首先,確定字段所處的包裹層次。X509v3數字證書的層次關系如圖2所示,最外層是Certificate::=SEQUENCE{…},第 2層 是tbsCertificate、signatureAlgorithm和signature,第3層為version、serialNumber等。擴展字段所處層次較多,從第3層到第5層都有,但有用信息在第5層,故在解析時要跳過第3和第4兩層。

圖2 證書層次關系圖
其次,在相應層次確定該字段的位置。在X509v3格式數字證書中,同一層次的字段位置相對固定,因此在確定字段所處的層次位置后即可根據相對位置確定字段的最終位置。在字段位置確定后,次序固定字段在定位后可直接取該字段的值;次序不固定字段則需要獲取字段的OID進行逐個比對,直至找到為止。
以DN字段為例:首先,確定其所在的層次是第3層;然后,在第3層中根據相對關系可知DN字段在版本字段之后;從而最終定位DN字段在整個證書中的位置,獲取其值。
前面描述了數字證書中各字段的包裹關系及數字證書的解析方法,為在資源受限環境下快速定位數字證書中各字段位置,高效地解析證書,在數字證書解析模塊中定義了如下數據結構TLV。其中tag表示標簽,暫時不用;length表示pValue指向的數據長度;selfLen表示TLV頭的長度。

同時設計了ReadTLV、WalkCert、SearchExtField等函數。函數ReadTLV用于從二進制數據流中獲取TLV信息。WalkCert函數根據層次和次序關系調用ReadTLV進行解析,跳過不必要的數據,定位所需字段的位置。對于擴展字段,還需要通過SearchExtField進行字段搜索,擴展字段搜索采用OID比對方式進行。在資源受限環境下數字證書解析的關鍵是證書中各字段的定位,下面給出WalkCert的定位方法:


定位到證書中各字段位置后,即可根據字段的定義類型進行相應的處理。在實現Windows和Linux內核層的證書解析時,還應考慮以下幾個方面的問題[7]:
① 部分C庫中的函數在內核中不能使用。如內存分配函數malloc需要根據平臺使用不同的函數,Windows平臺下使用ExAllocatePoolWithTag,Linux平臺下使用kmalloc。
② 缺乏像用戶空間一樣的保護機制。內核可以發現應用層程序非法訪問內存,但如果內核層程序發生內存錯誤,就會導致系統崩潰。
③ 內核層程序可使用的堆棧很小,不能在函數中使用大數組。
④內核層的程序執行效率要高,避免長時間占用CPU資源。
⑤要注意同步和競爭,避免發生死鎖,函數要可重入。
上面所述的方法同樣適用于證書撤銷列表(CRL)的解析。圖3給出了CRL的層次關系。
本文描述的方法已在Windows和Linux內核層、W78E58、TMS320C5416等環境中得到了應用。由于證書解析是純軟件的執行過程,與CPU的外圍電路無關,并且采用C語言實現,移植簡單,因此下文不再描述硬件電路和上文已描述過的實現過程,僅就不同環境中實現時的注意事項進行說明。
(1)Windows內核層
讀取證書文件時要使用InitializeObjectAttributes、ZwCreateFile、ZwReadFile、ZwClose等函數。獲取證書中的時間時需要考慮時區本地化處理,轉換時要使用Rtl-TimeFieldsToTime、ExSystemTimeToLocalTime、RtlTimeToTimeFields等函數。C標準庫中sprintf使用Rtl-StringCbPrintfA替換。動態內存分配使用ExAllocate-PoolWithTag,釋放使用ExFreePool。另外,還要加入unicode和gb2312編碼的轉換數組。
在WDK.6001.18002環境下編譯為內核動態鏈接庫。方式是,在普通內核模塊實現基礎上增加DllInitial-ize、DllUnload兩個函數,均直接返回成功即可,將需要導出的函數聲明為extern。

圖3 CRL層次關系圖
(2)Linux內核層
讀取證書文件時使用filp_open打開文件,get_fs、set_fs(KERNEL_DS)、vfs_read讀取文件,filp_close關閉文件。內核中沒有相應的時間轉換函數,需要參考應用層的代碼自行實現。C標準庫中的sprintf可直接使用。動態內存分配和釋放分別使用kmalloc(xx,GFP_KERNEL)、kfree,另外還要加入unicode和gb2312編碼的轉換數組。
編譯方式同普通內核模塊,用insmod命令加載模塊。
(3)W78E58和TMS320C5416
在W78E58和TMS320C5416環境下,不支持動態內存管理,證書從外部存儲器讀入內存中的全局數組中,運行時各函數使用指針參數傳遞證書數據,不復制緩沖區。臨時數據可使用堆棧保存,本方法的堆棧使用量不超過512字節。時間轉換參考Linux應用層代碼自行實現,在沒有硬件時鐘的環境下該轉換不需要。若設備上不需要顯示證書名稱,則unicode編碼轉換數組也可以省去,以節省空間。
分別在Keil和CCS環境下編譯通過。由于是軟件方法,可以直接在兩種環境中進行模擬測試。
常見的應用層證書解析方法不適用于資源受限環境,無法和本文方法直接比較。此外,證書解析不是一種頻繁的操作,性能不是重要的考核指標。基于上述考慮,表1僅給出了本方法在不同環境下的編譯代碼量和解析公鑰時的平均運行時間(代碼數約1500行)。

表1 編譯代碼量和平均運行時間
本文所描述的數字證書解析方法可方便地利用C語言實現,在Windows和Linux內核層以及嵌入式系統中具有廣泛的適應性,已在Windows和Linux內核層、8051單片機、TMS320C5416等多個環境中應用,具備運行穩定、代碼量小、解析速度快等特點,對開發人員有一定的參考價值。
[1]Cryptography Functions[CP/OL].[2013-01].http://msdn.microsoft.com.
[2]X509Certificate類 [CP/OL].[2013-01].http://msdn.microsof-t.com.
[3]OpenSS 源 碼 [CP/OL].[2013-01].http://www.openssl.org.
[4]譚曉青.利用OpenSSL建立PKI數字證書系統[J].科學技術與工程,2005(20):1552-1554.
[5]IETF.RFC3280Internet X509Public Key Infrastructure Certificate and Certificate Revocation List(CRL)rofile[DB/OL].(2002-04)[2013-01].http://w-ww.ietf.org.
[6]GB/T 16263.1-2006信息技術 ASN.1編碼規則第1部分:基本編碼規則(BER)、正則編碼規則(CER)和非典型編碼規則(DER)規范[S].
[7]譚文,楊瀟,邵堅磊.寒江獨釣:Windows內核安全編程[M].北京:電子工業出版社,2009.