如何编写 KSP 以连接到 KERB_CERTIFICATE_LOGON

2023-12-10

大家好,我编写了一个自定义凭据提供程序,在使用用户名/密码作为凭据时工作正常,密码通过蓝牙传输。 毕竟这并不困难,因为文档告诉您要实现哪些接口。

现在我想更改凭据以使用证书。我发现我应该为此使用 KERB_CERTIFICATE_LOGON 结构。深入研究该主题,我发现我应该实现一个自定义密钥存储提供程序,如中所述微软的这篇文章。这就是我迷失的地方。也许我太愚蠢了,无法搜索正确的文档,但我只是找不到必须实现哪些接口才能拥有可以在 KERB_CERTIFICATE_LOGON 结构的 CspData 字段中引用的 KSP。我只是找到了一堆方法并快速搜索 NCRYPT_CERTIFICATE_PROPERTY (在上面的链接文章中提到)揭示了惊人的two谷歌搜索结果。

我已经发现这个问题当我拥有凭证提供程序和 KSP 时,这将帮助我连接凭证提供程序和 KSP,但它没有解释如何编写 KSP。 :-(

谁能指导我在哪里查找信息或显示类似场景中使用的 KSP 的简短示例(仅方法声明,以及如何在调用 KERB_CERTIFICATE_LOGON 中使用生成的 KSP)?


This Microsoft 加密提供程序开发套件有一个很好的 KSP 样本。

除了这个示例之外,您唯一需要做的就是实施智能卡密钥证书财产。 代码将是这样的:

SECURITY_STATUS
WINAPI
KSPGetKeyProperty(
__in    NCRYPT_PROV_HANDLE hProvider,
__in    NCRYPT_KEY_HANDLE hKey,
__in    LPCWSTR pszProperty,
__out_bcount_part_opt(cbOutput, *pcbResult) PBYTE pbOutput,
__in    DWORD   cbOutput,
__out   DWORD * pcbResult,
__in    DWORD   dwFlags)
{

    ...

    if (wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0)
    {
        dwProperty = SAMPLEKSP_CERTIFICATE_PROPERTY;
        cbResult = GetCertificateSize(); //the size of the certificate that is associated with the key
    }

    *pcbResult = cbResult;
    if(pbOutput == NULL)
    {
        Status = ERROR_SUCCESS;
        goto cleanup;
    }

    if(cbOutput < *pcbResult)
    {
         Status = NTE_BUFFER_TOO_SMALL;
         goto cleanup;
    }

    if (wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0)
    {
        CopyMemory(pbOutput, crt, sizeof(crt));
    }


    switch(dwProperty)
    {
    case SAMPLEKSP_CERTIFICATE_PROPERTY:
        CopyMemory(pbOutput, GetCertificate(), cbResult); //Copy to pbOutput the certificate in binary form 
        break;

    ...

    }

    ...

}

实现 KSP 并注册后,您的 CredentialProvider 可以与其交互:

HRESULT MyCredential::GetSerialization(
    CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
    PWSTR* ppwszOptionalStatusText,
    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
    )
{

    ...

    ULONG ulAuthPackage;
    HRESULT hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
    ConstructAuthInfo(&pcpcs->rgbSerialization, &pcpcs->cbSerialization);

    if (SUCCEEDED(hr))
    {
        pcpcs->ulAuthenticationPackage = ulAuthPackage;
        pcpcs->clsidCredentialProvider = CLSID_MyCredentialProvider;

        // At this point the credential has created the serialized credential used for logon
        // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
        // that we have all the information we need and it should attempt to submit the 
        // serialized credential.
        *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
    }

    return hr;
}

HRESULT RetrieveNegotiateAuthPackage(ULONG * pulAuthPackage)
{
    HRESULT hr = S_OK;
    HANDLE hLsa = NULL;

    NTSTATUS status = LsaConnectUntrusted(&hLsa);
    if (SUCCEEDED(HRESULT_FROM_NT(status)))
    {

        ULONG ulAuthPackage;
        LSA_STRING lsaszKerberosName;
        LsaInitString(&lsaszKerberosName, MICROSOFT_KERBEROS_NAME_A);
        status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
        if (SUCCEEDED(HRESULT_FROM_NT(status)))
        {
            *pulAuthPackage = ulAuthPackage;
            hr = S_OK;
        }
        else
        {
            hr = HRESULT_FROM_NT(status);
        }
        LsaDeregisterLogonProcess(hLsa);
    }
    else
    {
        hr = HRESULT_FROM_NT(status);
    }

    return hr;
}

void ConstructAuthInfo(LPBYTE* ppbAuthInfo, ULONG *pulAuthInfoLen)
{
    WCHAR szCardName[] = L""; // no card name specified but you can put one if you want
    WCHAR szContainerName[] = L"my_key_name";
    WCHAR szReaderName[] = L"";
    WCHAR szCspName[] = L"My Key Storage Provider";
    WCHAR szPin[] = L"11111111";
    ULONG ulPinByteLen = wcslen(szPin) * sizeof(WCHAR);
    WCHAR szUserName[] = L"user";
    ULONG ulUserByteLen = wcslen(szUserName) * sizeof(WCHAR);
    WCHAR szDomainName[] = L"testdomain.com";
    ULONG ulDomainByteLen = wcslen(szDomainName) * sizeof(WCHAR);
    LPBYTE pbAuthInfo = NULL;
    ULONG  ulAuthInfoLen = 0;
    KERB_CERTIFICATE_LOGON *pKerbCertLogon;
    KERB_SMARTCARD_CSP_INFO *pKerbCspInfo;
    LPBYTE pbDomainBuffer, pbUserBuffer, pbPinBuffer;
    LPBYTE pbCspData;
    LPBYTE pbCspDataContent;

    ULONG ulCspDataLen = sizeof(KERB_SMARTCARD_CSP_INFO)-sizeof(TCHAR)+
        (wcslen(szCardName) + 1) * sizeof(WCHAR)+
        (wcslen(szCspName) + 1) * sizeof(WCHAR)+
        (wcslen(szContainerName) + 1) * sizeof(WCHAR)+
        (wcslen(szReaderName) + 1) * sizeof(WCHAR);

    ulAuthInfoLen = sizeof(KERB_CERTIFICATE_LOGON)+
        ulDomainByteLen + sizeof(WCHAR)+
        ulUserByteLen + sizeof(WCHAR)+
        ulPinByteLen + sizeof(WCHAR)+
        ulCspDataLen;

    pbAuthInfo = (LPBYTE)CoTaskMemAlloc(ulAuthInfoLen);
    ZeroMemory(pbAuthInfo, ulAuthInfoLen);

    pbDomainBuffer = pbAuthInfo + sizeof(KERB_CERTIFICATE_LOGON);
    pbUserBuffer = pbDomainBuffer + ulDomainByteLen + sizeof(WCHAR);
    pbPinBuffer = pbUserBuffer + ulUserByteLen + sizeof(WCHAR);
    pbCspData = pbPinBuffer + ulPinByteLen + sizeof(WCHAR);

    memcpy(pbDomainBuffer, szDomainName, ulDomainByteLen);
    memcpy(pbUserBuffer, szUserName, ulUserByteLen);
    memcpy(pbPinBuffer, szPin, ulPinByteLen);

    pKerbCertLogon = (KERB_CERTIFICATE_LOGON*)pbAuthInfo;

    pKerbCertLogon->MessageType = KerbCertificateLogon;
    pKerbCertLogon->DomainName.Length = (USHORT)ulDomainByteLen;
    pKerbCertLogon->DomainName.MaximumLength = (USHORT)(ulDomainByteLen + sizeof(WCHAR));
    pKerbCertLogon->DomainName.Buffer = (PWSTR)(pbDomainBuffer-pbAuthInfo);
    pKerbCertLogon->UserName.Length = (USHORT)ulUserByteLen;
    pKerbCertLogon->UserName.MaximumLength = (USHORT)(ulUserByteLen + sizeof(WCHAR));
    pKerbCertLogon->UserName.Buffer = (PWSTR)(pbUserBuffer-pbAuthInfo);
    pKerbCertLogon->Pin.Length = (USHORT)ulPinByteLen;
    pKerbCertLogon->Pin.MaximumLength = (USHORT)(ulPinByteLen + sizeof(WCHAR));
    pKerbCertLogon->Pin.Buffer = (PWSTR)(pbPinBuffer-pbAuthInfo);

    pKerbCertLogon->CspDataLength = ulCspDataLen;
    pKerbCertLogon->CspData = (PUCHAR)(pbCspData-pbAuthInfo);

    pKerbCspInfo = (KERB_SMARTCARD_CSP_INFO*)pbCspData;
    pKerbCspInfo->dwCspInfoLen = ulCspDataLen;
    pKerbCspInfo->MessageType = 1;
    pKerbCspInfo->KeySpec = CERT_NCRYPT_KEY_SPEC;

    pKerbCspInfo->nCardNameOffset = 0;
    pKerbCspInfo->nReaderNameOffset = pKerbCspInfo->nCardNameOffset + wcslen(szCardName) + 1;
    pKerbCspInfo->nContainerNameOffset = pKerbCspInfo->nReaderNameOffset + wcslen(szReaderName) + 1;
    pKerbCspInfo->nCSPNameOffset = pKerbCspInfo->nContainerNameOffset + wcslen(szContainerName) + 1;

    pbCspDataContent = pbCspData + sizeof(KERB_SMARTCARD_CSP_INFO)-sizeof(TCHAR);
    memcpy(pbCspDataContent + (pKerbCspInfo->nCardNameOffset * sizeof(WCHAR)), szCardName, wcslen(szCardName) * sizeof(WCHAR));
    memcpy(pbCspDataContent + (pKerbCspInfo->nReaderNameOffset * sizeof(WCHAR)), szReaderName, wcslen(szReaderName) * sizeof(WCHAR));
    memcpy(pbCspDataContent + (pKerbCspInfo->nContainerNameOffset * sizeof(WCHAR)), szContainerName, wcslen(szContainerName) * sizeof(WCHAR));
    memcpy(pbCspDataContent + (pKerbCspInfo->nCSPNameOffset * sizeof(WCHAR)), szCspName, wcslen(szCspName) * sizeof(WCHAR));

    *ppbAuthInfo = pbAuthInfo;
    *pulAuthInfoLen = ulAuthInfoLen;
}

这段代码主要有两点:

  1. 为了与 KSP 交互,pKerbCspInfo->KeySpecmust beCERT_NCRYPT_KEY_SPEC。
  2. 为了正确的结构序列化,根据this answer:

    pKerbCertLogon->DomainName.Buffer = (PWSTR)(pbDomainBuffer-pbAuthInfo); pKerbCertLogon->UserName.Buffer = (PWSTR)(pbUserBuffer-pbAuthInfo); pKerbCertLogon->Pin.Buffer = (PWSTR)(pbPinBuffer-pbAuthInfo); pKerbCertLogon->CspData = (PUCHAR)(pbCspData-pbAuthInfo);

此外,域控制器应使用“Kerberos 身份验证”模板颁发证书。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何编写 KSP 以连接到 KERB_CERTIFICATE_LOGON 的相关文章

  • 如何在 Caliburn.Micro 中使用 Conductor 的依赖注入

    我有时用Caliburn Micro http caliburnmicro com创建应用程序 使用最简单的 BootStrapper 我可以像这样使用 IoC 容器 SimpleContainer private SimpleContai
  • 将 ARGB 拆分为字节值

    我有一个 ARGB 值存储为 int 类型 它是通过调用 ToArgb 来存储的 我现在想要来自 int 值的各个颜色通道的字节值 例如 int mycolor 16744448 byte r g b a GetBytesFromColor
  • 如何创建语法突出显示文本框[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何使用 C Net 创建语法突出显示文本框 Take 闪烁网 http scintillanet codeplex com 并采取其
  • 命名管道客户端无法连接到作为网络服务运行的服务器

    我有一个服务在网络服务帐户下运行 该服务只是设置一个命名管道并侦听连接 NamedPipeServerStream listeningPipe new NamedPipeServerStream ourservicepipe PipeDir
  • C语言实现延时函数

    我想使用空循环实现延迟函数 但是完成一次循环所需的时间取决于编译器和机器 我希望我的程序自行确定时间并将程序延迟指定的时间 谁能给我任何想法如何做到这一点 注意 有一个名为delay 的函数可以将系统暂停指定的毫秒 是否可以在不使用此功能的
  • C++:字符串流有什么好处?

    谁能告诉我一些在 C 中使用字符串流的实际例子 即使用流插入和流提取运算符输入和输出到字符串流 您可以使用字符串流来转换任何实现operator lt lt 到一个字符串 include
  • 为什么 fgets 接受 int 而不是 size_t?

    功能如strcpy malloc strlen 和其他各种接受他们的参数或返回值作为size t代替int or an unsigned int出于显而易见的原因 一些文件功能 例如fread and fwrite use size t以及
  • 值类型如何实现引用类型

    我遇到了一个值类型正在实现 ref 的场景 类型 只是想知道这怎么可能 幕后发生了什么 结构体是值类型 接口是引用 类型但结构可以实现接口而不会出现任何错误 有什么想法吗 提前致谢 实际上 它同时以两种不同的方式进行 首先 任何值类型都可以
  • C 链表销毁函数

    我正在尝试学习 C 和很多人一样 我对指针有点困惑 无论如何 我创建了一个递归函数来销毁我的链表 但是正如我调试的那样 当我从函数返回时 列表的头部不应该为空 所以我猜这是对指针的一些基本误解 这是函数 void destroy struc
  • 我如何知道向量的实际最大大小? (不使用 std::vector::max_size)

    在在线课程中 我正在学习向量 在其中一个例子中 他们解释说 std vector max size 应该给我向量可以达到的最大大小 我决定测试一下 include
  • 如何生成可变参数包?

    给定不相关的输入是否可以生成非类型参数包 我的意思是 我想改变这一点 template
  • 将 std::pair const 转换为 std::pair const 安全吗?

    理论上或实践上 安全吗reinterpret cast a std pair
  • _MM_TRANSPOSE4_PS 在 GCC 中导致编译器错误?

    我第一次在 GCC 而不是 MSVC 中编译我的数学库 并经历了所有的小错误 我遇到了一个根本没有意义的错误 Line 284 error lvalue required as left operand of assignment 284号
  • 如何解析多态 JSON 数组?

    我有一个 JSON 格式的文件 其中包含个人用户的记录 一些用户的记录中间有一个评论字段 我只想解析顶级项目 全名 贡献者姓名 电子邮件 使用 Newtonsoft JSON 解析器 但我似乎无法让它识别单个对象 当我将整个字符串解析为一个
  • 如何将输出重定向到 boost 日志?

    我有一个使用boost log的C 程序 我加载了用户提供的动态链接库 我想将 stderr 重定向到 boost 日志 以便用户的库随时执行以下操作 std cerr lt lt Some stuff 它产生相同的结果 BOOST LOG
  • 当分配返回 0 时,具有空异常规范的运算符 new 调用构造函数

    我有以下声明 void operator new size t s PersistentMemory m throw return m gt allocatePersistentMemory s 我正在测试启动时的内存耗尽 这会导致m gt
  • 当一对迭代器初始化时,向量是否知道先保留?

    考虑以下代码 struct MyData MyData const BYTE pData size t uSize bucket pData pData uSize std vector
  • 我的代码哪里有泄漏?

    下面是我的代码 它打开一个 XML 文件 old xml 过滤无效字符并写入另一个 XML 文件 abc xml 最后 我将再次加载 XML abc xml 当执行以下行时 出现异常 表示 xml 文件被另一个进程使用 xDoc Load
  • NSubstitute - 测试特定的 linq 表达式

    我在当前正在开发的 MVC 3 应用程序中使用存储库模式 我的存储库界面如下所示 public interface IRepository
  • 为什么我无法通过 lambda 捕获“this”指针?

    考虑以下代码 class A public void foo auto functor this A a this auto functor a The compiler won t accept this instead of a a g

随机推荐

  • Swift:如何处理 PHPicker 的视频和照片结果?

    我需要用户能够从照片库中选择多张照片和视频 使用 PHPicker 我已经知道如何用这个获取图像 func picker picker PHPickerViewController didFinishPicking results PHPi
  • 在 R 地图中使用 FIPS 代码对县进行着色

    我正在寻找一种方法来在 R 中对美国地图上的县进行着色 我有数字 字符县 FIPS 代码列表 我可以将其作为参数输入 我只需要突出显示这些县 因此只需要对它们进行阴影处理 并且没有与县相对应的值或变化 我试着抬头 library choro
  • 使用 iPhone 执行服务器 Ping 操作

    我正在编写一个应用程序 需要知道两个服务器中哪一个响应速度最快 一台服务器在我的时区 另一台服务器在我的时区 如何从 iPhone 上的服务器 ping 获取往返时间 我查看了可达性 我认为它不能满足我在这种情况下的需要 也欢迎替代解决方案
  • 使用 PowerShell 以管理员身份运行命令?

    您知道如果您是系统的管理用户并且只需右键单击批处理脚本并以管理员身份运行它而无需输入管理员密码 该怎么办 我想知道如何使用 PowerShell 脚本来执行此操作 我不想输入密码 我只是想模仿右键以管理员身份运行 method 到目前为止我
  • Nginx - 下载 PHP 而不是执行

    我正在使用 CentOS 并且我有两个网页用于一项工作 第一个是 html 它有一个表单 使用 php 脚本将数据发送到我的 mySQL 数据库 这工作正常 第二个只是一个 php 页面 应该显示数据库中表的内容 问题是 我的浏览器下载文件
  • Ajax 使用express 和JQuery 刷新部分视图?

    我想使用 ajax 刷新部分视图 我知道如何将新数据附加到 HTML 但我想知道是否有更简单的方法 我有部分观点可以做到这一点 数据中的每个 x 李X姓名 我使用 partial test data data 传递数据 我想调用一个函数来再
  • 5 年后,还有比“最快的 C++ 代表”更好的东西吗?

    我知道 C 代表 这个话题已经被干死了 而且都http www codeproject com and http stackoverflow com深刻地涵盖了这个问题 一般来说 似乎唐 克拉格斯顿 Don Clugston 最快的代表是很
  • 将数据添加到列的简单方法

    我想将两个单独的数据集插入到 JavaFX TableView 中的列中 基本上我有 2 个带有字符串的 LinkedList 我想将一个列表放在一列中 另一个列表放在第二列中 做到这一点最简单的方法是什么 或者另一个 JavaFX 元素更
  • 应用程序定义或对象定义错误 1004

    VBA 抛出上面给出的错误Sheets Sheet1 Range A i Copy Destination Sheets Sheet2 Range A i A LastCol 1 我想做的实际上是复制 A i单元格 在第一次迭代中它是A2
  • Firestore 查询中可以使用通配符功能吗?

    我正在尝试决定如何在 firestore 中对数据进行建模 基本上 它是一个列出欢乐时光和餐厅其他特色菜的应用程序 每个餐厅 酒吧可能会根据一周中的某一天提供多种特色菜和欢乐时光 这是我的一个非常简单的例子hoping对数据建模 name
  • Python的平台模块未检测到Windows 10

    我目前正在 Windows 10 上工作 如果平台是 Windows 10 则需要放置一些代码 因此 我检查了 python 文档并阅读了有关平台模块的信息 文档是这样说的 platform win32 ver release versio
  • 按下播放按钮后进行回调 - Youtube 嵌入视频

    按下播放按钮后是否可以执行 JavaScript 操作 我知道我需要使用 Youtube API 中的 onStateChange 函数 但我真的不知道从哪里开始 有什么帮助吗 谢谢 我还在这里发现了一些东西 http apiblog yo
  • 计算中的小数点为 。或者 ,

    如果我使用小数点输入数字 则小数点会根据国家和地区格式而变化 可以作为点 或作为逗号 而且我无法控制应用程序在哪个设备上使用 如果区域格式使用逗号 计算就会出错 放入 5 6 与有时只放入 5 和同一时间放入 56 是相同的 即使我以编程方
  • 如何存储 bash 脚本连续两次运行之间的状态

    我有 bash 脚本 它使用cron每分钟都有工作 我想保存脚本的状态以便在下次运行时重用 保存状态的最佳方法是什么 在本例中是分配了数字的变量 因此 在下一次运行中 该数字可以与之前运行的值进行比较 从文件保存和重新加载变量值的示例 us
  • 从 Rails ActiveRecord 的结果中排除一些 id

    我对某些部分的查询文章有以下声明 Article all joins gt sections conditions gt sections gt id gt 3 4 6 7 8 9 id not in gt some ids limit g
  • 将 javascript 客户端协调到单个后端游戏的最佳方法是什么?

    用于通知 javascript 客户端异步游戏中发生的更改 即其他客户端所做的移动 的最佳方法是什么 作为一个例子 假设一个回合制棋盘游戏 我应该让客户端每隔一秒左右轮询一次 PHP 后端以获取新的动作 还是有更好的方法向同一游戏中的其他客
  • 自定义 Android 通知磁贴的背景

    我注意到我的一个 Android 应用程序为其通知磁贴设置了自定义背景 如下面的屏幕截图所示 该应用程序如何为其通知设置自定义背景 我如何在自己的应用程序中实现同样的目标 NotificationCompat Builder this ch
  • JCO.destination 中未到达合作伙伴错误

    我在 java 程序中建立与 SAP 的连接时遇到问题 我按照 JCO 下载中的示例进行操作 但总是收到此错误 com sap conn jco JCoException 102 RFC ERROR COMMUNICATION Connec
  • Objective C:向 UIButton 调用的方法发送参数

    我有一个在单击 UIButton 时被调用的方法 当我创建按钮时 我希望它存储 NSTimer 作为参数 这是计时器和 UIButton 的创建 我如何添加要发送到方法的计时器 我试过了withObject timer但它给了我一个警告并在
  • 如何编写 KSP 以连接到 KERB_CERTIFICATE_LOGON

    大家好 我编写了一个自定义凭据提供程序 在使用用户名 密码作为凭据时工作正常 密码通过蓝牙传输 毕竟这并不困难 因为文档告诉您要实现哪些接口 现在我想更改凭据以使用证书 我发现我应该为此使用 KERB CERTIFICATE LOGON 结