密碼引擎-加密API研究

20211428谷丰宇發表於2024-04-13

任務詳細

密碼引擎API的主要標準和規範包括:
1 微軟的Crypto API
2 RAS公司的PKCS#11標準
3 中國商用密碼標準:GMT 0016-2012 智慧密碼鑰匙密碼應用介面規範,GMT 0018-2012密碼裝置應用介面規範等

研究以上API介面,總結他們的異同,並以龍脈GM3000Key為例,寫出呼叫不同介面的程式碼,提交部落格連結和程式碼連結。
內容:
0 查詢各種標準的原始文件,研究學習(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)(5分)
1 總結這些API在程式設計中的使用方式(5分)
2 列出這些API包含的函式,進行分類,並總結它們的異同(10分)
3 以龍脈GM3000Key為例,寫出呼叫不同介面的程式碼(Crypto API,PKCS#11,SKF介面),把執行截圖加入部落格,並提供程式碼連結(10分)

研究學習原始文件

CryptoAPI

  • Cryptography
  • CryptoAPI System Architecture
  • Cryptographic Provider Types
  • Microsoft Cryptographic Service Providers
  • Cryptography API: Next Generation
  • wincrypt.h header

PKCS#11

  • PKCS#11 v2.20

GM/T 0016-2012 智慧密碼鑰匙密碼應用介面規範

  • 密碼行業標準列表

GM/T 0018-2012 密碼裝置應用介面規範

  • [http://www.gmbz.org.cn/main/viewfile/20180110020642562680.html]

總結這些API在程式設計中的使用方式

CryptoAPI

微軟的CryptoAPI是PKI推薦使用的加密 API。其功能是為應用程式開發者提供在Win32環境下使用加密、驗證等安全服務時的標準加密介面。CryptoAPI處於應用程式和CSP(cryptographic service provider)之間。

CryptoAPI的程式設計模型同Windows系統的圖形裝置介面 GDI比較類似,其中加密服務提供者CSP等同於圖形裝置驅動程式 ,加密硬體(可選)等同於圖形硬體,其上層的應用程式也類似,都不需要同裝置驅動程式和硬體直接打交道。

CryptoAPI共有五部分組成:簡單訊息函式(Simplified Message Functions)、低層訊息函式(Low-level Message Functions)、基本加密函式(Base Cryptographic Functions)、證書編解碼函式(Certificate Encode/Decode Functions)和證書庫管理函式(Certificate Store Functions)。其中前三者可用於對敏感資訊進行加密或簽名處理,可保證網路傳輸信心的私有性;後兩者透過對證書的使用,可保證網路資訊交流中的認證性。

  • 金鑰管理
    • 在CryptoAPI中,支援兩種型別的金鑰:會話金鑰、公私鑰對。會話金鑰也稱為對稱金鑰,用於對稱金鑰演算法。為了保證金鑰的安全性,在CryptoAPI中,這些金鑰都儲存在CSP內部,使用者可以透過CryptExportKey以加密金鑰形式匯出。公私鑰用於非對稱加密演算法。非對稱加密演算法主要用於加解密會話金鑰和數字簽名。在CryptoAPI中,一般來說,大多數CSP產生的金鑰容器包含兩對金鑰對,一對用於加密會話金鑰,稱為交換金鑰對,一對用於產生數字簽名,稱為簽名金鑰對。在CryptoAPI中所有的金鑰都儲存在CSP中,CSP負責金鑰的建立,銷燬,匯入匯出等操作。
  • 資料編解碼
    • CryptoAPI採用的編碼方式為ASN.1,編碼規則為DER,表示傳送資料時先把資料抽象為ASN.1物件,然後使用DER編碼規則把ASN.1物件轉化為可傳輸的0,1串;接收方接收到資料後,利用DER解碼規則把0,1串轉化為ASN.1物件,然後把ASN.1物件轉化為具體應用支援的資料物件。
  • 資料加解密
    • 在CryptoAPI中約定加密較大資料塊時,採用對稱金鑰演算法。透過其封裝好的加解密函式來實現資料加解密操作。
  • 雜湊和數字簽名
    • 雜湊和數字簽名一般用於資料的完整性校驗和身份鑑別。CryptoAPI中,透過其封裝好的雜湊與數字簽名函式來實現相關操作。微軟公司提供的CSP產生的數字簽名遵循RSA標準(PKCS#6)
  • 數字證書管理
    • 數字證書主要用於安全通訊中的身份鑑別。CryptoAPI中,對數字證書的使用管理函式分為證書與證書庫函式、證書驗證函式兩大部分。

PKCS#11

  • 我們把Cryptoki 確定為應用程式與各種各樣的行動式密碼裝置(基於智慧卡、PCMCIA卡以及智慧軟盤)間的一種介面。
  • Cryptoki 的主要目標是一個低階程式介面,該介面將裝置的細節抽象化,並把密碼裝置的通用模型—密碼令牌,或簡稱令牌—提供給應用程式。
  • Cryptoki的通用模型如下圖所示。模型從一個或多個必須執行某些密碼操作的應用程式開始,以一個或多個密碼裝置結束(在密碼裝置上執行某些或全部操作)。一個使用者可涉及也可不涉及一個程式。
  • Cryptoki 為一個或多個密碼裝置提供一個介面,這些裝置透過大量的槽在系統中執行。每個對應於一個物理閱讀器或另一個裝置介面的槽可包含一個令牌。當一臺密碼裝置存在於閱讀器中,一個令牌就存在於該槽中。當然,由於Cryptoki提供槽和令牌的邏輯檢視,所以可能有其它的物理譯碼。多個槽可能共享一個閱讀器。問題在於一個系統有相當多的槽,應用程式能連線到這些槽的其中任何一個或全部槽的令牌上。
  • Cryptoki的令牌邏輯檢視是一個能儲存物件和能執行密碼函式的裝置。Cryptoki定義如下三個物件:資料、證書和金鑰。資料物件由應用程式定義。一個證書物件儲存一個證書。一個金鑰物件儲存一個密碼金鑰。金鑰可以是一個公共金鑰、一個私鑰或是一個保密金鑰,每個種類的金鑰在專用機制中使用其的輔助型。令牌的這種邏輯檢視如下圖所示:

SKF

針對支援國密演算法USB KEY裝置的應用,我國頒佈一個行業標準《智慧密碼鑰匙應用介面規範》(GM/T0016-2012),市面上銷售的國密演算法的USB KEY裝置必須支援這個介面規範。因此,只要根據這個規範開發的應用程式,就可以相容使用不同廠家及品牌的USB KEY產品。由於此規範中函式名稱都以SKF開頭,所以我們一般把按照此規範提供的裝置開發介面庫叫做SKF庫或SKF介面。
智慧密碼鑰匙密碼應用介面位於智慧密碼鑰匙應用程式與裝置之間,如下圖所示

一個裝置中存在裝置認證金鑰和多個應用,應用之間相互獨立。裝置的邏輯結構如下圖所示

應用由管理員PIN,使用者PIN、檔案和容器組成,可以存在多個檔案和多個容器。每個應用維護各自的與管理員PIN和使用者PIN相關的許可權狀態。
一個應用的邏輯結構如下圖所示

容器中存放加密金鑰對、簽名金鑰對和會話金鑰。其中加密金鑰對用於保護會話金鑰,簽名金鑰對用於數字簽名和驗證,會話金鑰用於資料加解密和MAC運算。容器中也可以存放與加密金鑰對對應的加密數字證書和與簽名金鑰對對應的簽名數字證書。其中,簽名金鑰對由內部產生,加密金鑰對由外部產生並安全匯入,會話金鑰可由內部產生或者由外部產生並安全匯入。

列出這些API包含的函式,進行分類,並總結它們的異同

CryptoAPI

密碼服務提供者CSP函式
CryptoAPI的密碼服務提供者函式主要包括6個函式。連線或斷開CSP函式CryptAcquireContext、CryptReleaseContext,列舉CSP函式CryptEnumProviders,獲得或設定預設CSP函式CryptGetDefaultProvider、CryptSetProvider,獲取或設定CSP引數函式CryptGetProvParam、CryptSetProvParam。

連線CSP函式 CryptAcquireContext

函式功能:連線CSP,獲得指定CSP的金鑰容器的控制代碼。

列舉CSP函式 CryptEnumProviders
函式功能:列舉計算機上的所有CSP。此函式可以得到第一個或下一個可用的CSP。如果迴圈呼叫可以得到計算機上所有可用的CSP。

獲得預設CSP函式 CryptGetDefaultProvider

函式功能:獲得系統預設的CSP。

設定預設CSP函式 CryptSetProvider

函式功能:設定系統預設的CSP。

獲得CSP引數屬性函式 CryptGetProvParam

函式功能:獲得CSP各種引數屬性。

設定CSP引數函式 CryptSetProvParam

函式功能:設定CSP各種引數屬性。

斷開CSP函式 CryptReleaseContext
函式功能:斷開CSP,釋放CSP控制代碼,和 CryptAcquireContext相對應。

示例列舉CSP並獲得預設CSP的引數處理過程如下圖所示

金鑰的產生與交換函式
CryptoAPI金鑰產生和交換函式主要有生成金鑰函式 CryptGenKey、派生金鑰函式CryptDeriveKey、銷燬金鑰函式CryptDestoryKey、複製金鑰函式CryptDuplicateKey、匯出金鑰函式CryptExportKey、匯入金鑰函式CryptImportKey、獲得金鑰引數函式CryptGetKeyParam、設定金鑰引數函式CryptSetKeyParam、產生隨機函式 CryptGenRandom。

生成函式 CryptGenKey
函式功能:產生一個隨機的對稱或非對稱演算法的金鑰。

派生金鑰函式 CryptDeriveKey
函式功能:根據基礎資料派生一對稱金鑰(會話金鑰)。

銷燬金鑰函式 CryptDestroyKey

函式功能:銷燬金鑰。

複製金鑰函式 CryptDuplicateKey
函式功能:複製一個金鑰。產生一個金鑰的複製,包括其狀態。

匯出金鑰函式 CryptExportKey

函式功能:從CSP匯出金鑰或金鑰對。

匯入金鑰函式 CryptlmportKey
函式功能:把BLOB資料匯入的CSP。該函式可以匯入會話金鑰、公鑰、或者公/私鑰對。

獲得金鑰引數函式 CryptGetKeyParam

函式功能:獲得key控制代碼的各項引數。

獲得金鑰引數函式 CryptSetKeyParam

函式功能:設定key控制代碼的各項引數。

獲得金鑰引數函式 CryptGenRandom

函式功能:生成隨機數。

示例金鑰產生和交換例項處理過程如下圖所示

資料的加密與解密函式
CryptoAPI利用CryptEncrypt函式實現資料加密,利用CryptDecrypt實現資料解密。呼叫這2個函式前必須指定一個金鑰,這個金鑰可以由CryptGenKey、CryptDeriveKey或CryptImportKey產生。也可用CryptSetKeyParam函式指定額外的加密引數。

資料加密函式 CryptEncrypt
函式功能:使用hKey指定的金鑰和演算法加密資料。
資料解密函式 CryptDecrypt
函式功能:使用hKey指定的金鑰和演算法對加密資料解密。
示例資料加密處理過程如下圖所示

示例資料解密處理過程如下圖所示

雜湊和數字簽名函式

CryptoAPI提供的雜湊和數字簽名函式包括建立雜湊函式CryptCreateHash、銷燬雜湊CryptDestroyHash、複製雜湊函式CryptDuplicateHash、獲得雜湊引數函式CryptGetHashParam,設定雜湊引數函式CryptSetHashParam、雜湊會話金鑰函式 CryptHashSessionKey、雜湊資料函式CryptHashData、對雜湊簽名函式CryptSignHash和對雜湊驗證簽名函式CryptVerifySignature。

建立雜湊函式 CryptCreateHash

函式功能:建立雜湊。

銷燬雜湊 CryptDestroyHash

函式功能:銷燬雜湊物件。

複製雜湊函式 CryptDuplicateHash

函式功能:複製一個雜湊物件。

獲得雜湊引數函式 CryptGetHashParam

函式功能:獲得雜湊物件的引數。

設定雜湊引數函式 CryptSetHashParam

函式功能:設定雜湊物件的引數。

雜湊會話金鑰函式 CryptHashSessionKey
函式功能:對一個會話金鑰進行雜湊,把它加到指定的雜湊物件中。

雜湊資料函式 CryptHashData
函式功能:對資料進行雜湊操作,此函式可以反覆呼叫。

對雜湊簽名函式 CryptSignHash

函式功能:對雜湊物件進行簽名。

對雜湊驗證簽名函式 CryptVerifySignature

函式功能:驗證雜湊簽名。

示例對資料簽名和驗證的流程如下圖所示

證書和證書庫函式

CryptoAPI證書和證書庫函式主要包括開啟證書庫函式CertOpenStore、關閉證書庫函式CertCloseStore、從證書庫列舉證書函式CertEnumCertificatesInStore、從證書庫查詢證書函式CertFindCertificateInStore、建立證書控制代碼函式 CertCreateCertificateContext、釋放證書控制代碼函式CertFreeCertificateContext、獲得證書控制代碼屬性函式CertGetCertificateContextProperty、設定證書控制代碼屬性函式CertSetCertificateContextProperty和獲得證書主題名稱函式CertGetNameString。

開啟證書庫函式 CertOpenStore

函式功能:根據證書庫型別,開啟證書庫。

關閉證書庫函式 CertCloseStore

函式功能:關閉證書庫。

從證書庫列舉證書函式 CertEnumCertificateslnStore
函式功能:列舉證書庫中的證書。該函式透過迴圈呼叫,可以列舉證書庫內的全部證書,上一次的返回,是下一次的pPrevCertContext,直到返回值為NULL。

從證書庫查詢證書函式 CertFindCertificatelnStore

函式功能:從證書庫中查詢指定的證書。

建立證書控制代碼函式 CertCreateCertificateContext

函式功能:由證書資料建立證書控制代碼。

釋放證書控制代碼函式 CertFreeCertificateContext

函式功能:釋放證書控制代碼。

獲得證書控制代碼屬性函式 CertGetCertificateContextProperty

函式功能:獲得證書控制代碼屬性。

設定證書控制代碼屬性函式 CertSetCertificateContextProperty

函式功能:設定證書控制代碼屬性。

獲得證書主題名稱函式 CertGetNameString

函式功能:從證書中獲得主題或頒發者的名稱。

示例列舉證書庫並輸出其屬性的處理流程如下圖所示

SKF

裝置管理函式

訪問控制函式

應用管理函式

檔案管理函式

容器管理函式

密碼服務函式


得到國密證書

根據國密標準,一種裝置型別可以有多個裝置(Device),每一個裝置內可以有多個應用(Application),每一個應用裡可以有多個容器(Container),每個容器裡可以有一對證書(Certificate):簽名證書和加密證書。

因此,應按照下列順序依次呼叫相關介面:
1.SKF_EnumDev(BOOL bPresent, LPSTR szNameList, ULONG *pulSize);
呼叫這個方法用來遍歷當前電腦上的裝置,這個方法的第一個引數一般傳TRUE,表示遍歷的是插上的裝置;第二個引數就是返回的裝置名稱列表;第三個引數是裝置名稱列表緩衝區長度。按照慣例,這個方法應該被呼叫兩次:第一次szNameList傳NULL,pulSize返回長度;第二次給szNameList分配pulSize長度,返回裝置列表,每個裝置的名稱以單個’\0’結束,以雙’\0’表示列表的結束。
2.SKF_ConnectDev(LPSTR szName, DEVHANDLE *phDev);
透過迴圈呼叫這個方法用來連線每一個具體的裝置,szName為裝置名,即上面方法得到的列表中的裝置名;返回phDev為裝置控制代碼。
3.SKF_EnumApplication(DEVHANDLE hDev, LPSTR szAppNameList,ULONG *pulSize);
得到裝置控制代碼後,再透過此方法列舉得到裝置裡的應用列表。hDev為連線裝置時返回的裝置控制代碼;szAppNameList返回應用名稱列表;pulSize是列表緩衝區長度。這個方法也是照例要呼叫兩次,不再贅述。同樣,每個應用的名稱以單個’\0’結束,以雙’\0’表示列表的結束。
4.SKF_OpenApplication(DEVHANDLE hDev, LPSTR szAppName, HAPPLICATION phApplication);
透過迴圈呼叫此方法開啟應用列表裡的每一個應用,hDev為連線裝置時返回的裝置控制代碼;szAppName是要開啟的應用名稱;phApplication為返回的應用控制代碼。
5.SKF_EnumContainer(IN HAPPLICATIONhApplication, OUT LPSTRszContainerNameList, OUT ULONG
pulSize)
拿到應用控制代碼後,我們就可以用此方法遍歷應用中的所有容器了。hApplication是應用控制代碼;szContainerNameList是返回的容器名稱列表;pulSize是列表長度;後面的就不用了多說了。
6.SKF_OpenContainer(HAPPLICATION hApplication,LPSTR szContainerName,HCONTAINER phContainer);
迴圈呼叫此方法開啟每一個容器。hApplication是應用控制代碼;szContainerName是要開啟的容器名稱;phContainer是返回的容器控制代碼。
7.SKF_ExportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE
pbCert, ULONG *pulCertLen);
最後就可以透過這個方法取得每個容器裡的證書。hContainer是容器控制代碼;bSignFlag為匯出的證書型別; TRUE表示匯出的是簽名證書;FALSE表示匯出加密證書。pbCert為返回的證書資料,pulCertLen是證書資料的長度。同樣需兩次呼叫。

數字簽名

在數字簽名時,要指定簽名所使用的證書。透過遍歷本機上的證書,與簽名用的證書進行對比,定位到簽名證書在USBKEY中的位置,得到裝置、應用和容器的控制代碼,然後使用證書的私鑰進行簽名。遍歷對比的過程可參見上一節的內容。另外,由於數字簽名會用到私鑰,因此這裡需要驗證口令。

1.SKF_VerifyPIN(HAPPLICATION hApplication, ULONG ulPINType, LPSTR szPIN, ULONG pulRetryCount);
此方法用來驗證證書所在應用的PIN碼,及上面說的口令,為後面的簽名取得許可權。hApplication是應用控制代碼;ulPINType是PIN型別,可以為0是管理員賬戶,1為普通使用者,這個引數一般選擇1。szPIN值是PIN碼,pulRetryCount為出錯後返回的重試次數。
2.SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE
pbBlob, ULONG* pulBlobLen);
這個方法用來匯出容器中的簽名公鑰,hContainer為證書所在容器控制代碼;bSignFlag 為匯出金鑰型別,TRUE表示匯出簽名公鑰,FALSE表示匯出加密公鑰,這裡選擇TRUE;pbBlob為返回公鑰的資料;pulBlobLen為資料的長度。這裡這個方法可以不用呼叫兩次,因為公鑰結構是已知的,其長度也是固定的,因此可以直接為pbBlob分配固定長度的資料,以返回公鑰。
3.SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);
此方法進行雜湊(國密標準裡把摘要稱之為雜湊)運算初始化,並指定計算訊息雜湊的演算法。hDev為裝置控制代碼;ulAlgID是雜湊演算法標識,這裡選擇SGD_SM3(0x00000001),表明使用SM3演算法;pPubKey為簽名用證書公鑰資料;pucID為簽名者的ID值;ulIDLen是簽名者的ID值的長度;phHash為返回的雜湊物件控制代碼。加入簽名者ID值是SM2數字簽名的一個重要特徵,預設使用"1234567812345678"這個字串值。
4.SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG *pulHashLen);
初始化後,呼叫此方法進行資料雜湊運算。hHash是SKF_DigestInit方法返回的雜湊物件控制代碼; pbData為產生簽名的原文,ulDataLen是原文資料的長度,pbHashData返回雜湊資料; pulHashLen返回雜湊結果的長度。同樣,因為雜湊資料的長度都是固定的,這裡同樣可以為pbHashData事先分配固定長度,而不用再呼叫兩遍。
注意,如果進行雜湊的資料是分組的,那就得使用下面兩個方法:
SKF_DigestUpdate(HANDLE hHash, BYTE *pbData, ULONG ulDataLen);
SKF_DigestFinal(HANDLE hHash, BYTE *pHashData, ULONG *pulHashLen);
對每一組資料都使用SKF_DigestUpdate,最後呼叫SKF_DigestFinal返回雜湊值。當然,在數字簽名運算中不存在分塊計算簽名的情況,所以這裡也不會把資料分塊雜湊。
5.SKF_ECCSignData(HCONTAINER hContainer, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);
最後呼叫此方法進行數字簽名。hContainer用來簽名的私鑰所在容器控制代碼,也就是遍歷對比證書得到的容器控制代碼;pbData是被簽名的資料;ulDataLen是被簽名資料長度,必須小於金鑰模長; pbSignature為返回的簽名值。

