安全地生成身份驗證令牌
(免責聲明:我是一位經驗豐富的伺服器端 Java 程序員,但幾乎沒有加密經驗。)
客觀的:
我有一個登錄伺服器,給定一個登錄名+密碼對,應該發出一個唯一的身份驗證令牌。多次使用相同的登錄名+密碼對應該發出新的唯一令牌,它們都應該同時有效。令牌是只有登錄伺服器才能創建的密文。其他人應該只能從中提取有效載荷。
可能的解決方案:
首先,我認為我應該只使用 RSA 用私鑰加密有效載荷。其他方只會使用公鑰來解密它。但是我在某處讀到 RSA 密碼不提供完整性和真實性,所以我想我需要添加一個 RSA 簽名,所以它看起來像:
RSAcipher-with-private-key(payload-length + payload + RSAsignature(payload))
然後我發現明文不能大於密鑰大小,因此我需要一個對稱密碼,所以我研究了 AES 並偶然發現了 GCM。據我了解,GCM 解決了 A. 和 I. 問題,所以我不再需要簽名,並想出了:
RSAcipher-with-private-key(AESkey + GCMiv) + AES-GCM-cipher(payload)
因此,其他方將使用公鑰提取 AESkey 和 GCMiv,然後使用它來解密 AEScipher 塊。
我正在使用 BouncyCastle java 庫,因為有人說 Java 1.8 中的 AES/GCM 已損壞。
那行得通嗎,那安全嗎,我想在這裡發明一個輪子嗎?如果是這種情況,我可以使用什麼開箱即用的解決方案?
在包括 RSA 在內的非對稱加密中,我們總是用公鑰加密,用私鑰解密(從不反過來)。在這個問題中,想要的是用私鑰簽名,而不是**encrypt。
這足以解決整個問題,因為在 BouncyCastle 或 Java 加密 API 中公開的 RSA 簽名方案允許對任意長度的數據進行簽名。
補充:現代密碼學中加密的目的是防止對手獲得有關加密內容的資訊(長度除外)。當一段數據被轉換成密碼,可以使用公鑰返回原始數據時,只有私鑰的持有者才能準備好密碼,這不是加密;那是帶有消息恢復的簽名。
在那個名稱中,簽名涵蓋了持有私鑰是準備密碼所必需的,並且私鑰允許檢查密碼。消息恢復涵蓋可以將密碼轉換回原始消息。
帶有消息恢復的 RSA 簽名方案(例如ISO/IEC 9796-2中的那些)比帶有附錄的那些(例如PKCS#1 中的那些)具有優勢:簽名的大小成本更小(略超過散列大小,而不是比模數大小)。例如,當使用 RSA-2048 和 SHA-256 時,ISO/IEC 9796-2 將 222 字節的消息嵌入到 256 字節的密碼中,而 PKCS#1 需要 478 字節的密碼(222 字節的明文消息, 以及 256 字節的簽名)。
據我了解,該問題試圖通過混合教科書 RSA(用於簽名與對稱密鑰的消息恢復)和 AES/GCM(用於完整性檢查和恢復大部分資訊)。這是最不安全的:對手(假定持有公鑰,根據定義)可以從 RSA 部分確定對稱密鑰,然後將消息本身更改為所需的任何內容。
再次,手頭的問題通過標準 RSA 簽名得到了很好的解決。可從 BouncyCastle 或 Java 加密 API 直接獲得帶有附錄的適當方案。如果將密碼的大小減少幾百字節很重要,則可以使用消息恢復。雖然 ECDSA 簽名(也很容易獲得)也將減少簽名成本(僅是帶有消息恢復的 RSA 的兩倍左右),並顯著加快簽名速度,但它的缺點是檢查密碼比使用 RSA 慢得多。