You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C++实现支持同域与跨域的Kerberos身份认证

C++实现Kerberos域身份认证(支持同域/跨域)

基于ADSI的Kerberos认证改造

你当前使用的AdsOpenObject本身支持Kerberos认证,只需调整参数即可完成升级,无需替换核心API。

关键参数调整

  • 启用ADS_SECURE_AUTHENTICATION标志:ADSI会优先使用Kerberos,仅在Kerberos不可用时回退到NTLM。
  • 结合ADS_USE_SEAL和ADS_USE_SIGN:确保通信加密和完整性,提升安全性。
  • 跨域必须用UPN格式用户名:放弃DOMAIN\user的SAM账户格式,改用user@domain.com的用户主体名称,这是跨域Kerberos认证的必要条件。

改造后的代码示例

#include <adshlp.h>
#include <iostream>

#pragma comment(lib, "adsiid.lib")
#pragma comment(lib, "activeds.lib")

HRESULT KerberosADAuthentication(LPCWSTR ldapRootPath, LPCWSTR userUPN, LPCWSTR password, IADs** ppADsObj)
{
    *ppADsObj = nullptr;
    // 配置Kerberos认证标志,可选添加ADS_NO_AUTHENTICATION_FALLBACK强制禁用NTLM回退
    DWORD authFlags = ADS_SECURE_AUTHENTICATION | ADS_USE_SEAL | ADS_USE_SIGN;

    HRESULT hr = AdsOpenObject(
        ldapRootPath,
        userUPN,
        password,
        authFlags,
        IID_IADs,
        reinterpret_cast<void**>(ppADsObj)
    );

    if (FAILED(hr)) {
        std::wcerr << L"Kerberos认证失败,错误码: 0x" << std::hex << hr << std::endl;
    }
    return hr;
}

int main()
{
    IADs* pADs = nullptr;
    // 同域示例:LDAP路径指向本域,用户名用本域UPN
    HRESULT hr = KerberosADAuthentication(
        L"LDAP://dc=yourdomain,dc=com",
        L"john.doe@yourdomain.com",
        L"YourSecurePassword123!",
        &pADs
    );

    // 跨域示例:LDAP路径指向信任域,用户名用信任域UPN
    // HRESULT hr = KerberosADAuthentication(
    //     L"LDAP://dc=trusteddomain,dc=com",
    //     L"jane.smith@trusteddomain.com",
    //     L"HerSecurePassword456!",
    //     &pADs
    // );

    if (SUCCEEDED(hr) && pADs) {
        std::wcout << L"Kerberos认证成功" << std::endl;
        pADs->Release();
    }
    return 0;
}

强制禁用NTLM回退

如果需要严格确保仅使用Kerberos(禁止 fallback 到NTLM),只需在authFlags中添加ADS_NO_AUTHENTICATION_FALLBACK:

DWORD authFlags = ADS_SECURE_AUTHENTICATION | ADS_USE_SEAL | ADS_USE_SIGN | ADS_NO_AUTHENTICATION_FALLBACK;

此时若Kerberos协商失败,会直接返回错误,不会尝试NTLM认证。

跨域认证核心要求

  • 域信任关系:源域与目标域必须已建立双向信任,否则跨域Kerberos无法完成。
  • DNS正确性:客户端必须能解析目标域KDC(密钥分发中心)的DNS记录,确保Kerberos能找到目标域的认证服务。
  • UPN格式强制:跨域场景下,SAM账户名(DOMAIN\user)无法被Kerberos正确解析,必须使用UPN格式。

底层SSPI实现方案(无需ADSI)

如果需要更底层的Kerberos控制(比如不依赖ADSI),可以直接使用Windows SSPI(安全支持提供器接口):

#include <sspi.h>
#include <iostream>

#pragma comment(lib, "secur32.lib")

HRESULT RawKerberosAuthentication(LPCWSTR targetSPN)
{
    CredHandle hCred;
    TimeStamp tsExpiry;
    HRESULT hr = AcquireCredentialsHandle(
        nullptr,
        L"Kerberos",
        SECPKG_CRED_OUTBOUND,
        nullptr,
        nullptr,
        nullptr,
        nullptr,
        &hCred,
        &tsExpiry
    );

    if (FAILED(hr)) return hr;

    CtxtHandle hContext = {0};
    SecBuffer outBuffer;
    outBuffer.cbBuffer = 4096;
    outBuffer.BufferType = SECBUFFER_TOKEN;
    outBuffer.pvBuffer = malloc(outBuffer.cbBuffer);

    SecBufferDesc outBufferDesc;
    outBufferDesc.ulVersion = SECBUFFER_VERSION;
    outBufferDesc.cBuffers = 1;
    outBufferDesc.pBuffers = &outBuffer;

    DWORD contextAttrs = ISC_REQ_SEAL | ISC_REQ_SIGN;
    hr = InitializeSecurityContext(
        &hCred,
        nullptr,
        const_cast<WCHAR*>(targetSPN),
        contextAttrs,
        0,
        SECURITY_NATIVE_DREP,
        nullptr,
        0,
        &hContext,
        &outBufferDesc,
        nullptr,
        &tsExpiry
    );

    // 实际场景中需与服务端交互交换令牌,此处仅展示初始化流程
    if (hr == SEC_E_OK || hr == SEC_I_CONTINUE_NEEDED) {
        std::wcout << L"Kerberos上下文初始化成功" << std::endl;
    }

    // 资源清理
    free(outBuffer.pvBuffer);
    DeleteSecurityContext(&hContext);
    FreeCredentialsHandle(&hCred);

    return hr;
}

验证要点

  • 同域认证:在域内机器运行,使用本域UPN,验证是否能成功绑定AD对象。
  • 跨域认证:在非域机器或其他域机器运行,使用目标域UPN,确认域信任和DNS配置正常后验证绑定结果。

内容的提问来源于stack exchange,提问作者5039_Nishanth S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:45:37