如何在.NET Framework 4.8生成SPNEGO格式Kerberos令牌用于IIS Windows认证
解决方案:用DefaultCredentials生成SPNEGO格式Kerberos令牌并适配IIS
一、正确使用SSPI生成SPNEGO格式令牌
你之前的问题出在SSPI调用时的安全包选择或上下文初始化逻辑错误,以下是基于当前用户默认凭证(DefaultCredentials)生成SPNEGO格式令牌的正确实现:
核心思路
SPNEGO是Kerberos/NTLM的封装协议,必须通过SSPI的Negotiate安全包生成,而非直接调用Kerberos包。使用SEC_WINNT_AUTH_IDENTITY_NULL参数即可自动使用当前登录用户的凭证,无需显式指定账号密码。
代码实现(P/Invoke方式)
using System; using System.Runtime.InteropServices; using System.Text; public class SpnegoTokenGenerator { // SSPI常量定义 private const int SECURITY_NATIVE_DREP = 0x00000010; private const int ISC_REQ_CONNECTION = 0x00000001; private const int SEC_E_OK = 0; private const int SECPKG_CRED_OUTBOUND = 2; private const int SECBUFFER_TOKEN = 2; // SSPI结构体定义 [StructLayout(LayoutKind.Sequential)] private struct SecHandle { public IntPtr dwLower; public IntPtr dwUpper; } [StructLayout(LayoutKind.Sequential)] private struct SecBufferDesc { public int ulVersion; public int cBuffers; public IntPtr pBuffers; } [StructLayout(LayoutKind.Sequential)] private struct SecBuffer { public int cbBuffer; public int BufferType; public IntPtr pvBuffer; } // SSPI函数声明 [DllImport("secur32.dll", CharSet = CharSet.Unicode)] private static extern int AcquireCredentialsHandle( string pszPrincipal, string pszPackage, int fCredentialUse, IntPtr pvLogonID, IntPtr pAuthData, IntPtr pGetKeyFn, IntPtr pvGetKeyArgument, out SecHandle phCredential, out IntPtr ptsExpiry); [DllImport("secur32.dll")] private static extern int InitializeSecurityContext( ref SecHandle phCredential, IntPtr phContext, string pszTargetName, int fContextReq, int Reserved1, int TargetDataRep, IntPtr pInput, int Reserved2, ref SecHandle phNewContext, ref SecBufferDesc pOutput, out int pfContextAttr, out IntPtr ptsExpiry); [DllImport("secur32.dll")] private static extern int FreeCredentialsHandle(ref SecHandle phCredential); [DllImport("secur32.dll")] private static extern int DeleteSecurityContext(ref SecHandle phContext); public static string GenerateSpnegoToken(string targetSpn) { SecHandle credHandle = new SecHandle(); SecHandle contextHandle = new SecHandle(); IntPtr expiry = IntPtr.Zero; try { // 获取当前用户的Negotiate凭证 int result = AcquireCredentialsHandle( null, "Negotiate", SECPKG_CRED_OUTBOUND, IntPtr.Zero, IntPtr.Zero, // 使用默认凭证,无需指定AUTH_IDENTITY IntPtr.Zero, IntPtr.Zero, out credHandle, out expiry); if (result != SEC_E_OK) throw new System.ComponentModel.Win32Exception(result); // 初始化安全上下文,生成SPNEGO令牌 SecBufferDesc outputBufferDesc = new SecBufferDesc(); SecBuffer outputBuffer = new SecBuffer(); outputBuffer.cbBuffer = 65536; outputBuffer.BufferType = SECBUFFER_TOKEN; outputBuffer.pvBuffer = Marshal.AllocHGlobal(outputBuffer.cbBuffer); outputBufferDesc.ulVersion = 0; outputBufferDesc.cBuffers = 1; outputBufferDesc.pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(outputBuffer)); Marshal.StructureToPtr(outputBuffer, outputBufferDesc.pBuffers, false); int contextAttr; result = InitializeSecurityContext( ref credHandle, IntPtr.Zero, targetSpn, ISC_REQ_CONNECTION, 0, SECURITY_NATIVE_DREP, IntPtr.Zero, 0, ref contextHandle, ref outputBufferDesc, out contextAttr, out expiry); // 初始请求允许返回SEC_I_CONTINUE_NEEDED,只要能生成令牌即可 if (result != SEC_E_OK && result != 0x00090312) throw new System.ComponentModel.Win32Exception(result); // 提取令牌并转换为Base64 SecBuffer outBuffer = Marshal.PtrToStructure<SecBuffer>(outputBufferDesc.pBuffers); byte[] tokenBytes = new byte[outBuffer.cbBuffer]; Marshal.Copy(outBuffer.pvBuffer, tokenBytes, 0, outBuffer.cbBuffer); return Convert.ToBase64String(tokenBytes); } finally { // 释放资源 if (!credHandle.dwLower.Equals(IntPtr.Zero)) FreeCredentialsHandle(ref credHandle); if (!contextHandle.dwLower.Equals(IntPtr.Zero)) DeleteSecurityContext(ref contextHandle); } } }
使用说明
- 调用
GenerateSpnegoToken时,传入目标IIS服务的SPN(格式通常为HTTP/<服务器主机名>,比如HTTP/webserver.contoso.com)。 - 将返回的Base64字符串放到请求头的
Authorization: Negotiate <令牌>中即可。
二、关于IIS接受非SPNEGO令牌的可能性
无法配置IIS直接接受纯Kerberos令牌:
- IIS的Windows认证模块中,
Negotiate认证机制严格遵循SPNEGO协议规范,要求令牌必须是SPNEGO封装格式(包含标识Kerberos/NTLM的OID头)。 - 若强行发送纯Kerberos令牌,IIS会返回401错误,因为无法识别令牌格式。
关键注意点
- 确保当前运行程序的用户有权限访问目标IIS服务(已在AD中配置SPN和委派权限,若为跨域场景)。
- 目标IIS服务器必须启用
Windows身份认证,并将Negotiate设为首选认证方案。
内容的提问来源于stack exchange,提问作者exeq
相关产品推荐
相关产品推荐

