承接 第 1 部分 的訊息格式分析,本文進入程式碼實作層面,示範如何在資源受限的 J2ME 環境下,利用 Bouncy Castle 輕量級加密庫與自訂 ASN.1 工具類,完成 Kerberos 客戶端核心類別 的建構與 TGT 請求 (AS-REQ) 的產生。
為平衡程式碼重用與 Kerberos 專用邏輯,採三層類別設計:
ASN1DataTypes 提供統一的「Tag-Length-Value」建構流程:
getLengthBytes() 產生長度欄位(支援短/長形式)。關鍵方法:
getLengthBytes(int):自動判斷短/長形式編碼長度。getIntegerBytes(int):產生 Universal Tag 2 的 INTEGER。getGeneralStringBytes(String)、getOctetStringBytes(byte[])、getBitStringBytes(byte[])、getGeneralizedTimeBytes(byte[]):對應 Universal Tag 27/4/3/24。concatenateBytes(byte[], byte[]):位元組陣列串接工具。getSequenceBytes(byte[]):包裝成 Universal Tag 16 SEQUENCE。getTagAndLengthBytes(int tagType, int tagNumber, byte[]):產生 Application/Context-specific 標籤結構,tagType 為 APPLICATION_TYPE 或 CONTEXT_SPECIFIC。Kerberos 規範定義從「密碼 + realm + 用戶名」衍生 DES-CBC 金鑰,防止跨 realm 金鑰重用攻擊。KerberosKey.generateKey() 實作 11 步驟:
password + realm + username 字串。加密運算採用 Bouncy Castle DESEngine + CBCBlockCipher,getFinalKey() 示範 CBC 模式下以前一區塊密文為 IV 的鏈式加密。
KerberosClient.getTicketResponse() 依 ASN.1 結構逐欄位建構:
AS-REQ ::= [APPLICATION 10] SEQUENCE {
pvno [1] INTEGER (5),
msg-type [2] INTEGER (10),
req-body [4] KDC-REQ-BODY
}
req-body 子欄位產生順序:
kdc-options:5-byte 全零 BIT STRING(不啟用任何選項)。cname:PrincipalName = SEQUENCE { name-type [0] INTEGER (NT_PRINCIPAL=1), name-string [1] SEQUENCE OF GeneralString(username) },外層 Context Tag 1。realm:GeneralString(realmName),Context Tag 2。sname:PrincipalName,name-type = NT_UNKNOWN(0),name-string = SEQUENCE { "krbtgt", realmName },Context Tag 3。till:GeneralizedTime("19700101000000Z") 表不指定失效期,Context Tag 5。nonce:getRandomNumber() 產生 4-byte 隨機整數,Context Tag 7。etype:SEQUENCE OF INTEGER (3=DES-CBC),Context Tag 8。最終以 getTagAndLengthBytes(APPLICATION_TYPE, 10, sequenceBytes) 包裝完整 AS-REQ。getRandomNumber() 結合系統時間、種子與用戶資料雜湊,經 KerberosKey.getFinalKey() 混淆產出 4-byte 隨機數。
原始碼下載包含:
ASN1DataTypes.java、KerberosKey.java、KerberosClient.java 三大核心類。J2MEClientMIDlet.java 簡易測試 MIDlet。ReadMe.txt 建置與執行說明(需引入 Bouncy Castle J2ME 版)。下一部分將實作 KDC 回應解析、服務票據請求與安全通訊上下文建立。