65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 和 Kerberos 进行 Web 服务身份验证 (POC)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (18投票s)

2008年7月6日

CPOL

7分钟阅读

viewsIcon

202087

这是一篇概念验证(POC)文章,旨在解释如何在用户需要请求 Web 服务时实现 Kerberos 身份验证来验证用户。

引言

本文是一篇概念验证(POC)文章。它解释了如何使用 WSE 3.0 实现 Kerberos 身份验证来验证用户请求 Web 服务时的身份。这个目标非常重要,尤其是在使用面向服务架构(SOA)时。在阅读了 Microsoft 的一项模式与实践:《Web 服务安全指南》后,我决定撰写这篇文章,这是一份关于此问题的非常有价值的参考资料。

下面的文章不会详细描述 Kerberos 身份验证的工作原理,并假设读者对此有一定的了解。它侧重于如何使用 Microsoft 技术来实现它。单点登录(SSO)实现是此概念的主要用途。例如:假设我们有两个域用户,一个市场营销组用户和一个会计组用户。他们每个人都可以登录到自己的 Windows 账户,然后打开公司网站,而无需针对公司网站进行身份验证。登录只需要在登录 Windows 时进行一次。当然,公司网站只会为每个用户提供其组权限。简单来说,我们的系统中将有以下参与者:

  • Web 服务:负责验证任何需要请求它的客户端。
  • 客户端:需要请求 Web 服务;在请求 Web 服务时应提供身份验证凭据。
  • Kerberos 分发中心(KDC):负责验证客户端并 issuing 一个包含客户端凭据的票据,然后客户端可以使用它来与 Web 服务进行身份验证。对于 Microsoft 环境,KDC 在 Windows Server 2003 中可用,例如,它是一个域控制器。

上图显示了我们系统参与者之间的关系。现在,我们将文章分为两部分:

  • 第一部分:实现演示所需的基础知识。它可以用以下几点来说明:
    • Kerberos 身份验证过程概述。
    • 准备 Web 服务和 IIS 配置。
    • 在 Web 服务上应用 Kerberos 身份验证。
    • 在客户端应用程序上应用 Kerberos 身份验证。
  • 第二部分:根据第一部分描述一个非常简单的演示。

第一部分

Kerberos 身份验证过程概述

简而言之,当客户端需要请求服务时,它会执行五个步骤,如下图所示:

我想更详细地解释步骤 2 和 4。

  • 在步骤 2 中,KDC 执行以下操作:
    • 生成一个新的会话密钥。
    • 将新生成的会话密钥和一些客户端数据打包到服务票据中。
    • 使用 Web 服务的母密钥(Web 服务知道)加密服务票据。
    • 将加密的服务票据和新的会话密钥发送给客户端。

    然后,客户端执行以下操作:

    • 使用会话密钥加密验证器(包含时间戳和其他信息)。
    • 将加密的验证器和服务票据打包到新的 Kerberos 安全令牌中。
  • 在步骤 4 中,Web 服务执行以下操作:
    • 从 Kerberos 令牌中获取服务票据。
    • 使用其母密钥解密它。
    • 从解密的服务票据中获取会话密钥。
    • 使用会话密钥解密验证器并验证文本。

准备 Web 服务和 IIS 配置

在本节中,我们将更详细地了解母密钥以及如何拥有一个具有唯一母密钥的 Web 服务。什么是用于加密服务票据的母密钥?在 Windows 服务器中:KDC 上注册的每个对象(计算机或用户)都有一个共享密钥(也称为母密钥)。此共享密钥用于加密服务票据,也用于解密它。该对象通过一个唯一的名称 SPN(服务主体名称)在 KDC 上注册,因此在向 KDC 请求票据时,应确定 SPN。默认情况下,Windows 上运行的所有服务都使用内置的 Network Service 账户,并且引用它的默认 SPN 是“host/PCName”。IIS 的默认应用程序池使用 Network Service 账户向 Windows 标识自己。这意味着,当为 IIS 上的 Web 服务生成服务票据时,它将使用与“Network Service”账户相关的共享密钥进行加密,然后使用相同应用程序池的任何 Web 服务都可以解密该票据。

