Dot-Net

所有不同的 X509KeyStorageFlags 的基本原理是什麼?

  • October 10, 2018

今天,一位同事又遇到了與這些相關的另一個錯誤!過去我發現這些標誌真的很令人沮喪,因為如果您在實例化 X509Certificate2 對象、導出它們或將它們保存在 X509Store 中時稍有錯誤,您可能會遇到各種奇怪的錯誤,例如:

  • 出乎意料地無法告訴 NETSH.exe 或 ASP.net [通過其指紋] 使用某個 SSL 證書,即使您的機器儲存中有該證書
  • 出乎意料的是,您可以導出證書數據,但是使用 .Export() 在沒有私鑰的情況下導出了它
  • 意外地,您的單元測試在較新的 Windows 版本上開始失敗,顯然是因為您沒有使用正確的標誌

是的,它們都被記錄在案了(而且一些文件幾乎似乎是有道理的),但是為什麼它必須這麼複雜呢?

主要是今天必須這麼複雜,因為昨天這麼複雜,沒有人想出更簡單的方法。

我無法在這裡提出線性敘述,所以請忍受需要的來回編織。

什麼是 PFX/PKCS#12 文件?

雖然我不能完全說明 PFX 的起源是什麼,但在 Windows 函式的名稱中有一條線索可以讀取和寫入它們:PFXImportCertStorePFXExportCertStore。它們包含許多可以使用屬性標識符相互關聯的獨立實體(證書、私鑰和其他東西)。它們似乎被設計為整個證書儲存的導出/導入機制,就像所有 CurrentUser\My. 但由於一種儲存是“記憶體儲存”(任意集合),.NET 導入/導出是有意義的,但會出現一些複雜情況(從 .NET 之前開始)。

Windows 私鑰

Windows 支持許多不同位置的私鑰,但對於舊版加密 API,它們歸結為 4 部分定址方案:

  • 加密提供者的名稱
  • 密鑰容器的名稱
  • 這是機器相關密鑰還是使用者相關密鑰的標識符
  • 這是一個“簽名”密鑰還是“交換”密鑰的標識符。

這被簡化為 CNG 的 3 部分方案:

  • 儲存引擎的名稱
  • 鑰匙的名字
  • 這是機器相關密鑰還是使用者相關密鑰的標識符。

為什麼需要機器或非機器標識符?

CAPI 和 CNG 都支持直接與命名鍵互動。因此,您創建了一個名為“EmailDecryption”的密鑰。系統上的另一個使用者創建了一個同名的密鑰。那應該工作嗎?我們可能會。所以,huzzah,確實如此!單獨的鍵,因為它們保存在與創建它們的使用者相關的上下文中。

但是現在您需要一個可供多個使用者使用的密鑰。這不是您通常想要的東西,所以它不是預設設置。這是一個選擇。CRYPT_MACHINE_KEYSET國旗誕生了。

我將繼續在這裡說,我聽說現在不鼓勵直接使用命名鍵;CAPI/CNG 團隊更喜歡以 GUID 命名的密鑰,並且您可以通過證書儲存與它們進行互動。但這是進化的一部分。

導入 PFX 有什麼作用?

PFXImportCertStore 將所有證書從 PFX 複製到提供的儲存中。它還導入(CryptImportKeyBCryptImportKey,取決於它認為需要什麼)。然後,對於它導入的每個密鑰,它會(通過 PFX 中的屬性值)找到匹配的證書,並在證書儲存表示上設置一個屬性為“這是我的 4 部分標識符”(CNG 密鑰只是設置了第 4 個部分為 0); 這實際上是證書對其私鑰的全部了解。

(PFX 是一種非常複雜的文件格式,如果沒有使用任何“奇怪的部分”,則此描述是正確的)

密鑰壽命

Windows 私鑰永遠存在,或者直到有人刪除它們。

因此,當 PFX 導入它們時,它們將永遠存在。如果您要導入 CurrentUser\My,這是有道理的。如果您正在做一些暫時的事情,那就沒有意義了。

.NET 顛倒關係 / 讓它“太容易”

Windows 設計(主要)是您與證書儲存互動,並從證書儲存中獲取證書。.NET 後來出現了,並且(根據了解應用程序真正在做什麼來推測)使證書成為頂級對象,並儲存某種次要的東西。

