使用Schannel的DTLS

6
我正在尝试在Windows下使用Schannel创建DTLS“连接”(我正在测试最近的Windows 10版本,因此Schannel支持的所有DTLS版本都应该可用)
我尝试通过遵循文档中的步骤,从已有的代码建立常规TLS连接:
1. 在第一次传递时使用空输入的InitializeSecurityContext,输出SECBUFFER_TOKEN和SECBUFFER_ALERT 2. 在输入上使用SECBUFFER_TOKEN和SECBUFFER_EMPTY的AcceptSecurityContext,输出SECBUFFER_TOKEN和SECBUFFER_ALERT。 3. 重复以上两个步骤直到成功,然后继续使用Encrypt/DecryptMessage 在流模式(ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM)下,这很完美地运行。
如果我尝试将STREAM替换为ISC/ASC_REQ_DATAGRAM,则我的InitializeSecurityContext成功返回SEC_I_CONTINUE_NEEDED,但我的第一个AcceptSecurityContext随即失败,并显示SEC_E_INVALID_PARAMETER。
我已尝试将SCHANNEL_CRED的grbitEnabledProtocols设置为0以使用双方都记录的默认值,我还尝试将其设置为SP_PROT_DTLS1_X,但我仍然从我的第一个ASC中得到了Invalid Parameter返回值。 我也尝试了DTLS_1_0常量,以防万一。此外,所有安全协议在我的注册表设置中默认启用。根据我对DTLS RFC的理解,我的代码在HelloVerifyRequest步骤失败,而且根据我对RFC的理解,此部分需要安全提供程序从ClientHello消息的几个部分以及源IP地址生成cookie。但是,我找不到任何记录的方法来将此信息传递给ASC函数。我认为? :) 我已经在整个互联网上搜索了任何在没有运气的情况下使用Schannel的DTLS的工作示例。在stackoverflow上,我发现了这个问题,它简单地提到它受支持:Is DTLS supported by Schannel on Windows 7?,并且链接的MSDN文章只是一个高级概述。
我搜索了与此功能相关的常量的任何使用情况...我搜索了与此 (ISC_REQ_DATAGRAM、SP_PROT_DTLS*、SECBUFFER_DTLS_MTU等) 相关的常量的任何使用情况,但在我能想到的所有搜索引擎中找到的唯一东西都是 sspi.h 的副本或索引 Windows API 中常量的网站...
我知道其他实现(如 OpenSSL 等)很好地支持 DTLS,但我真的更喜欢使用 Schannel,因为我的代码的其他部分目前在 TLS 模式下可以正常工作。
2个回答

5

来自微软:

目前没有使用Schannel实现DTLS的文档,存在已知并持久的文档空白。

虽然有一些差异,但TLS客户端或服务器可以相对容易地适应DTLS(许多客户已经成功地完成了这个过程)。

  1. 将SCHANNEL_CRED.grbitEnabledProtocols设置为SP_PROT_DTLS1_X。
  2. 在调用AcceptSecurityContext时,通过SECBUFFER_EXTRA传递客户端的SOCKADDR。
  3. MTU可以通过SetContextAttributes使用常量SECPKG_ATTR_DTLS_MTU进行设置,其中缓冲区只是一个指向ULONG的指针。【默认为1096字节】
  4. 当ISC / ASC返回SEC_I_MESSAGE_FRAGMENT时,请发送此片段并再次调用ISC / ASC,在循环中获取下一个片段(无需尝试从网络读取数据)。
  5. 在您的应用程序中实现超时和重传逻辑(因为schannel不拥有套接字)。
  6. 接收片段时,schannel将尝试消除重复项、重新排序和重新组装,如果可能的话。
  7. SCHANNEL_SHUTDOWN不适用于DTLS。

考虑开一个新问题,但也许你可以在这里回答?当我从 ASC 接收到 SEC_I_MESSAGE_FRAGMENT 并再次调用以检索下一个片段时,我会收到 SEC_E_INCOMPLETE_MESSAGE 并且输出令牌缓冲区中没有内容。(如果传递零长度输入令牌缓冲区,那就是这种情况——如果我重新传递原始输入,则会将第一片段重复输出。)是否有特定的事情我需要做来指示 ASC 应该给我下一个片段? - Haddon CD.
我猜你还在谈判循环中?如果是这样,请退出循环并处理加密数据下溢的错误,然后在下一个recv上排队和处理。要做到这一点,检查从ASC返回的第二个元素的SECBUFFER_EXTRA(索引1)。备份数据以从输入缓冲区中删除已经处理的信息。 当再次接收数据时,通过调整存储数据缓冲区的大小将新数据添加到备份数据中,然后可以继续谈判或调用DecryptMessage()。 - Matthew J. Bobowski
Matthew,感谢你(如果我错了,还请见谅),但这听起来像是如何处理SEC_E_INCOMPLETE_MESSAGE。我在处理SEC_I_MESSAGE_FRAGMENT方面遇到了困难,也就是你上面回答的第4步。实际上,我已经提出了一个新问题(并提供了更多详细信息和可复现问题的示例代码),但这个问题仍然没有得到答复。如果你想看一下,链接在这里:https://stackoverflow.com/q/67463514/1831696 - Haddon CD.
我的评论是针对SEC_I_MESSAGE_FRAGMENT的。区别在于你可以继续循环,而不是因为SEC_E_INCOMPLETE_MESSAGE(数据不足)而退出循环。 - Matthew J. Bobowski
嗨,Matthew。请查看我的问题以获取更多详细信息,但是当我收到SEC_I_MESSAGE_FRAGMENT时,我在循环中继续执行(即发送输出SECBUFFER_TOKEN,然后再次调用ASC而不轮询套接字),但是我不确定在下一次迭代中向ASC传递什么作为输入。我假设(与SEC_E_INCOMPLETE_MESSAGE相同)除非ASC使用输出SECBUFFER_EXTRA指示其未处理的重复SECBUFFER_TOKEN数据,否则我不应该传递它,但是如果我传递零长度输入(作为SECBUFFER_TOKEN或_EMPTY),我只会得到SEC_E_INCOMPLETE_MESSAGE,而不是下一个片段。再次感谢! - Haddon CD.
1
感谢另一位用户的帮助,我已经解决了我的示例代码问题,这与我的SEC_I_MESSAGE_FRAGMENT处理无关。如果有人想看看Matthew在他的答案中建议的示例,可以在此处找到修复后的代码:https://gist.github.com/haddoncd/381c5e9542e977ca238ff16229bd9a0e - Haddon CD.

-2

1
这个问题特别涉及到SChannel。除了SChannel之外,DTLS可以轻松地使用OpenSSL和其他工具实现。你在stackoverflow上的大部分回答似乎都是你项目的广告。 - fbrosseau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接