要为 Web 服务分配一个新的母密钥,您需要执行以下操作:

  1. 向 Active Directory 添加一个新的域账户。
  2. 创建一个新的 SPN 来引用新的域账户。
  3. 在 IIS 中添加一个新的应用程序池,并将其标识映射到新的域账户。
  4. 配置 Web 服务虚拟目录以使用新的应用程序池。
  5. 授予新账户访问 Web 服务虚拟目录所需的所有权限,并使其能够作为 IIS_WPG 组的成员。
  6. 重启 IIS。

详细说明

创建新的 SPN 来引用新的域账户

Setspn 工具包含在 Windows Support Tools 中,用于管理 SPN。

在命令提示符下键入以下命令:Setspn –a http/ServiceName DomainName\DomainAccount,其中:

  • ServiceName:是 Web 服务的名称。
  • DomainName:是域的名称。
  • DomainAccount:是新的域账户名。

在 IIS 中添加新的应用程序池,并将其标识映射到新的域账户

配置 Web 服务虚拟目录以使用新的应用程序池

授予新账户访问 Web 服务虚拟目录并作为 IIS_WPG 组的成员所需的所有权限
  • 将新域账户添加到 IIS_WPG 组。
  • 为该新域账户分配以下两个用户权限以启动 CGI 进程:Adjust memory quotas for a process(调整进程的内存配额)和 Replace a process level token(替换进程级别令牌)。

  • 授予新域账户对 Web 服务文件夹的完全控制权限。
  • 授予新域账户对 Windows 目录下的 temp 文件夹的完全控制权限。

在 Web 服务上应用 Kerberos 身份验证

要在 Web 服务中使用 Kerberos 身份验证:

  1. 启用 WSE 3.0,并启用策略。
  2. 添加策略文件并配置策略。
  3. 将策略应用于 Web 服务。

详细说明

  1. 启用 WSE 3.0 并启用策略:通过在 web.config 文件中添加以下标签:
    <configSections>
      <section name="microsoft.web.services3" 
        type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration,
             Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
             PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    
    <system.web>
    
      <compilation debug="true">
        <assemblies>
          <add assembly="Microsoft.Web.Services3, Version=3.0.0.0,
    Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        </assemblies>
      </compilation>
    
      <webServices>
        <soapExtensionImporterTypes>
          <add type="Microsoft.Web.Services3.Description.WseExtensionImporter,
                        Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
                        PublicKeyToken=31bf3856ad364e35" />
        </soapExtensionImporterTypes>
        <soapServerProtocolFactory 
          type="Microsoft.Web.Services3.WseProtocolFactory,Microsoft.Web.Services3,
                Version=3.0.0.0,Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </webServices>
    </system.web>
    
    <microsoft.web.services3>
      <policy fileName="wse3policyCache.config" />
      <tokenIssuer>
        <statefulSecurityContextToken enabled="false" />
      </tokenIssuer>
    </microsoft.web.services3>
  2. 添加策略文件并配置策略:向您的项目添加一个名为“wse3policyCache.config”的配置文件,然后添加以下标签:
    <policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
      <policy name="KerberosService">
        <authorization>
          <allow user="Mawhiba\Akram" />
          <deny role="*" />
        </authorization>
        <kerberosSecurity establishSecurityContext="true"
        renewExpiredSecurityContext="true" requireSignatureConfirmation="false"
        messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"
        requireDerivedKeys="true" ttlInSeconds="300">
          <protection>
            <request 
               signatureOptions="IncludeAddressing, IncludeTimestamp, 
                                 IncludeSoapBody" 
               encryptBody="true" />
            <response signatureOptions="IncludeAddressing, IncludeTimestamp, 
                                        IncludeSoapBody" 
                      encryptBody="true" />
            <fault signatureOptions="IncludeAddressing, IncludeTimestamp, 
                                     IncludeSoapBody" 
                   encryptBody="false" />
          </protection>
        </kerberosSecurity>
        <requireActionHeader />
      </policy>
    </policies>
    

    授权部分可以根据业务角色进行更改。

  3. 将策略应用于 Web 服务:通过在服务类之前添加以下代码:
    [Policy("KerberosService")]

在客户端应用程序上应用 Kerberos 身份验证

