【问题标题】:SSPI WDigest Server Vaildation FailureSSPI WDigest 服务器验证失败
【发布时间】:2019-07-26 22:34:33
【问题描述】:

我正在尝试让 Win32 SSPI API 验证来自客户端的 challenge response。对AcceptSecurityContext 的调用总是以 SEC_E_INVALID_TOKEN (0x80090308) 或 SEC_E_INTERNAL_ERROR (0x80090304) 失败。

我已将问题简化为以下示例代码:

#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <security.h>
#include <wdigest.h>
#include <cstdlib>
#include <string>
#include <iostream>

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

using namespace std::string_literals;

int main()
{
    auto const realm = L"realm"s;
    auto const client_method = "GET"s;
    auto const client_target = L"HTTP/TARGETMACHINE"s;
    auto const client_uri = L"/"s;
    auto const client_uri_utf8 = "/"s;

    // server generate digest challenge
    PSecPkgInfoW package_info;
    auto result = QuerySecurityPackageInfoW(const_cast<LPWSTR>(WDIGEST_SP_NAME_W), &package_info);
    if (result != SEC_E_OK) 
    {
        return EXIT_FAILURE;
    }

    CredHandle serverCredHandle;
    TimeStamp lifetime;
    result = AcquireCredentialsHandleW(nullptr, const_cast<LPWSTR>(WDIGEST_SP_NAME_W), SECPKG_CRED_INBOUND, nullptr, nullptr, nullptr, nullptr, &serverCredHandle, &lifetime);
    if (result != SEC_E_OK)
    {
        return EXIT_FAILURE;
    }

    SecBuffer challengeInBuffers[5];

    // token
    challengeInBuffers[0].BufferType = SECBUFFER_TOKEN;
    challengeInBuffers[0].cbBuffer = 0;
    challengeInBuffers[0].pvBuffer = nullptr;

    // method
    challengeInBuffers[1].BufferType = SECBUFFER_PKG_PARAMS;
    challengeInBuffers[1].cbBuffer = 0;
    challengeInBuffers[1].pvBuffer = nullptr;

    // uri
    challengeInBuffers[2].BufferType = SECBUFFER_PKG_PARAMS;
    challengeInBuffers[2].cbBuffer = 0;
    challengeInBuffers[2].pvBuffer = nullptr;

    // body hash
    challengeInBuffers[3].BufferType = SECBUFFER_PKG_PARAMS;
    challengeInBuffers[3].cbBuffer = 0;
    challengeInBuffers[3].pvBuffer = nullptr;

    // realm
    challengeInBuffers[4].BufferType = SECBUFFER_PKG_PARAMS;
    challengeInBuffers[4].cbBuffer = realm.size() * sizeof(wchar_t);
    challengeInBuffers[4].pvBuffer = const_cast<void*>(static_cast<void const*>(realm.c_str()));

    SecBufferDesc challengeInBufferDesc;

    challengeInBufferDesc.ulVersion = 0;
    challengeInBufferDesc.cBuffers = 5;
    challengeInBufferDesc.pBuffers  = challengeInBuffers;

    std::string challenge;
    challenge.resize(package_info->cbMaxToken);

    SecBuffer challengeOutBuffer;
    challengeOutBuffer.BufferType = SECBUFFER_TOKEN;
    challengeOutBuffer.cbBuffer = challenge.size();
    challengeOutBuffer.pvBuffer = const_cast<void*>(static_cast<void const*>(challenge.data()));

    SecBufferDesc challengeOutBufferDesc;
    challengeOutBufferDesc.ulVersion = 0;
    challengeOutBufferDesc.cBuffers = 1;
    challengeOutBufferDesc.pBuffers = &challengeOutBuffer;

    CtxtHandle serverContextHandle;
    unsigned long outContextAttributes;
    result = AcceptSecurityContext(&serverCredHandle, nullptr, &challengeInBufferDesc, 0, SECURITY_NETWORK_DREP, &serverContextHandle, &challengeOutBufferDesc, &outContextAttributes, &lifetime);
    if (result != SEC_I_CONTINUE_NEEDED)
    {
        return EXIT_FAILURE;
    }

    challenge.resize(challengeOutBuffer.cbBuffer);

    std::cout << "Challenge: [" << challenge << "]\n";


    // client challenge response generation
    SEC_WINNT_AUTH_IDENTITY_W auth_data;
    auth_data.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
    auth_data.User = nullptr;
    auth_data.UserLength = 0;
    auth_data.Domain = nullptr;
    auth_data.DomainLength = 0;
    auth_data.Password = nullptr;
    auth_data.PasswordLength = 0;

    CredHandle clientCredHandle;
    result = AcquireCredentialsHandleW(nullptr, const_cast<LPWSTR>(WDIGEST_SP_NAME_W), SECPKG_CRED_OUTBOUND, nullptr, &auth_data, nullptr, nullptr, &clientCredHandle, &lifetime);
    if (result != SEC_E_OK)
    {
        return EXIT_FAILURE;
    }

    SecBuffer challengeResponseInBuffers[4];

    // token
    challengeResponseInBuffers[0].BufferType = SECBUFFER_TOKEN;
    challengeResponseInBuffers[0].cbBuffer = challenge.size();
    challengeResponseInBuffers[0].pvBuffer = const_cast<void*>(static_cast<void const*>(challenge.data()));

    // method
    challengeResponseInBuffers[1].BufferType = SECBUFFER_PKG_PARAMS;
    challengeResponseInBuffers[1].cbBuffer = client_method.size();
    challengeResponseInBuffers[1].pvBuffer = const_cast<void*>(static_cast<void const*>(client_method.data()));

    // body hash
    challengeResponseInBuffers[2].BufferType = SECBUFFER_PKG_PARAMS;
    challengeResponseInBuffers[2].cbBuffer = 0;
    challengeResponseInBuffers[2].pvBuffer = nullptr;

    // target
    challengeResponseInBuffers[3].BufferType = SECBUFFER_STREAM;
    challengeResponseInBuffers[3].cbBuffer = client_target.size() * sizeof(wchar_t);
    challengeResponseInBuffers[3].pvBuffer = const_cast<void*>(static_cast<void const*>(client_target.data()));

    SecBufferDesc challengeResponseInBufferDesc;

    challengeResponseInBufferDesc.ulVersion = 0;
    challengeResponseInBufferDesc.cBuffers = 4;
    challengeResponseInBufferDesc.pBuffers  = challengeResponseInBuffers;

    std::string challengeResponse;
    challengeResponse.resize(package_info->cbMaxToken);

    SecBuffer challengeResponseOutBuffer;
    challengeResponseOutBuffer.BufferType = SECBUFFER_TOKEN;
    challengeResponseOutBuffer.cbBuffer = challengeResponse.size();
    challengeResponseOutBuffer.pvBuffer = const_cast<void*>(static_cast<void const*>(challengeResponse.data()));

    SecBufferDesc challengeResponseOutBufferDesc;
    challengeResponseOutBufferDesc.ulVersion = 0;
    challengeResponseOutBufferDesc.cBuffers = 1;
    challengeResponseOutBufferDesc.pBuffers = &challengeResponseOutBuffer;

    CtxtHandle clientContextHandle;
    result = InitializeSecurityContextW(&clientCredHandle, nullptr, const_cast<SEC_WCHAR*>(client_uri.c_str()), 0, 0, SECURITY_NETWORK_DREP, &challengeResponseInBufferDesc, 0, &clientContextHandle, &challengeResponseOutBufferDesc, &outContextAttributes, &lifetime);
    if(result != SEC_E_OK)
    {
        return EXIT_FAILURE;
    }

    challengeResponse.resize(challengeResponseOutBuffer.cbBuffer);

    std::cout << "Challenge Response: [" << challengeResponse << "]\n";


    // server verify challenge response

    SecBuffer verifyChallengeResponseInBuffers[5];

    // token
    verifyChallengeResponseInBuffers[0].BufferType = SECBUFFER_TOKEN;
    verifyChallengeResponseInBuffers[0].cbBuffer = challengeResponse.size();
    verifyChallengeResponseInBuffers[0].pvBuffer = const_cast<void*>(static_cast<void const*>(challengeResponse.data()));

    // method
    verifyChallengeResponseInBuffers[1].BufferType = SECBUFFER_PKG_PARAMS;
    verifyChallengeResponseInBuffers[1].cbBuffer = client_method.size();
    verifyChallengeResponseInBuffers[1].pvBuffer = const_cast<void*>(static_cast<void const*>(client_method.data()));

    // uri
    verifyChallengeResponseInBuffers[2].BufferType = SECBUFFER_PKG_PARAMS;
    verifyChallengeResponseInBuffers[2].cbBuffer = client_uri_utf8.size();
    verifyChallengeResponseInBuffers[2].pvBuffer = const_cast<void*>(static_cast<void const*>(client_uri_utf8.data()));

    // body hash
    verifyChallengeResponseInBuffers[3].BufferType = SECBUFFER_PKG_PARAMS;
    verifyChallengeResponseInBuffers[3].cbBuffer = 0;
    verifyChallengeResponseInBuffers[3].pvBuffer = nullptr;

    // realm
    verifyChallengeResponseInBuffers[4].BufferType = SECBUFFER_PKG_PARAMS;
    verifyChallengeResponseInBuffers[4].cbBuffer = realm.size() * sizeof(wchar_t);
    verifyChallengeResponseInBuffers[4].pvBuffer = const_cast<void*>(static_cast<void const*>(realm.c_str()));

    SecBufferDesc verifyChallengeResponseInBufferDesc;

    verifyChallengeResponseInBufferDesc.ulVersion = 0;
    verifyChallengeResponseInBufferDesc.cBuffers = 5;
    verifyChallengeResponseInBufferDesc.pBuffers  = verifyChallengeResponseInBuffers;

    std::string verifyChallengeResponse;
    verifyChallengeResponse.resize(package_info->cbMaxToken);

    SecBuffer verifyChallengeResponseOutBuffer;
    challengeOutBuffer.BufferType = SECBUFFER_TOKEN;
    challengeOutBuffer.cbBuffer = verifyChallengeResponse.size();
    challengeOutBuffer.pvBuffer = const_cast<void*>(static_cast<void const*>(verifyChallengeResponse.data()));

    SecBufferDesc verifyChallengeResponseOutBufferDesc;
    verifyChallengeResponseOutBufferDesc.ulVersion = 0;
    verifyChallengeResponseOutBufferDesc.cBuffers = 1;
    verifyChallengeResponseOutBufferDesc.pBuffers = &verifyChallengeResponseOutBuffer;

    result = AcceptSecurityContext(&serverCredHandle, &serverContextHandle, &verifyChallengeResponseInBufferDesc, 0, SECURITY_NETWORK_DREP, &serverContextHandle, &verifyChallengeResponseOutBufferDesc, &outContextAttributes, &lifetime);
    if (result != SEC_I_COMPLETE_NEEDED)
    {
        std::cout << "Challenge Response Verification Failed with [0x" << std::hex << result << "]";
        return EXIT_FAILURE;
    }

    std::cout << "Challenge Response Verified";
    return EXIT_SUCCESS;
}