因為 Windows 證書(實際上是“儲存證書元素”)“知道”它們的私鑰是什麼,所以 .NET 證書“知道”它們的私鑰是什麼。

哦,但是 MMC 證書管理器說它可以使用其私鑰導出證書(到 PFX 中),為什麼證書建構子不能接受除了“只是一個證書”格式之外的那些字節?好的,現在可以了。

調和一生

您打開一些字節作為 X509Certificate/X509Certificate2。這是一個“無密碼”的 PFX(通過任何可能是真的方式)。您發現它是錯誤的,然後您將證書交給了垃圾收集器。該私鑰永遠存在,因此您的硬碟驅動器會慢慢填滿,並且密鑰儲存訪問變得越來越慢。然後你生氣了,重新格式化你的電腦。

這看起來很糟糕,所以 .NET 所做的是,當證書的(一個欄位)被垃圾收集(實際上是最終確定的)時,它會告訴 CAPI(或 CNG)刪除密鑰。現在事情按預期工作,對吧?好吧,只要程序沒有異常終止。

哦,您將其添加到持久儲存中?但是在新的證書儲存實體“知道”如何找到私鑰之後,我將刪除私鑰。這似乎很糟糕。

進入X509KeyStorageFlags.PersistKeySet

PersistKeySet 說“不要做那個刪除的事情”。當您打算將證書添加到 X509Store 時。

如果您希望在不指定標誌的情況下獲得相同的行為,請在導入後呼叫Environment.FailFast或拔下電腦。

關於那個機器或使用者位

在 .NET 中,您可以輕鬆地在集合中獲取一袋證書並呼叫Export它。如果有些人有機器密鑰,而另一些人有使用者密鑰怎麼辦?PFXExportCertStore 進行救援。導出機器密鑰時,它會記下一個標識符,表明它是機器密鑰,因此導入會將其放回同一位置。

嗯,通常。也許您從一台機器上導出了機器密鑰,而您只想在另一台機器上以非管理員身份檢查它。好的,您可以指定CRYPT_USER_KEYSETaka X509KeyStorageFlags.UserKeySet

哦,您在一台機器上以使用者身份創建它,但希望它作為另一台機器上的機器密鑰?美好的。 CRYPT_MACHINE_KEYSET/ X509KeyStorageFlags.MachineKeySet

我真的需要“臨時”文件嗎?

如果您只是檢查 PFX 文件,或者想臨時使用它們,為什麼還要費心將密鑰寫入磁碟呢?好的,Windows Vista 說,我們可以直接將私鑰載入到加密密鑰對像中,然後我們會告訴您指針。

PKCS12_NO_PERSIST_KEY/X509KeyStorageFlags.EphemeralKeySet

我想如果 Windows 在 NT4 中具有此功能,那麼這將是 .NET 的預設設置。它現在不能成為預設值,因為太多的事情取決於“正常”導入如何檢測私鑰是否可用的內部機制。

最後兩個呢?

PFXImportCertStore 的預設模式是私鑰不應該是可重新導出的。要告訴它這是錯誤的,您可以指定CRYPT_EXPORTABLE/ X509KeyStorageFlags.Exportable

CAPI 和 CNG 都支持一種機制,其中軟體密鑰在使用私鑰之前可能需要同意或密碼(如智能卡的 PIN 提示),但您必須在首次創建(或導入)密鑰時聲明. 因此 PFXImportCertStore 允許您指定CRYPT_USER_PROTECTED(並且 .NET 將其公開為X509KeyStorageFlags.UserProtected)。

最後兩個實際上只對“一個私鑰”PFX 有意義,因為它們適用於所有密鑰。它們也不包含原始密鑰可能具有的全部選項…… CNG 和 CAPI 都支持“可歸檔”密鑰,這意味著“可導出一次”。機器密鑰上的自定義 ACL 在 PFX 中也得不到任何支持。

概括

對於證書(或證書集合),一切都很簡單。一旦涉及到私鑰,事情就會變得一團糟,Windows 證書(儲存)的抽象會變得有點薄,您需要了解持久性模型和儲存模型。

引用自:https://stackoverflow.com/questions/52750160