要在客户端中使用 Kerberos 身份验证:

  1. 启用 WSE 3.0,并启用策略。
  2. 添加策略文件并配置策略。
  3. 使用 Web 服务的增强版本并在客户端应用策略。

详细说明

  1. 启用 WSE 3.0 并启用策略:通过在 web.config 文件中添加以下标签:
    <configSections>
      <section name="microsoft.web.services3" 
         type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration,
                Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
                PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    
    <system.web>
    
      <compilation debug="true">
        <assemblies>
          <add assembly="Microsoft.Web.Services3, Version=3.0.0.0,
    Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        </assemblies>
      </compilation>
    
      <webServices>
        <soapExtensionImporterTypes>
          <add type="Microsoft.Web.Services3.Description.WseExtensionImporter,
                        Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
                        PublicKeyToken=31bf3856ad364e35" />
        </soapExtensionImporterTypes>
      </webServices>
    </system.web>
    
    <microsoft.web.services3>
      <policy fileName="wse3policyCache.config" />
    </microsoft.web.services3>
  2. 添加策略文件并配置策略:向您的项目添加一个名为“wse3policyCache.config”的配置文件,然后添加以下标签:
    <policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
      <policy name="KerberosClient">
        <kerberosSecurity establishSecurityContext="true"
        renewExpiredSecurityContext="true" requireSignatureConfirmation="false"
        messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"
        requireDerivedKeys="true" ttlInSeconds="300">
          <token>
            <kerberos targetPrincipal="http/ServiceName"
            impersonationLevel="Impersonation" />
          </token>
          <protection>
            <request 
              signatureOptions="IncludeAddressing, 
                                IncludeTimestamp, IncludeSoapBody" 
              encryptBody="true" />
            <response signatureOptions="IncludeAddressing, 
                                        IncludeTimestamp, IncludeSoapBody" 
               encryptBody="true" />
            <fault signatureOptions="IncludeAddressing, 
                                     IncludeTimestamp, IncludeSoapBody" 
               encryptBody="false" />
          </protection>
        </kerberosSecurity>
        <requireActionHeader />
      </policy>
    </policies>

    其中,“http/ServiceName”是所需服务的 SPN。

  3. 使用 Web 服务的增强版本并在客户端应用策略:使用“Wse”版本,如下面的代码所示:
    Client.localhost.ServiceWse myService = new ServiceWse(); 
    myService.SetPolicy("KerberosClient");

第二部分

