PKCS#11 - 物件與連線

PKCS#11 是一個應用程式介面標準,用於存取硬體安全模組(HSM)或智慧卡等加密裝置。它提供了一個通用的框架,允許應用程式與各種加密裝置互動。

在這篇文章中,我們將深入探討 PKCS#11 中的兩個核心管理功能:Session 管理與 Object 管理。對這些概念的深入理解,對於有效地操作安全模組以及開發安全應用程式至關重要,尤其是在現代分佈式系統和高安全性應用中。

Session 管理

Session 是 PKCS#11 中的核心概念之一。每當應用程式需要與加密裝置進行互動時,必須建立一個 session。這個 session 就像是應用程式和加密裝置之間的安全通道,允許雙方進行資料交換並且提供適當的隔離和保護。Session 可以分為只讀讀寫兩種模式,這些模式影響到應用程式能進行的操作類型。建立一個 session 不僅是為了進行簡單的資料傳輸,更是為了在加密操作中設定上下文,確保所有的操作都能遵循正確的安全協定並維持資料的機密性和完整性。

在 PKCS#11 中,常用的 Session 管理函數包括:

  • C_OpenSession():此函數用於開啟一個新的 session,使應用程式可以與加密裝置互動。該函數的參數可以指定 session 的屬性,例如是否允許多重操作(即同一時間能進行多個不同的操作),或者是否允許讀寫操作。這些參數可以根據不同的使用場景進行調整,以最佳化性能和安全性。
  • C_CloseSession():用於關閉指定的 session,並釋放相關資源。在完成加密操作後,及時關閉 session 是非常重要的,這樣可以防止不必要的資源佔用,並降低安全風險。如果 session 長時間保持開啟,則可能會成為攻擊者的攻擊目標,進而威脅到系統的安全性。
  • C_CloseAllSessions():這個函數用於關閉所有與指定 token 相關的 session。在完成所有需要的操作後,使用這個函數可以確保不會遺留任何開放的 session,從而有效降低管理和安全的複雜性。
  • C_Login()C_Logout():用於進行使用者身份驗證。例如,C_Login() 允許應用程式獲取更高的權限來操作敏感資料,而 C_Logout() 則可以確保在操作結束後將權限回收,以防止未經授權的存取。這些函數確保了敏感操作只能在合法使用者經過適當身份驗證後進行。

以下是一個開啟 session 並進行使用者登入的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CK_SESSION_HANDLE GetUserSession(CK_FUNCTION_LIST_PTR f, CK_UTF8CHAR_PTR pPIN, CK_ULONG ulPinLen) {
CK_RV rv = CKR_GENERAL_ERROR;
CK_SESSION_HANDLE hSession = 0;
CK_SLOT_ID pSlotList[10] = {0};
CK_ULONG ulSlotCount = 10;

if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
printf("C_Initialize fail %X\n", rv);
return 0;
} else {
printf("C_Initialize OK\n");
}

if ((rv = f->C_GetSlotList(TRUE, pSlotList, &ulSlotCount)) != CKR_OK) {
printf("C_GetSlotList fail %X\n", rv);
return 0;
} else {
printf("C_GetSlotList ulSlotCount %d\n", (int)ulSlotCount);
}

if ((rv = f->C_OpenSession(pSlotList[0], CKF_RW_SESSION | CKF_SERIAL_SESSION, 0, 0, &hSession)) != CKR_OK) {
printf("C_OpenSession fail %X\n", rv);
return 0;
} else {
printf("C_OpenSession OK\n");
}

if ((rv = f->C_Login(hSession, CKU_USER, pPIN, ulPinLen)) != CKR_OK) {
printf("C_Login fail %X\n", rv);
return 0;
} else {
printf("C_Login OK\n");
}

return hSession;
}

這個範例展示了如何初始化庫、打開 session 並進行使用者登入操作。C_OpenSession() 用於開啟與加密裝置的通道,而 C_Login() 則負責驗證使用者身份,以確保後續的所有操作都在合法的上下文中進行。這些操作不僅保護了資料的機密性,還確保了應用程式與加密裝置之間的所有通訊都是受控的。PKCS#11 Logo

Session 管理流程圖

為了更好地理解 Session 管理的流程,我們可以用以下 PlantUML 表示該流程:

Session Management

此流程圖展示了從初始化庫到成功開啟 session 並登入的整個過程,以及在各種操作失敗時的處理方式。

Object 管理

在 PKCS#11 中,Object 是指儲存在加密裝置中的各種資料,例如金鑰、憑證和資料物件。這些物件通常是需要進行加密、解密、簽章、驗證等操作的核心資料。對 Object 的管理是 PKCS#11 的另一個關鍵部分,因為它們包含了所有敏感的加密資訊,必須妥善管理以確保其安全性。