我得到的输出是:

挑战: [QOP = “AUTH”,算法MD5 =-sess,则随机数= “+升级+ v1db70e06e6d35dfd59df6fcd8d3cf7f7671d23e810144d5012972e89ccadaa398f05e8d5ab7a9c0eb3d52b00f4446530312ac45e30f4ac02c”,字符集= UTF-8,境界= “境界”] P>

挑战回应: [用户名= “”,境界= “域”,随机数= “+升级+ v1db70e06e6d35dfd59df6fcd8d3cf7f7671d23e810144d5012972e89ccadaa398f05e8d5ab7a9c0eb3d52b00f4446530312ac45e30f4ac02c”,URI = “/”,cnonce = “+升级+ v15c1d89757bd55776df6e2788dcdca9220f6aa1af03856d237a0e37a8136b5a44”,NC = 00000001,算法= MD5-sess,则响应=“ee315ddeed6f2d3d68b6cafff9f7d52e ",qop="auth",charset=utf-8,hashed-dirs="service-name,channel-binding",service-name="",channel-binding="000000000000000000000000000000000"]

质询响应验证失败并显示 [0x80090308]

我尝试改变质询/质询响应和验证部分的输入,但没有实际结果。

MSDN 文档似乎没有太多帮助,我对这个摘要 API 的大部分见解都来自此处的 .net 参考源: https://referencesource.microsoft.com/#System/net/System/Net/_NTAuthentication.cs

有人知道我做错了什么吗?

更新:

对于下面答案中的示例应用程序,我的机器输出 UseLogonCredential 设置为“1”(尽管我用它测试了“1”和“0”,输出完全相同)我得到以下输出:

Capabilities:   0x800304
wVersion:       0x1
Max Token Size: 0x1000
SERVER: AcquireCredentialsHandle SUCCESS
CLIENT: AcquireCredentialsHandle SUCCESS
CLIENT: InitializeSecurityContext: SEC_I__CONTINUE_NEEDED
SERVER: AcceptSecurityContext: SEC_I__CONTINUE_NEEDED
CLIENT: InitializeSecurityContext: SEC_I_CONTINUE_NEEDED
AcceptSecurityContext SUCCESS
InitializeSecurityContext (2) failed with 0x8009030A

这对我来说看起来像是在工作,但我不明白为什么你仍然需要最后一个客户端 InitializeSecurityContext ,因为服务器端已经验证了它?

我也可以取出第一个“CLIENT:InitializeSecurityContext”,因为它不需要并返回一个“空白”令牌,如果删除它仍然可以正常工作。

我无法继续使用客户端的当前用户凭据,我必须提供具有有效凭据的 SEC_WINNT_AUTH_IDENTITY。这有什么原因吗?

【问题讨论】:

    标签: winapi sspi


    【解决方案1】:

    默认情况下禁用 wdigest 包,因为它使用不安全的 MD5。 您必须在注册表中启用包才能使用代码。 KB2871997 and Wdigest – Part 1。以及为什么要使用 wdigest,因为它不再安全了。

    #include <windows.h>
    #include <stdio.h>
    #define SECURITY_WIN32
    #include <sspi.h>
    
    void wmain(int argc, WCHAR *argv[])
    {
        SECURITY_STATUS ss;
        SecPkgInfo      *spi = {0};
    
        ss = QuerySecurityPackageInfo(L"wdigest", &spi);
        if (ss != SEC_E_OK)
        {
            wprintf(L"QuerySecurityPackageInfo failed with %u\n", ss);
            return;
        }
    
        wprintf(L"Capabilities:   0x%X\n", spi->fCapabilities);
        wprintf(L"wVersion:       0x%X\n", spi->wVersion);
        wprintf(L"Max Token Size: 0x%X\n", spi->cbMaxToken);
    
        LPBYTE lpbOut = NULL;
        lpbOut = (LPBYTE)LocalAlloc(LPTR, spi->cbMaxToken);
        if (lpbOut == NULL)
        {
            wprintf(L"LocalAlloc failed with %u\n", GetLastError());
            return;
        }
    
        CredHandle ServerCred;
        CredHandle ClientCred;
        TimeStamp LifeTime;
    
        // server side
        ss = AcquireCredentialsHandle(NULL, L"wdigest", SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &ServerCred, &LifeTime);
        if (ss != SEC_E_OK)
        {
            wprintf(L"AcquireCredentialsHandle failed with %u\n", ss);
            return;
        }
        else
            wprintf(L"SERVER: AcquireCredentialsHandle SUCCESS\n");
    
        // client side
        SEC_WINNT_AUTH_IDENTITY   AuthIdentity;
    
    
        WCHAR szDomain[] = L"domain";
        WCHAR szPassword[] = L"password";
        WCHAR szUser[] = L"user";
    
    
    
        AuthIdentity.Domain = (unsigned short *)szDomain;
        AuthIdentity.DomainLength = (unsigned long)wcslen(szDomain);
        AuthIdentity.Password = (unsigned short *)szPassword;
        AuthIdentity.PasswordLength = (unsigned long)wcslen(szPassword);
        AuthIdentity.User = (unsigned short *)szUser;
        AuthIdentity.UserLength = (unsigned long)wcslen(szUser);
        AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
    
        /*
        AuthIdentity.Domain = (unsigned short *)NULL;
        AuthIdentity.DomainLength = (unsigned long)0;
        AuthIdentity.Password = (unsigned short *)NULL;
        AuthIdentity.PasswordLength = (unsigned long)0;
        AuthIdentity.User = (unsigned short *)NULL;
        AuthIdentity.UserLength = (unsigned long)0;
        AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;*/
    
    
        ss = AcquireCredentialsHandle(NULL, L"wdigest", SECPKG_CRED_OUTBOUND, NULL, &AuthIdentity, NULL, NULL, &ClientCred, &LifeTime);
        if (ss != SEC_E_OK)
        {
            wprintf(L"OUTBOUND: AcquireCredentialsHandle failed with 0x%X\n", ss);
            return;
        }
        else
            wprintf(L"CLIENT: AcquireCredentialsHandle SUCCESS\n");
    
        SecBufferDesc OutputBuffers;
        SecBuffer     TempTokensOut[6];
    
        OutputBuffers.ulVersion = SECBUFFER_VERSION;
        OutputBuffers.cBuffers  = 1;
        OutputBuffers.pBuffers  = TempTokensOut;
    
        TempTokensOut[0].BufferType = SECBUFFER_TOKEN;
        TempTokensOut[0].cbBuffer   = spi->cbMaxToken;
        TempTokensOut[0].pvBuffer   = lpbOut;
    
        CtxtHandle ClientCtxtHandle;
        ULONG ClientContextRetFlags = 0;
        BOOL  bContinue             = FALSE;
    
        ss = InitializeSecurityContext(&ClientCred, NULL, L"HTTP//test.com", /*ISC_REQ_INTEGRITY | ISC_REQ_CONFIDENTIALITY | */ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, NULL, SECURITY_NATIVE_DREP, NULL, NULL,
                                       &ClientCtxtHandle, &OutputBuffers, &ClientContextRetFlags, &LifeTime);
        if (ss == SEC_I_CONTINUE_NEEDED)
        {
            bContinue = TRUE;
    
            wprintf(L"CLIENT: InitializeSecurityContext: SEC_I__CONTINUE_NEEDED\n");
        }
        else
        {
            if (ss != SEC_E_OK)
            {
                wprintf(L"InitializeSecurityContext failed with 0x%X\n", ss);
                return;
            }
        }
    
        SecBufferDesc InputBuffers;
        SecBuffer     TempTokensIn[10];
    
        LPBYTE lpbIn = NULL;
        lpbIn = (LPBYTE)LocalAlloc(LPTR, spi->cbMaxToken);
        if (lpbIn == NULL)
        {
            wprintf(L"LocalAlloc failed with %u\n", GetLastError());
            return;
        }
    
        InputBuffers.ulVersion = SECBUFFER_VERSION;
        InputBuffers.cBuffers  = 1;
        InputBuffers.pBuffers  = TempTokensIn;
    
        TempTokensIn[0].BufferType = SECBUFFER_TOKEN;
        TempTokensIn[0].cbBuffer   = TempTokensOut[0].cbBuffer;
        TempTokensIn[0].pvBuffer   = TempTokensOut[0].pvBuffer;
    
        OutputBuffers.ulVersion = SECBUFFER_VERSION;
        OutputBuffers.cBuffers  = 1;
        OutputBuffers.pBuffers  = TempTokensOut;
    
        TempTokensOut[0].BufferType = SECBUFFER_TOKEN;
        TempTokensOut[0].cbBuffer = spi->cbMaxToken;
        TempTokensOut[0].pvBuffer = lpbOut;
    
        ULONG      TargetDataRep = 0;
        CtxtHandle ServerCtxtHandle;
        ULONG      ServerContextRetFlags = 0;
    
        ss = AcceptSecurityContext(&ServerCred, NULL, &InputBuffers, 0 /*ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY*/, TargetDataRep, &ServerCtxtHandle, &OutputBuffers, &ServerContextRetFlags, &LifeTime);
        if (ss == SEC_I_CONTINUE_NEEDED)
        {
            bContinue = TRUE;
    
            wprintf(L"SERVER: AcceptSecurityContext: SEC_I__CONTINUE_NEEDED\n");
        }
        else
        {
            if (ss != SEC_E_OK)
            {
                wprintf(L"AcceptSecurityContext failed with 0x%X\n", ss);
                return;
            }
        }
    
        InputBuffers.ulVersion = SECBUFFER_VERSION;
        InputBuffers.cBuffers  = 1;
        InputBuffers.pBuffers  = TempTokensIn;
    
        TempTokensIn[0].BufferType = SECBUFFER_TOKEN;
        TempTokensIn[0].cbBuffer   = TempTokensOut[0].cbBuffer;
        TempTokensIn[0].pvBuffer   = TempTokensOut[0].pvBuffer;
    
        OutputBuffers.ulVersion = SECBUFFER_VERSION;
        OutputBuffers.cBuffers  = 1;
        OutputBuffers.pBuffers  = TempTokensOut;
    
        TempTokensOut[0].BufferType = SECBUFFER_TOKEN;
        TempTokensOut[0].cbBuffer = spi->cbMaxToken;
        TempTokensOut[0].pvBuffer = lpbOut;
    
        ss = InitializeSecurityContext(&ClientCred, NULL, L"HTTP//test.com", /*ISC_REQ_INTEGRITY | ISC_REQ_CONFIDENTIALITY | */ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, NULL, SECURITY_NATIVE_DREP, &InputBuffers, NULL,
                                       &ClientCtxtHandle, &OutputBuffers, &ClientContextRetFlags, &LifeTime);
        if (ss == SEC_I_CONTINUE_NEEDED)
        {
            bContinue = TRUE;
    
            wprintf(L"CLIENT: InitializeSecurityContext: SEC_I_CONTINUE_NEEDED\n");
        }
        else
        {
            if (ss != SEC_E_OK)
            {
                wprintf(L"InitializeSecurityContext (2) failed with 0x%X\n", ss);
                return;
            }
        }
    
        InputBuffers.ulVersion = SECBUFFER_VERSION;
        InputBuffers.cBuffers  = 1;
        InputBuffers.pBuffers  = TempTokensIn;
    
        TempTokensIn[0].BufferType = SECBUFFER_TOKEN;
        TempTokensIn[0].cbBuffer   = TempTokensOut[0].cbBuffer;
        TempTokensIn[0].pvBuffer   = TempTokensOut[0].pvBuffer;
    
        OutputBuffers.ulVersion = SECBUFFER_VERSION;
        OutputBuffers.cBuffers  = 1;
        OutputBuffers.pBuffers  = TempTokensOut;
    
        TempTokensOut[0].BufferType = SECBUFFER_TOKEN;
        TempTokensOut[0].cbBuffer = spi->cbMaxToken;
        TempTokensOut[0].pvBuffer = lpbOut;
    
        ss = AcceptSecurityContext(&ServerCred, NULL, &InputBuffers, 0 /*ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY*/, TargetDataRep, &ServerCtxtHandle, &OutputBuffers, &ServerContextRetFlags, &LifeTime);
        if (ss == SEC_I_CONTINUE_NEEDED)
        {
            bContinue = TRUE;
        }
        else if (ss != SEC_E_OK)
        {
            wprintf(L"AcceptSecurityContext (2) failed with 0x%X\n", ss);
            return;
        }
        else
            wprintf(L"AcceptSecurityContext SUCCESS\n");
    
        InputBuffers.ulVersion = SECBUFFER_VERSION;
        InputBuffers.cBuffers  = 1;
        InputBuffers.pBuffers  = TempTokensIn;
    
        TempTokensIn[0].BufferType = SECBUFFER_TOKEN;
        TempTokensIn[0].cbBuffer   = TempTokensOut[0].cbBuffer;
        TempTokensIn[0].pvBuffer   = TempTokensOut[0].pvBuffer;
    
        OutputBuffers.ulVersion = SECBUFFER_VERSION;
        OutputBuffers.cBuffers  = 1;
        OutputBuffers.pBuffers  = TempTokensOut;
    
        TempTokensOut[0].BufferType = SECBUFFER_TOKEN;
        TempTokensOut[0].cbBuffer = spi->cbMaxToken;
        TempTokensOut[0].pvBuffer = lpbOut;
    
        ss = InitializeSecurityContext(&ClientCred, NULL, L"HTTP//test.com", /*ISC_REQ_INTEGRITY | ISC_REQ_CONFIDENTIALITY |*/ ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION, NULL, SECURITY_NATIVE_DREP, &InputBuffers, NULL,
                                       &ClientCtxtHandle, &OutputBuffers, &ClientContextRetFlags, &LifeTime);
        if (ss == SEC_I_CONTINUE_NEEDED)
        {
            bContinue = TRUE;
        }
        else if (ss != SEC_E_OK)
        {
            wprintf(L"InitializeSecurityContext (2) failed with 0x%X\n", ss);
            return;
        }
        else
            wprintf(L"InitializeSecurityContext SUCCESS\n");
    
        ss = FreeCredentialsHandle(&ClientCred);
        if (ss != SEC_E_OK)
        {
            wprintf(L"FreeCredentialsHandle failed with %u\n", ss);
            return;
        }
    
        ss = FreeCredentialsHandle(&ServerCred);
        if (ss != SEC_E_OK)
        {
            wprintf(L"FreeCredentialsHandle failed with %u\n", ss);
            return;
        }
    
        if (lpbOut != NULL)
            LocalFree((HLOCAL)lpbOut);
    
        if (lpbIn != NULL)
            LocalFree((HLOCAL)lpbIn);
    
        if (spi != NULL)
            FreeContextBuffer((PVOID)spi);
    }
    

    启用 wdigest 包后,此示例可以工作吗?

    【讨论】:

    • 文章没有说 wdigest 被禁用,而是说“在内存中缓存凭据”被禁用。 http 服务器使用 wdigest 进行基本身份验证。 MD5 被认为是“严重受损”,但并非完全不安全。当在 SSL (HTTPS) 上分层时,http 基本身份验证中的 AFAIK MD5 使用被认为是可以的。我为什么要使用 wdigest 是为了像 ISS 一样为 Windows 凭据实现 HTTP 基本身份验证...
    • “在内存中缓存凭据”被禁用,这会导致故障。使用此方法验证挑战响应是否成功?
    • 将“UseLogonCredential”设置为“1”(并重新启动)仍然给我相同的结果,因为它的工作方式不同。关于如何使用验证,我有一些不明白的地方,但没有关于如何验证的文档。
    • 此外,诸如“当 UseLogonCredential 值设置为 0 时观察到的行为变化是,您可能会注意到使用 WDigest 时需要更频繁地使用凭据。”表示 WDigest 未通过 UseLogonCredential 设置禁用。 support.microsoft.com/en-nz/help/2871997/…
    • 我在答案中添加了示例代码,请检查。
    猜你喜欢
    • 2011-07-07
    • 2018-10-20
    • 2013-06-17
    • 1970-01-01
    • 2016-09-23
    • 2017-07-16
    • 1970-01-01
    • 2016-05-28
    • 2021-12-14
    相关资源
    最近更新 更多