好的,我们现在可以开始我们的简单项目来演示 Kerberos 身份验证了。

  1. 创建两个具有不同母密钥的 Web 服务(webservice1webservice2)。
  2. 应用 Kerberos 身份验证,并为每个 Web 服务添加允许某些用户访问并拒绝其他用户访问的策略。
    <authorization>
      <allow user="CodeProject\Akram" />
      <deny role="*" />
    </authorization>
  3. 在两个 Web 服务中,编写一个测试方法,如下所示:
        [WebMethod]
        public string Test() 
        {
            return "Succeeded ";
        }
  4. 创建一个控制台应用程序并添加对这两个 Web 服务的引用。
  5. 将这两个 Kerberos 身份验证策略都应用到它(一个策略用于请求 webservice1,名为 'KerberosClient1';另一个用于请求 webservice2,名为 'KerberosClient2')。策略文件将是这样的:
    <policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
      <policy name="KerberosClient1">
        <kerberosSecurity establishSecurityContext="true"
        renewExpiredSecurityContext="true" requireSignatureConfirmation="false"
        messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"
        requireDerivedKeys="true" ttlInSeconds="300">
          <token>
            <kerberos targetPrincipal="http/WS1"
            impersonationLevel="Impersonation" />
          </token>
          <protection>
            <request signatureOptions="IncludeAddressing, 
                                       IncludeTimestamp, IncludeSoapBody" 
                     encryptBody="true" />
            <response signatureOptions="IncludeAddressing, 
                                        IncludeTimestamp, IncludeSoapBody" 
                      encryptBody="true" />
            <fault signatureOptions="IncludeAddressing, 
                                     IncludeTimestamp, IncludeSoapBody" 
                   encryptBody="false" />
          </protection>
        </kerberosSecurity>
        <requireActionHeader />
      </policy>
    
      <policy name="KerberosClient2">
        <kerberosSecurity establishSecurityContext="true"
        renewExpiredSecurityContext="true" requireSignatureConfirmation="false"
        messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"
        requireDerivedKeys="true" ttlInSeconds="300">
          <token>
            <kerberos targetPrincipal="http/WS2"
            impersonationLevel="Impersonation" />
          </token>
          <protection>
            <request signatureOptions="IncludeAddressing, 
                                       IncludeTimestamp, IncludeSoapBody" 
                     encryptBody="true" />
            <response signatureOptions="IncludeAddressing, 
                                        IncludeTimestamp, IncludeSoapBody" 
                      encryptBody="true" />
            <fault signatureOptions="IncludeAddressing, 
                                     IncludeTimestamp, IncludeSoapBody" 
                   encryptBody="false" />
          </protection>
        </kerberosSecurity>
        <requireActionHeader />
      </policy>
    </policies>

    这个控制台应用程序没有 web.config 文件,所以我们可以使用 app.config 文件来启用 WSE。修改并添加 Web 引用后,App.config 将如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="microsoft.web.services3" 
          type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration,
                Microsoft.Web.Services3,Version=3.0.0.0, Culture=neutral, 
                PublicKeyToken=31bf3856ad364e35" />
        <sectionGroup name="applicationSettings" 
                type="System.Configuration.ApplicationSettingsGroup, System, 
                      Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
          <section name="Client.Properties.Settings" 
                type="System.Configuration.ClientSettingsSection, System, 
                      Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
                requirePermission="false" />
        </sectionGroup>
      </configSections>
      <microsoft.web.services3>
        <policy fileName="wse3policyCache.config" />
      </microsoft.web.services3>
      <applicationsettings />
        <client.properties.settings />
          <setting name="Client_localhost_Service" serializeAs="String">
            <value />https:///WS_Kerb_Demo/Service.asmx</value>
          </setting>
          <setting name="Client_localhost2_Service" serializeAs="String">
            <value>https:///WS_Kerb_Demo2/Service.asmx</value>
          </setting>
        </Client.Properties.Settings>
      </applicationSettings>
    </configuration>
  6. 编写一些代码,尝试使用这两个策略请求这两个 Web 服务,如下所示:
    class Program
    {
        static void Main(string[] args)
        {
            localhost1.ServiceWse service1 = new localhost1.ServiceWse();
            localhost2.ServiceWse service2 = new localhost2.ServiceWse();
    
            service1.SetPolicy("KerberosClient1");            
    
            Console.WriteLine("");
            Console.WriteLine("(1) Token with first SPN " + 
                              "and calling the first service ");
    
            try
            {
                Console.WriteLine(service1.Test());
            }
            catch
            {
                Console.WriteLine("Failed");
            }
            Console.ReadLine();
    
            Console.WriteLine("");
    
            service2.SetPolicy("KerberosClient1");
            Console.WriteLine("(2) Token with first SPN " + 
                              "and calling the secound service ");
    
            try
            {
                Console.WriteLine(service2.Test());
            }
            catch
            {
                Console.WriteLine("Failed ");
            }
            Console.ReadLine();
    
            Console.WriteLine("");
    
            service1.SetPolicy("KerberosClient2"); 
            Console.WriteLine("(3) Token with secound SPN" + 
                              " and calling the first service ");
    
            try
            {
                Console.WriteLine(service1.Test());
            }
            catch
            {
                Console.WriteLine("Failed ");
            }
            Console.ReadLine();
    
            Console.WriteLine("");
    
            service2.SetPolicy("KerberosClient2");
            Console.WriteLine("(4) Token with secound SPN" + 
                              " and calling the secound service ");
    
            try
            {
                Console.WriteLine(service2.Test());
            }
            catch
            {
                Console.WriteLine("Failed ");
            }
            Console.ReadLine();
            
        }
    }
  7. 运行应用程序,您将看到以下输出:

OutPut.JPG

谢谢

我希望这篇文章对某些人有所帮助。这是我在 The Code Project 上的第一篇文章,希望不是最后一篇。最后,我想感谢位于沙特阿拉伯利雅得的 Arabian Advanced Systems (AAS),我从中获得了文章的大部分知识,感谢他们的支持和团队合作。

© . All rights reserved.