Kerberos 协议:开发者深度解析
Kerberos 解决了身份模拟挑战:机器 A 上的服务收到远程客户端的请求,必须代表该客户端执行操作,包括访问本地资源。为此,会基于提供的凭据创建一个新的访问令牌。没有 Kerberos,该服务将使用自己的令牌,从而无法访问需要提升权限的文件。
在链式请求场景中,服务 A 请求服务 B。委托机制让你选择:使用自己的帐户模拟身份,还是使用原始客户端身份。Kerberos 通过加密票据传递用户的 SID、组和 PAC(特权属性证书)。
关键组件:
- Principal:具有密码的实体(用户、计算机、服务)。
- KDC(密钥分发中心):存储密码,颁发 TGT(票据授予票据)和服务票据。
- SPN(服务主体名称):服务标识符,通常指向服务运行的帐户。
Kerberos 将认证与授权区分开来:解密票据确认身份,令牌则决定权限。
消息结构与传输
Kerberos 消息使用 ASN.1(按 RFC 4120 的 PER/DER 编码)。它们通过 88 端口的 UDP/TCP 传输。UDP 适用于短包(TGT 大约 1-2 KB),TCP 适用于较大包(带 PAC 的情况)。
Active Directory 中的 KDC 结合了 AS(认证服务)和 TGS(票据授予服务)。SRV 记录(_kerberos._tcp.<domain>)指向域控制器。
AS 交换过程(获取 TGT):
- 客户端 → KDC:KRB_KDC_REQ(使用时间戳的预认证,用密码或 PKINIT 加密)。
- KDC → 客户端:KRB_KDC_REP(TGT,用客户端长期密钥加密;预认证中包含会话密钥)。
- 客户端将 TGT 存储在 LSA(本地安全权威)中。
TGS 交换(服务票据):
- 客户端 → KDC:KRB_TGS_REQ(TGT + 认证器 + 服务名称/SPN)。
- KDC → 客户端:KRB_TGS_REP(服务票据、服务会话密钥)。
AP-REQ(带服务票据)封装在 LDAP、HTTP Negotiate、SMB、RPC 中。Wireshark 显示 GSS-WRAP,其中包含 NTLM/Kerberos 令牌。
加密与密钥
密码被哈希成长期密钥(Windows 使用 AES-256-CTs)。会话密钥是临时的,用于保护消息。
使用 CNG(下一代加密 API)的 Kerberos AES 加密 C++ 示例代码:
#include <windows.h>
#include <ncrypt.h>
#include <ntstatus.h>
NTSTATUS EncryptKerberosAES(
PUCHAR key, ULONG keyLen,
PUCHAR plaintext, ULONG ptLen,
PUCHAR* ciphertext, ULONG* ctLen) {
NCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
NTSTATUS status;
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) return status;
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CTS, sizeof(BCRYPT_CHAIN_MODE_CTS), 0);
// Kerberos uses AES-CTS
status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, key, keyLen);
// ... encrypt logic with IV from Kerberos spec
// Full code omitted for brevity
BCryptCloseAlgorithmProvider(hAlg, 0);
return status;
}
该代码演示了在 Kerberos 特有的 CTS(密文窃取)模式下设置 AES。
委托类型与测试
Kerberos 支持受限委托和不受限委托。
委托选项:
- 简单:无委托(服务不进一步传递凭据)。
- 不受限:完整 TGT 转发(有风险,已弃用)。
- 受限:S4U2Proxy — 仅针对指定 SPN 的服务票据。
- 基于资源的受限:服务端(Windows Server 2012+)。
单机测试:使用 klist、AcquireCredentialsHandle(带 SSPI)。U2U(用户到用户)示例:
CredHandle cred;
TimeStamp expiry;
AcquireCredentialsHandle(NULL, L"Kerberos", SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &cred, &expiry);
// U2U: impersonate without KDC, direct exchange
对于 PKINIT:将 X.509 证书传递给 AcquireCredentialsHandle,使用 SECPKG_CRED_CERTIFICATE。GSS-API 是 Windows 上 SSPI 的封装。
跨域认证与 SPN
信任帐户(名称 = 域)启用跨域认证。来自域 A 的客户端请求域 B 中信任主体的 TGT。
SPN 无密码 — 它标识主机/用户。重复 SPN 指向单个主体。系统通过格式确定类型:host/<fqdn>、HTTP/<fqdn>。
管理多个凭据:
- 使用 logon 的 ImpersonateHandleExplicit。
- 多用户 SPN 的 LSA 策略。
- 检查多个密钥的自定义 GSS 接受器。
关键要点
- Kerberos 是 AD 中单点登录的 GSS-API 层;票据包含带有 SID/组的 PAC。
- 委托对于后端到后端至关重要;受限委托可最小化风险。
- 测试 SSPI:AcquireCredentialsHandle 仅需 3-5 行代码,而非样板代码。
- PKINIT 和 U2U 扩展了无密码场景。
- ASN.1 + AES-CTs 是协议核心;在 88 端口使用 Wireshark 监控。
— Editorial Team
暂无评论。