驗證簽名

1.SKF_CreateContainer(HAPPLICATION hApplication, LPSTR szContainerName, HCONTAINER phContainer)
呼叫此方法建立一個臨時容器。hApplication為容器所在的應用控制代碼;szContainerName是ASCII字串,表示所建立容器的名稱,最大長度不能超過64位元組;phContainer是返回所建立容器的容器控制代碼。
2.SKF_ImportCertificate(HCONTAINER hContainer, BOOL bSignFlag, BYTE
pbCert, ULONG ulCertLen);
將簽名用的證書匯入到容器中。hContainer為容器控制代碼,即用上一方法建立的臨時容器;bSignFlag為證書型別,TRUE表示簽名證書,FALSE表示加密證書,這裡選TRUE;pbCert,是證書資料;ulCertLen為證書資料長度;
3.SKF_ExportPublicKey(HCONTAINER hContainer, BOOL bSignFlag, BYTE* pbBlob, ULONG* pulBlobLen);
匯出公鑰。
4.SKF_DigestInit(DEVHANDLE hDev, ULONG ulAlgID, ECCPUBLICKEYBLOB *pPubKey, unsigned char *pucID, ULONG ulIDLen, HANDLE *phHash);
雜湊初始化。
5.SKF_Digest(HANDLE hHash, BYTE *pbData, ULONG ulDataLen, BYTE *pbHashData, ULONG pulHashLen);
雜湊運算。
6.SKF_ECCVerify(DEVHANDLE hDev , ECCPUBLICKEYBLOB
pECCPubKeyBlob, BYTE *pbData, ULONG ulDataLen, PECCSIGNATUREBLOB pSignature);
進行簽名驗證。hDev是裝置控制代碼;pECCPubKeyBlob是公鑰資料結構,即第3步得到的公鑰; pbData為待驗證簽名的資料;ulDataLen是待驗證簽名資料長度;pbSignature待驗證的簽名值。
7.SKF_DeleteContainer(HAPPLICATION hApplication, LPSTR szContainerName);
刪除臨時容器。hApplication為容器所在的應用控制代碼;szContainerName為容器名稱。

3.以龍脈GM3000Key為例,寫出呼叫不同介面的程式碼(Crypto API,PKCS#11,SKF介面),把執行截圖加入部落格,並提供程式碼連結

SKF介面

  • 龍脈密碼鑰匙驅動例項工具等\mToken-GM3000\skf\samples\windows\EncryptData\EncryptData.sln

Crypto API

  • 龍脈密碼鑰匙驅動例項工具等\mToken-GM3000\csp\samples\CryptAPI\VC\EncryptDecryptFile\EncryptFile.sln
    在當前目錄下先建立 20211428.txt,內容為20211428gfy


PKCS#11

  • 龍脈密碼鑰匙驅動例項工具等\mToken-GM3000\pkcs11\windows\samples\PKCStest\PKCStest.sln
    • DES
    • DES3
    • RC2
    • RC4
    • RSA
    • AES

相關文章