如何使用安全字符串密码从 C# 程序登录 XEN 会话?

How do I login to a XEN session from a C# program using a secure string password?

我正在使用 PowerShell 5.1、Visual Studio 2017、C# 和 XenServer SDK 7.1.1。

在 PowerShell 程序中使用 Get-Credentials 和 Export-CliXml,我已将 root 用户的池主服务器登录凭据保存到 XML 凭据文件 (xml_creds.xml)

现在,我想使用 C# 创建并登录会话(请参见下面的代码)。如您所见,我被迫将我的安全字符串转换为纯文本字符串以满足 Xen .NET API 的 login_with_password 方法的签名。

使用 API,如何使用安全字符串登录会话?

代码

try
{

    securedPassword = new SecureString();
    string unsecuredPassword = "";

    Runspace rs = RunspaceFactory.CreateRunspace();
    rs.Open();

    Pipeline pipeline = rs.CreatePipeline(@"Import-CliXml 'C:\foo\xml_creds.xml';");

    Collection<PSObject> results = pipeline.Invoke();

    if (results.Count == 1)
    {
        PSObject psOutput = results[0];

        securedPassword = ((PSCredential)psOutput.BaseObject).Password;
        unsecuredPassword = new System.Net.NetworkCredential(string.Empty, securedPassword).Password;
        username = ((PSCredential)psOutput.BaseObject).UserName;

        rs.Close();

        session = new Session(hostname, port);

        session.login_with_password(username, unsecuredPassword, API_Version.API_1_3);
    }
    else
    {
        throw new System.Exception("Could not obtain pool master server credentials");
    }
}
catch (Exception e1)
{
    System.Console.WriteLine(e1.Message);
}
finally
{
    if (securedPassword != null)
    {
        securedPassword.Dispose();
    }

    if (session != null)
    {
        session.logout(session);
    }
}

我联系了 Citrix。

The Xen API does not provide a mechanism for logging into a session using a secure string password.

因此,我最终使用了一个 C# 程序,该程序执行两个支持安全字符串密码的 PowerShell 脚本。

查看下面的代码。

Notes:

I have the 7.1.1 XenServerPSModule installed in %USERPROFILE%\Documents\WindowsPowerShell\Modules\XenServerPSModule. This module provides the Connect-XenServer cmdlet

I created the xml credentials file using PowerShell get-credentials followed by using export-clixml

I loaded the requisite System.Management.Automation reference by installing Microsoft.PowerShell.5.ReferenceAssemblies from NuGet

form1.cs(表单只有一个按钮)

using System;
using System.Windows.Forms;
using XenSnapshotsXenAccess;

namespace Create_XenSnapshotsUi
{
    public partial class Form1 : Form
    {
        XenSessionAccess xenSession = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Button1_Click(object sender, EventArgs e)
        {

            xenSession = new XenSessionAccess("https://xxx.xx.x.x", @"C:\foo\xml_credentials.xml");

            xenSession.Logout();

        }
    }
}

XenSessionAccess class

using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using XenAPI;

namespace XenSnapshotsXenAccess
{
    public class XenSessionAccess
    {
        private Session xenSession = null;

        public Session XenSession { get => xenSession; set => xenSession = value; }

        public void Logout()
        {
            if (XenSession != null)
            {
                XenSession.logout(XenSession);
            }
        }

        public XenSessionAccess(string poolMasterServerUrl, string xml_creds_path)
        {
            Collection<PSObject> results = null;
            PSCredential psCredential = null;

            //https://docs.microsoft.com/en-us/powershell/developer/hosting/creating-an-initialsessionstate

            //Createdefault2* loads only the commands required to host Windows PowerShell (the commands from the Microsoft.PowerShell.Core module.
            InitialSessionState initialSessionState = InitialSessionState.CreateDefault2();

            using (Runspace runSpace = RunspaceFactory.CreateRunspace(initialSessionState))
            {
                runSpace.Open();

                using (PowerShell powerShell = PowerShell.Create())
                {
                    powerShell.Runspace = runSpace;
                    powerShell.AddCommand("Import-CliXml");

                    powerShell.AddArgument(xml_creds_path);
                    results = powerShell.Invoke();

                    if (results.Count == 1)
                    {
                        PSObject psOutput = results[0];
                        //cast the result to a PSCredential object
                        psCredential = (PSCredential)psOutput.BaseObject;
                    }
                    else
                    {
                        throw new System.Exception("Could not obtain pool master server credentials");
                    }
                }

                runSpace.Close();
            }

            initialSessionState = InitialSessionState.CreateDefault2();
            initialSessionState.ImportPSModule(new string[] { "XenServerPSModule" });
            initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;

            SessionStateVariableEntry psCredential_var = new SessionStateVariableEntry("psCredential", psCredential, "Credentials to log into pool master server");
            initialSessionState.Variables.Add(psCredential_var);

            SessionStateVariableEntry poolUrl_var = new SessionStateVariableEntry("poolUrl", poolMasterServerUrl, "Url of pool master server");
            initialSessionState.Variables.Add(poolUrl_var);

            using (Runspace runSpace = RunspaceFactory.CreateRunspace(initialSessionState))
            {
                runSpace.Open();

                using (PowerShell powerShell = PowerShell.Create())
                {
                    powerShell.Runspace = runSpace;
                    powerShell.AddScript(@"$psCredential | Connect-XenServer -url $poolUrl -SetDefaultSession -PassThru");
                    results = powerShell.Invoke();
                }

                if (results.Count == 1)
                {
                    PSObject psOutput = results[0];
                    //cast the result to a XenAPI.Session object
                    XenSession = (Session)psOutput.BaseObject;
                }
                else
                {
                    throw new System.Exception(String.Format("Could not create session for {0}", poolMasterServerUrl));
                }

                runSpace.Close();
            }
        }
    }
}