返回首页

针对开发者的 Kerberos:协议和 SSPI

本文剖析了 AD 的 Kerberos 协议:从模拟和 TGT 到委托和 C++ 代码。SSPI、PKINIT、U2U 示例,可在一台机器上测试。适用于高级开发人员。

Kerberos:TGT、委托和 SSPI 的 C++ 代码
Advertisement 728x90

Kerberos 协议:开发者深度解析

Kerberos 解决了身份模拟挑战:机器 A 上的服务收到远程客户端的请求,必须代表该客户端执行操作,包括访问本地资源。为此,会基于提供的凭据创建一个新的访问令牌。没有 Kerberos,该服务将使用自己的令牌,从而无法访问需要提升权限的文件。

在链式请求场景中,服务 A 请求服务 B。委托机制让你选择:使用自己的帐户模拟身份,还是使用原始客户端身份。Kerberos 通过加密票据传递用户的 SID、组和 PAC(特权属性证书)。

关键组件:

Google AdInline article slot
  • 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>)指向域控制器。

Google AdInline article slot

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 令牌。

Google AdInline article slot

加密与密钥

密码被哈希成长期密钥(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+)。

单机测试:使用 klistAcquireCredentialsHandle(带 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

Advertisement 728x90

继续阅读