之前在寫 AES 加解密用的工具方法的時候, 意外發現某個特殊的情境, 會導致解密時因為沒有 Key 而拋出例外 (但 Key 確實有設定).
問題解析
首先要來看一下示意的程式碼
1 | // Main logic |
1 | // extension method for decrypt |
接著來分析一下錯誤內容
錯誤訊息: System.Security.Cryptography.CryptographicException: ‘Decrypting a value requires that a key be set on the algorithm object.’
堆疊追蹤最後一行:at System.Security.Cryptography.AesCryptoServiceProvider.CreateDecryptor()
這看起來像是 Key 不存在, 但我們確實有指定 Key, 而用中斷點進去 symmetric.CreateDecryptor()
這一行的 symmetric
中卻發現 Key 的值已經不是一開始指定的值了.
問題點說明
總之是耗了一點時間在排除問題, 最後發現是 KeySize = 128
這一行導致的, 因為設定 KeySize 這件事本身會使得 Key 被釋放掉.
這個現象要看官方的原始碼才能知道, 從 AesCryptoServiceProvider.cs 中可以看到 KeySize 的 setter 會將一開始設定的 Key 釋放掉, 導致 AesCryptoServiceProvider.CreateDecryptor() 執行時發現 m_key
沒有值而拋錯.
再更進一步去看父類別中 SymmetricAlgorithm 中的 KeySize 就可以發現, 其實不只 AES, 整個 SymmetricAlgorithm 的衍生類別都會在 KeySize 的 setter 中清掉現有的 Key (除非有誰特別將這段邏輯覆寫掉).
結論
相關的程式碼想看可以去上面那些連結裡面深入了解, 總之
Key 和 KeySize 之間是有依賴的, 指定 Key 之前如果先指定 KeySize 沒有意義, 因為指定 Key 的時候, KeySize 也會被改變, 所以下面範例最後的 KeySize 是 256 而不是 128.
1
2
3
4
5
6new AesCryptoServiceProvider()
{
Mode = CipherMode.ECB,
KeySize = 128,
Key = GenerateKey(256),
}同理, 如果指定了 Key, 就不可以在後面再指定 KeySize, 不然原本指定的 Key 會被清掉, 也就是本文遇到的問題.
需要指定 KeySize 的時機是, 加密時要指定 Key 的長度且不想自己產生 Key, 例如
1
2
3
4
5new AesCryptoServiceProvider()
{
Mode = CipherMode.ECB,
KeySize = 128
}相關問題, 網路上也有人討論, 附在下方參考中
其實這樣想起來也合理, Key 的值本身就可以算出 KeySize, 如果 KeySize 被指定時沒有清掉舊的 Key, 那就會可能發生 Key 的實際長度跟 KeySize 紀錄的長度不一樣的問題.
然後 Reference Source 和 Source Browser - .net core 真的是神物, 很多時候能靠這兩個東西了解細部的運作與問題發生的具體原因.
參考
RijndaelManaged - What does setting KeySize property do?