Yubikey 和在 C# 中的 Active Directory 中执行管理任务

问题描述 投票:0回答:1

我构建了一个应用程序,可以帮助我完成一些任务,例如将用户添加到组、删除、创建用户、删除用户以及在 Active Directory 中解锁用户。这些都需要一些管理员权限。

目前,它验证管理员帐户连接到域的方式是基于密码的身份验证。

系统会提示用户提供用户名和密码,并使用

PrincipalContext
我对会话进行身份验证。在整个应用程序中,我引用
context
变量来执行这些任务。

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, _myDomainName, adminUsername, adminPassword))

我想检查应用程序的登录情况以使用智能卡。我能够使用它,但据我所知,

PrincipalContext
似乎只支持基于密码的身份验证,不支持基于证书的身份验证。这导致
context
变量无法在会话中引用我的管理员帐户,从而导致“访问被拒绝”错误。

目前我像这样验证用户身份:

static X509Certificate2 GetAdminCertificate()
{
    Console.Write("Enter admin username: ");
    adminUsername = Console.ReadLine().Trim();
    /*
    Console.Write("Enter your smart card PIN: ");
    string smartCardPin = PasswordManager.GetPassword().Trim();
    */

    using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
    {
        store.Open(OpenFlags.ReadOnly);
        
        X509Certificate2Collection yubiKeyCerts = new X509Certificate2Collection();

        foreach (var cert in store.Certificates)
        {
            // First, check if the subject name matches the admin username
            string subjectName = cert.GetNameInfo(X509NameType.SimpleName, false);

            if (!subjectName.Equals(adminUsername, StringComparison.OrdinalIgnoreCase))
            {
                // Skip this certificate if the subject does not match
                continue;
            }

            try
            {
                if (cert.HasPrivateKey)
                {
                    // Access the private key
                    var privateKey = cert.PrivateKey;

                    if (privateKey is RSACng rsaCng)
                    {
                        if (rsaCng.KeyExchangeAlgorithm.Equals("RSA", StringComparison.OrdinalIgnoreCase))
                        {
                            // Match the certificate with admin username
                            if (subjectName.Equals(adminUsername, StringComparison.OrdinalIgnoreCase))
                            {
                                // Attempt to sign data with the RSA key to validate access
                                byte[] dataToSign = new byte[] { 0x01 }; // Dummy data
                                byte[] signedData = rsaCng.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                                yubiKeyCerts.Add(cert);
                            }  // end of if-statement
                        }  // end of if-statement 
                    }  // end of if-statement
                }  // end of if-statement
            }  // end of try
            catch (Exception ex)
            {
                Console.WriteLine($"Error obtaining certification: {ex.Message}");
                continue;
            }  // end of catch
        }  // end of foreach

        if (yubiKeyCerts.Count == 0)
        {
            Console.WriteLine("No smart card detected or no valid certificates found on the connected smart card.");
            return null;
        }

        // X509Certificate2 selectedCert = null;
        if (yubiKeyCerts.Count > 0)
        {
            selectecdCert = X509Certificate2UI.SelectFromCollection(
                yubiKeyCerts,
                "Select a YubiKey certificate",
                "Please select your admin certificate from the YubiKey",
                X509SelectionFlag.SingleSelection
            )[0];
        }

        adminUsername = selectecdCert.GetNameInfo(X509NameType.SimpleName, false);
        // adminPassword = selectecdCert.Verify();

        return selectecdCert;
    }  // end of using x509Store
}  // end of GetAdminCertificate

(太多 if 语句 - 我知道)

应用程序通过以下方式连接到域:

