SSPI/Kerberos 与 GSSAPI 的互操作性

如果需要与 GSSAPI 实现互操作性,在使用 Kerberos 安全支持提供程序 (SSP) 时必须谨慎。 以下代码约定允许与基于 GSSAPI 的应用程序实现互操作性:

您可以在 Samples\Security\SSPI\GSS 下的平台软件开发工具包 (SDK) 中找到示例代码。 此外,等效的 UNIX 示例分布在 MIT 和 Heimdal Kerberos 分发版、GSS 客户端和服务器中。

Windows 兼容名称

GSSAPI 函数使用 RFC 中指定的名称格式,称为 gss_nt_service_name。 例如,sample@host.dom.com 是可在基于 GSSAPI 的应用程序中使用的名称。 Windows 操作系统无法识别 gss_nt_service_name 格式,必须使用完整的 服务主体名称(例如 sample/host.dom.com@REALM)。

身份验证

在客户端和服务器之间首次设置连接时,通常会处理身份验证。 在此示例中,客户端正在使用安全支持提供程序接口 (SSPI),服务器正在使用 GSSAPI。

在 SSPI 客户端中设置身份验证

  1. 使用 AcquireCredentialsHandle 获取出站凭据
  2. 使用 gss_import_name() 创建服务名称,并使用 gss_acquire_cred 获取入站凭据。
  3. 使用 InitializeSecurityContext (Kerberos) 获取要发送到服务器的身份验证令牌。
  4. 将令牌发送到服务器。

在 GSSAPI 服务器中设置身份验证

  1. 分析来自客户端的消息以提取安全令牌。 使用 gss_accept_sec_context 函数,将令牌作为参数传递。

  2. 分析来自服务器的消息以提取安全令牌。 将此安全令牌传递给 InitializeSecurityContext (Kerberos)。

  3. 向客户端发送响应令牌。

    gss_accept_sec_context 函数可返回可以发回客户端的令牌。

  4. 如果需要继续,请向服务器发送响应令牌;否则,身份验证设置将结束。

  5. 如果需要继续,请等待客户端的下一个令牌;否则,身份验证设置将结束。

信息完整性和隐私

大多数基于 GSSAPI 的应用程序在发送消息之前使用 GSS_Wrap 函数对消息进行签名。 相反,GSS_Unwrap 函数会验证签名。 GSS_Wrap 在 API 版本 2.0 中提供,现已在 Internet 标准中广泛使用和指定,这些标准描述了使用 GSSAPI 向协议添加安全性。 此前,GSS SignMessage 和 SealMessage 函数用于消息完整性隐私。 GSS_Wrap 和 GSS_Unwrap 用于完整性和隐私性,隐私性的使用由参数“conf_flag”的值控制。

如果指定基于 GSSAPI 的协议以使用 gss_get_mic gss_verify_mic 函数,则正确的 SSPI 函数将是 MakeSignature VerifySignature。 请注意,当 conf_flag 设置为零或 GSS_Unwrap 时,MakeSignature VerifySignature 不会与 GSS_Wrap 互操作。 仅用于签名的 EncryptMessage (Kerberos) 集gss_verify_mic 也是如此。

注意

当 GSS_Wrap 和 GSS_Unwrap 被调用时,请勿使用 MakeSignature VerifySignature 函数。

 

等效于 GSS_Wrap 的 SSPI 是用于完整性和隐私性的 EncryptMessage (Kerberos)。

以下示例演示如何使用 EncryptMessage (Kerberos) 签署 GSS_Unwrap 验证的数据。

在 SSPI 客户端中:

// Need three descriptors, two for the SSP and
// one to hold the application data. 
in_buf_desc.cBuffers = 3;
in_buf_desc.pBuffers = wrap_bufs;
in_buf_desc.ulVersion = SECBUFFER_VERSION;
wrap_bufs[0].cbBuffer = sizes.cbSecurityTrailer;
wrap_bufs[0].BufferType = SECBUFFER_TOKEN;
wrap_bufs[0].pvBuffer = malloc(sizes.cbSecurityTrailer);

// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = in_buf.cbBuffer;
wrap_bufs[1].pvBuffer = malloc(wrap_bufs[1].cbBuffer);
memcpy(wrap_bufs[1].pvBuffer, in_buf.pvBuffer, in_buf.cbBuffer);
wrap_bufs[2].BufferType = SECBUFFER_PADDING;
wrap_bufs[2].cbBuffer = sizes.cbBlockSize;
wrap_bufs[2].pvBuffer = malloc(wrap_bufs[2].cbBuffer);
maj_stat = EncryptMessage(&context,
SignOnly ? KERB_WRAP_NO_ENCRYPT : 0,
&in_buf_desc, 0);

// Send a message to the server.

在 GSSAPI 服务器中:

// Received message is in recv_buf. 
maj_stat = gss_unwrap(&min_stat, context, &recv_buf, &msg_buf,
    &conf_state, (gss_qop_t *) NULL);
(void) gss_release_buffer(&min_stat, &recv_buf);

// Original message is in msg_buf.

等效于 GSS_Unwrap 的 SSPI 为 DecryptMessage (Kerberos)。 以下示例演示如何使用 DecryptMessage (Kerberos) 解密由 GSS_Wrap 加密的数据。

在 GSSAPI 服务器中:

// Seal the message.
send_buf.value = msg;
send_buf.length = msglen;

// If encrypt_flag = 1, privacy; encrypt_flag = 0, integrity.
maj_stat = gss_wrap(&min_stat, context, encrypt_flag,
    GSS_C_QOP_DEFAULT, &send_buf, &state, &msg_buf); 

// The message to send is in msg_buf.

在 SSPI 客户端中:

wrap_buf_desc.cBuffers = 2;
wrap_buf_desc.pBuffers = wrap_bufs;
wrap_buf_desc.ulVersion = SECBUFFER_VERSION; 

// This buffer is for SSPI.
wrap_bufs[0].BufferType = SECBUFFER_STREAM;
wrap_bufs[0].pvBuffer = xmit_buf.pvBuffer;
wrap_bufs[0].cbBuffer = xmit_buf.cbBuffer;

// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = 0;
wrap_bufs[1].pvBuffer = NULL;
maj_stat = DecryptMessage(
&context,
&wrap_buf_desc,
0, // no sequence number
&qop
);

// This is where the data is.
msg_buf = wrap_bufs[1];

// Check QOP of received message.
// If QOP is KERB_WRAP_NO_ENCRYPT, the message is signed only; 
// otherwise, it is encrypted.