常見的 Object 管理函數包括:

  • C_FindObjectsInit()C_FindObjects()C_FindObjectsFinal():這組函數用於搜尋符合特定屬性的物件,例如具有特定標籤的簽章私鑰。對於管理大量物件的情況,這些函數非常有用,可以幫助開發者快速而精確地定位特定的金鑰或資料。這些函數的協同作用使得應用程式能夠有效地篩選出需要的物件,進而提升系統的整體效率。
  • C_GetAttributeValue()C_SetAttributeValue():這些函數用於讀取或設定物件的屬性。例如,可以使用 C_GetAttributeValue() 來查詢金鑰的屬性(如是否可被導出),或者使用 C_SetAttributeValue() 更新某些屬性值以符合應用程式的需求。這些操作為開發者提供了靈活性,使其能夠根據實際需求調整物件的屬性,確保加密策略符合業務規範。
  • C_CreateObject()C_DestroyObject():用於創建和刪除物件。C_CreateObject() 允許應用程式在需要時動態地創建新物件,例如生成一對新的加密金鑰。而 C_DestroyObject() 則能確保在物件不再需要時,將其安全地從加密裝置中刪除,防止敏感資料遺留而導致潛在的安全問題。

以下是一個用於尋找特定標籤的私鑰物件的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
CK_OBJECT_HANDLE FindSigningKeyHandle(CK_FUNCTION_LIST_PTR f, CK_SESSION_HANDLE hSession, LPCSTR pLabel) {
CK_RV rv = CKR_GENERAL_ERROR;
CK_OBJECT_CLASS ulClass = CKO_PRIVATE_KEY;
CK_ATTRIBUTE ObjAttr[] = {
{CKA_CLASS, &ulClass, sizeof(CK_OBJECT_CLASS)},
{CKA_TOKEN, &bTrue, sizeof(CK_BBOOL)},
{CKA_PRIVATE, &bTrue, sizeof(CK_BBOOL)},
{CKA_LABEL, (CK_VOID_PTR)pLabel, (CK_ULONG)strlen(pLabel)},
{CKA_SIGN, &bTrue, sizeof(CK_BBOOL)}
};
CK_OBJECT_HANDLE hObjects[10] = {0};
CK_ULONG ulObjectCount = 10;

if ((rv = f->C_FindObjectsInit(hSession, ObjAttr, sizeof(ObjAttr) / sizeof(CK_ATTRIBUTE))) != CKR_OK) {
printf("C_FindObjectsInit fail %x\n", rv);
return 0;
} else {
printf("C_FindObjectsInit OK\n");
}

if ((rv = f->C_FindObjects(hSession, hObjects, 10, &ulObjectCount)) != CKR_OK) {
printf("C_FindObjects fail %x\n", rv);
return 0;
} else {
printf("C_FindObjects OK\n");
}

if ((rv = f->C_FindObjectsFinal(hSession)) != CKR_OK) {
printf("C_FindObjectsFinal fail %x\n", rv);
return 0;
} else {
printf("C_FindObjectsFinal OK\n");
}

printf("找到 %d 個物件,標籤為 [%s]\n", ulObjectCount, pLabel);
return (ulObjectCount) ? hObjects[0] : 0;
}

這段程式碼展示了如何根據物件標籤來搜尋私鑰。C_FindObjectsInit()C_FindObjects()C_FindObjectsFinal() 這三個函數分別負責初始化搜尋、執行搜尋和結束搜尋。這些操作對於加密應用程式而言非常重要,因為在進行簽章、驗證等操作之前,找到正確的金鑰是至關重要的步驟。

此外,在 PKCS#11 中,物件的屬性也必須嚴格管理。例如,CKA_PRIVATE 屬性控制物件是否為私有,僅限授權的使用者才能存取。CKA_TOKEN 則決定物件是否永久儲存在硬體安全模組中,這對於保證敏感資料的持久性和安全性至關重要。這些屬性使開發者可以根據應用需求,精確地控制物件的存取權限和使用範圍,從而有效降低安全風險。

Object 管理流程圖

為了更好地理解 Object 管理的過程,以下是使用 PlantUML 表示的 Object 搜尋流程:

Object 搜尋流程

此流程圖展示了如何初始化物件搜尋、執行搜尋以及在成功找到物件後進行操作的完整流程。

總結

在 PKCS#11 中,Session 管理和 Object 管理是不可或缺的部分。通過掌握這些管理功能,開發者可以更加靈活地控制與加密裝置的互動,並有效管理金鑰和其他敏感資料。Session 管理確保了應用程式與加密裝置之間的通訊安全,而 Object 管理則負責控制所有敏感資料的存取和操作,這兩者相輔相成,保障了系統的整體安全性。

這些功能的結合使得 PKCS#11 成為開發安全應用程式的強大工具。不論是保護資料的完整性,還是確保加密金鑰的安全,這些管理功能都能幫助開發者構建更安全、更可靠的系統。PKCS#11 提供了標準化的方法,將各種不同的硬體安全模組統一到同一套 API 下,極大地簡化了開發和整合工作。

希望這篇文章能幫助你更深入地理解 PKCS#11 的核心概念,並能夠運用這些知識來開發更安全的應用程式。如果你對某些部分有疑問或想要深入了解,歡迎留言討論!

也許你也會想看看