static void Main(string[] args)
{
      ActiveDirectoryManager ADManager = new ActiveDirectoryManager();
      AccountCreationManager ACManager;
      AccountDeactivationManager ACCDeactivationManager = new AccountDeactivationManager();
      PasswordManager PWDManager = null;
      ADGroupActionManager ADGroupManager = null;
      AuditLogManager auditLogManager = null;

      configuration = new ConfigurationBuilder()
          .SetBasePath(Directory.GetCurrentDirectory())
          .AddJsonFile("Appsettings.json", optional: false, reloadOnChange: true)
          .Build();

      EmailNotifcationManager emailManager = new EmailNotifcationManager(configuration);
      string _myDomainName = configuration["AccountCreationSettings:myDomainName"];

      do
      {
          X509Certificate2 certificate = GetAdminCertificate();

          if (certificate == null)
          {
              Console.WriteLine("No valid smart card certificate found.");
              return;
          }

          try
          {
              using (PrincipalContext context = new PrincipalContext(ContextType.Domain, _myDomainName))                                                          
              // Check if the the password/user are correct
              {
                  // Throw error if the password/username is incorrect        
                  if (context.ConnectedServer != null)                                                                                                                
                  {
                      isAuthenticated = true;
                      Console.WriteLine($"Connected to Active Directory as: {adminUsername}.".Pastel(Color.GreenYellow));

                      auditLogManager = new AuditLogManager(adminUsername, configuration);
                      ADGroupManager = new ADGroupActionManager(auditLogManager);
                      PWDManager = new PasswordManager(auditLogManager);
                      ACManager = new AccountCreationManager(auditLogManager, configuration);

                      bool exit = false;

                      // Loop the menu
                      while (!exit)                                                                                                                                           
                      {
                          DisplayMainMenu();
                          string choice = Console.ReadLine();
                          exit = HandleMainMenuChoice(choice, context, ADManager, ADGroupManager, PWDManager, ACManager, ACCDeactivationManager);
                      }   // end of while-loop
                  }    // end of if statement

                  context.Dispose();
              }   // end of using
          }   // end of try
          catch (DirectoryServicesCOMException)                                                                                                                                         
          {
              // Error out if password/username are incorrect
              Console.WriteLine("Error: Unable to connect to the Active Directory server. Please check your credentials and try again.".Pastel(Color.IndianRed));
          }
          catch (Exception ex)
          {
              Console.WriteLine($"An error occurred: {ex.Message}".Pastel(Color.IndianRed));
          }    // end of Catch
      } while (!isAuthenticated || string.IsNullOrEmpty(adminUsername));                                                                                                                                              
        // Repeat until a valid password is entered
}  // end of Main Method

访问被拒绝的代码片段示例。正如您所看到的,我在此处引用了

context
变量来使用管理会话来执行这些任务。

UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);                                              

// Check for user in AD
if (user != null)
{
     // Check for group in AD
     GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupName);                                                                        

     if (group != null)
     {
         // If the user is not in the group add him
         if (!group.Members.Contains(user))                                                                                                               
         {
             // Add the user to the group
             group.Members.Add(user);
             // Apply changes
             group.Save();                                                                                                                       

             group.Dispose();

             Console.WriteLine($"User '{username}' added to group '{groupName}' successfully.".Pastel(Color.LimeGreen));

             string logEntry = ($"\"{user.DisplayName}\" has been added to \"{groupName}\" group in Active Directory\n");
             emailActionLog.Add(logEntry);
             auditLogManager.Log(logEntry);
         }   // end of inner-2 if-statement
         else
         {
             Console.WriteLine($"User '{username}' is already a member of group '{groupName}'.".Pastel(Color.DarkGoldenrod));
         }   // end of inner-2 else-statement
     }   // end of inner if-statement
     else
     {
         Console.WriteLine($"Group '{groupName}' not found in Active Directory.".Pastel(Color.IndianRed));
     }   // end of outer else-statement
}

我的主要问题是,有没有一种方法可以通过基于认证的登录来验证我的管理员帐户?

c# active-directory windows-authentication smartcard yubico
1个回答
0
投票

您可以;

  1. 设置证书颁发机构 (CA) 并颁发智能卡证书或

  2. 从第三方证书提供商获取智能卡证书

在第一个选项中,如果您设置了 Microsoft CA,它可以颁发智能卡证书,您可以直接使用该证书登录 AD(Active Directory)域,它会将 CA 视为受信任的根权限并自动映射 UPN(用户主体)名称)证书的一部分(应位于 CN 字段)给 AD 用户。

在第二个选项中,您应该将提供商的根 CA 分发到客户端 PC,并按照以下步骤进行操作 https://learn.microsoft.com/en-us/troubleshoot/windows-server/certificates-and-public-密钥基础设施-pki/导入第三方-ca-至企业-ntauth-store。智能卡登录需要此过程。

在这两个选项中,应用程序将运行的客户端 PC 都应该是 AD 域成员。

在您的应用程序中,我认为您应该检查CN(通用名称)字段中的UPN信息,并检查智能卡证书链的根证书是否受到操作系统的信任。那么 UPN 可能应该映射到 AD 用户。

© www.soinside.com 2019 - 2024. All rights reserved.