Sources/SecurityFever.CredentialManager.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
 
namespace SecurityFever.CredentialManager
{
    /// <summary>
    /// Type of credential.
    /// </summary>
    public enum CredentialType
    {
        Generic = 0x01,
        DomainPassword = 0x02,
        DomainCertificate = 0x03,
        DomainVisiblePassword = 0x04,
        GenericCertificate = 0x05,
        DomainExtended = 0x06,
        Maximum = 0x07,
        MaximumEx = Maximum + 1000
    }
 
    /// <summary>
    /// Type of credential persistence.
    /// </summary>
    public enum CredentialPersist : uint
    {
        Session = 0x01,
        LocalMachine = 0x02,
        Enterprise = 0x03
    }
 
    /// <summary>
    /// Helper class having native methods to manage credential manager entries.
    /// </summary>
    internal static class NativeMethods
    {
        private const string Advapi32 = "advapi32.dll";
        private const string Kernel32 = "kernel32.dll";
 
        #region advapi32.dll
        /// <summary>
        /// <para>The CredWrite function creates a new credential or modifies an existing
        /// credential in the user's credential set. </para>
        /// <para>The new credential is associated with the logon session of the current token. </para>
        /// <para>The token must not have the user's security identifier (SID) disabled.</para>
        /// </summary>
        /// <param name="credential">A pointer to the `<see cref="Credential)"/>` structure to be
        /// written.</param>
        /// <param name="flags">Flags that control the function's operation. Must be set to 0.</param>
        /// <returns>True if success; false otherwise.</returns>
        [DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredWriteW", SetLastError = true)]
        internal static extern bool CredWrite(ref Credential credential, UInt32 flags);
 
        /// <summary>
        /// <para>Reads a credential from the user's credential set. </para>
        /// <para>The credential set used is the one associated with the logon session of the
        /// current token. </para>
        /// <para>The token must not have the user's SID disabled.</para>
        /// </summary>
        /// <param name="targetName">Pointer to a null-terminated string that contains the name of
        /// the credential to read.</param>
        /// <param name="type">Type of the credential to read. Type must be one of the `<see cref="CredentialType"/>`
        /// defined types.</param>
        /// <param name="flags">Currently reserved and must be zero.</param>
        /// <param name="credential">
        /// <para>Pointer to a single allocated block buffer to return the credential. </para>
        /// <para>Any pointers contained within the buffer are pointers to locations within this
        /// single allocated block. </para>
        /// <para>The single returned buffer must be freed by calling `<see cref="CredFree(IntPtr)"/>`.</para>
        /// </param>
        /// <returns>True if success; false otherwise.</returns>
        [DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredReadW", SetLastError = true)]
        internal static extern bool CredRead(string targetName, CredentialType type, uint flags, out IntPtr credential);
 
        /// <summary>
        /// <para>The CredDelete function deletes a credential from the user's credential set.</para>
        /// <para>The credential set used is the one associated with the logon session of the
        /// current token. </para>
        /// <para>The token must not have the user's SID disabled.</para>
        /// </summary>
        /// <param name="targetName">Pointer to a null-terminated string that contains the name of
        /// the credential to delete.</param>
        /// <param name="type">
        /// <para>Type of the credential to delete. Must be one of the `<see cref="CredentialType"/>`
        /// defined types. For a list of the defined types, see the Type member of the `<see cref="Credential"/>`
        /// structure.</para>
        /// <para>If the value of this parameter is `<see cref="CredentialType.DomainExtended"/>`,
        /// this function can delete a credential that specifies a user name when there are
        /// multiple credentials for the same target, and the value of the `<see cref="Credential.TargetName"/>`
        /// parameter must specify the user name as Target|UserName.</para>
        /// </param>
        /// <param name="flags">Reserved and must be zero.</param>
        /// <returns>True if success; false otherwise.</returns>
        [DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredDeleteW", SetLastError = true)]
        internal static extern bool CredDelete(string targetName, CredentialType type, uint flags);
 
        /// <summary>
        /// The CredFree function frees a buffer returned by any of the credentials management
        /// functions.
        /// </summary>
        /// <param name="buffer"Pointer to the buffer to be freed.></param>
        [DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredFree", SetLastError = true)]
        internal static extern void CredFree(IntPtr credential);
 
        /// <summary>
        /// Enumerates the credentials from the user's credential set. The credential set used
        /// is the one associated with the logon session of the current token. The token must
        /// not have the user's SID disabled.
        /// </summary>
        /// <param name="targetNameFilter">
        /// <para>Pointer to a null-terminated string that contains the filter for the returned
        /// credentials. Only credentials with a TargetName matching the filter will be returned.
        /// The filter specifies a name prefix followed by an asterisk. For instance, the filter
        /// "FRED*" will return all credentials with a TargetName beginning with the string
        /// "FRED".</para>
        /// <para>If <see langword="null"/> is specified, all credentials will be returned.</para>
        /// </param>
        /// <param name="flags">The value of this parameter can be zero or more values combined
        /// with a bitwise-OR operation.</param>
        /// <param name="count">Count of the credentials returned in the <paramref name="credentialsArrayPtr"/>.</param>
        /// <param name="credentialsArrayPtr">
        /// <para>Pointer to an array of pointers to credentials. The returned credential is a
        /// single allocated block. Any pointers contained within the buffer are pointers to
        /// locations within this single allocated block.</para>
        /// <para>The single returned buffer must be freed by calling <see cref="CredFree"/>.</para>
        /// </param>
        /// <returns></returns>
        [DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredEnumerateW", SetLastError = true)]
        internal static extern bool CredEnumerate(string targetNameFilter, CredentialEnumerateFlags flags, out int count, out IntPtr credentialsArrayPtr);
 
        [Flags]
        internal enum CredentialEnumerateFlags : uint
        {
            None = 0,
            AllCredentials = 1 << 0,
        }
 
        [Flags]
        internal enum CredentialFlags : uint
        {
            /// <summary>
            /// <para>Bit set if the `<see cref="Credential"/>` does not persist the `<see cref="Credential.CredentialBlob"/>`
            /// and the credential has not been written during this logon session. This bit is
            /// ignored on input and is set automatically when queried.</para>
            /// <para>If `<see cref="Credential.Type"/>` is <see cref="CredentialType.DomainCertificate"/>,
            /// the `<see cref="Credential.CredentialBlob"/>` is not persisted across logon
            /// sessions because the PIN of a certificate is very sensitive information.</para>
            /// <para>Indeed, when the credential is written to credential manager, the PIN is
            /// passed to the CSP associated with the certificate. The CSP will enforce a PIN
            /// retention policy appropriate to the certificate.</para>
            /// <para>If Type is `<see cref="CredentialType.DomainPassword"/>` or
            /// `<see cref="CredentialType.DomainCertificate"/>`, an authentication package always
            /// fails an authentication attempt when using credentials marked as `<see cref="CredentialFlags.PromptNow"/>`.</para>
            /// <para>The application (typically through the key ring UI) prompts the user for the
            /// password. The application saves the credential and retries the authentication. </para>
            /// <para>Because the credential has been recently written, the authentication package
            /// now gets a credential that is not marked as `<see cref="CredentialFlags.PromptNow"/>`.</para>
            /// </summary>
            PromptNow = 0x02,
            /// <summary>
            /// <para>Bit is set if this `<see cref="Credential"/>` has a `<see cref="Credential.TargetName"/>`
            /// member set to the same value as the `<see cref="Credential.UserName"/>` member.</para>
            /// <para>Such a credential is one designed to store the `<see cref="Credential.CredentialBlob"/>`
            /// for a specific user.</para>
            /// <para>This bit can only be specified if `<see cref="Credential.Type"/>` is
            /// `<see cref="CredentialType.DomainPassword"/>` or <see cref="CredentialType.DomainCertificate"/>.</para>
            /// </summary>
            UsernameTarget = 0x04,
        }
 
        internal enum CredentialPersist : uint
        {
            /// <summary>
            /// <para>The `<see cref="Credential"/>` persists for the life of the logon session.</para>
            /// <para>It will not be visible to other logon sessions of this same user.</para>
            /// <para>It will not exist after this user logs off and back on.</para>
            /// </summary>
            Session = 0x01,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` persists for all subsequent logon sessions on
            /// this same computer.</para>
            /// <para>It is visible to other logon sessions of this same user on this same computer
            /// and not visible to logon sessions for this user on other computers.</para>
            /// </summary>
            /// <remarks>
            /// Windows Vista Home Basic, Windows Vista Home Premium, Windows Vista Starter, and
            /// Windows XP Home Edition: This value is not supported.
            /// </remarks>
            LocalMachine = 0x02,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` persists for all subsequent logon sessions on
            /// this same computer. </para>
            /// <para>It is visible to other logon sessions of this same user on this same computer
            /// and to logon sessions for this user on other computers.</para>
            /// </summary>
            /// <remarks>
            /// Windows Vista Home Basic, Windows Vista Home Premium, Windows Vista Starter, and
            /// Windows XP Home Edition: This value is not supported.
            /// </remarks>
            Enterprise = 0x03
        }
 
        internal enum CredentialType : uint
        {
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is a generic credential. The credential will
            /// not be used by any particular authentication package.</para>
            /// <para>The credential will be stored securely but has no other significant
            /// characteristics.<para>
            /// </summary>
            Generic = 0x01,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is a password credential and is specific to
            /// Microsoft's authentication packages. </para>
            /// <para>The NTLM, Kerberos, and Negotiate authentication packages will automatically
            /// use this credential when connecting to the named target.</para>
            /// </summary>
            DomainPassword = 0x02,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is a certificate credential and is specific to
            /// Microsoft's authentication packages. </para>
            /// <para>The Kerberos, Negotiate, and Schannel authentication packages automatically
            /// use this credential when connecting to the named target.</para>
            /// </summary>
            DomainCertificate = 0x03,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is a password credential and is specific to
            /// authentication packages from Microsoft. </para>
            /// <para>The Passport authentication package will automatically use this credential
            /// when connecting to the named target.</para>
            /// </summary>
            [Obsolete("This value is no longer supported", true)]
            DomainVisiblePassword = 0x04,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is a certificate credential that is a generic
            /// authentication package.</para>
            /// </summary>
            GenericCertificate = 0x05,
            /// <summary>
            /// <para>The `<see cref="Credential"/>` is supported by extended Negotiate packages.</para>
            /// </summary>
            /// <remarks>
            /// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This
            /// value is not supported.
            /// </remarks>
            DomainExtended = 0x06,
            /// <summary>
            /// <para>The maximum number of supported credential types.<para>
            /// </summary>
            /// <remarks>
            /// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This
            /// value is not supported.
            /// </remarks>
            Maximum = 0x07,
            /// <summary>
            /// <para>The extended maximum number of supported credential types that now allow new
            /// applications to run on older operating systems.</para>
            /// </summary>
            /// <remarks>
            /// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This
            /// value is not supported.
            /// </remarks>
            MaximumEx = Maximum + 1000
        }
 
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct Credential
        {
            internal const int AttributeMaxLength = 63;
            internal const int PasswordMaxLength = 2047;
            internal const int StringMaxLength = 255;
            internal const int UsernameMaxLength = 511;
 
            /// <summary>
            /// <para>A bit member that identifies characteristics of the credential. </para>
            /// <para>Undefined bits should be initialized as zero and not otherwise altered to
            /// permit future enhancement.</para>
            /// </summary>
            public CredentialFlags Flags;
            /// <summary>
            /// <para>The type of the credential. This member cannot be changed after the credential
            /// is created. </para>
            /// </summary>
            public CredentialType Type;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string TargetName;
            /// <summary>
            /// <para>A string comment from the user that describes this credential. </para>
            /// <para>This member cannot be longer than `<see cref="StringMaxLength"/>` characters.</para>
            /// </summary>
            [MarshalAs(UnmanagedType.LPWStr)]
            public string Comment;
            /// <summary>
            /// <para>The time, in Coordinated Universal Time (Greenwich Mean Time), of the last
            /// modification of the credential. </para>
            /// <para>For write operations, the value of this member is ignored.</para>
            /// </summary>
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            /// <summary>
            /// <para>The size, in bytes, of the `<see cref="CredentialBlob"/>` member. </para>
            /// <para>This member cannot be larger than `<see cref="PasswordMaxLength"/>` bytes.</para>
            /// </summary>
            public uint CredentialBlobSize;
            /// <summary>
            /// <para>Secret data for the credential. The CredentialBlob member can be both read
            /// and written.</para>
            /// <para>If the `<see cref="Type"/>` member is `<see cref="CredentialType.DomainPassword"/>`,
            /// this member contains the plaintext Unicode password for `<see cref="UserName"/>`.</para>
            /// <para>The `<see cref="CredentialBlob"/>` and `<see cref="CredentialBlobSize"/>`
            /// members do not include a trailing zero character.</para>
            /// <para>Also, for `<see cref="CredentialType.DomainPassword"/>`, this member can only
            /// be read by the authentication packages.</para>
            /// <para>If the Type member is `<see cref="CredentialType.DomainCertificate"/>`, this
            /// member contains the clear test Unicode PIN for `<see cref="UserName"/>`.</para>
            /// <para>The `<see cref="CredentialBlob"/>` and `<see cref="CredentialBlobSize"/>`
            /// members do not include a trailing zero character. </para>
            /// <para>Also, this member can only be read by the authentication packages.</para>
            /// <para>If the `<see cref="Type"/>` member is `<see cref="CredentialType.Generic"/>`,
            /// this member is defined by the application.</para>
            /// <para>Credentials are expected to be portable. Applications should ensure that the
            /// data in `<see cref="CredentialBlob"/>` is portable.</para>
            /// </summary>
            public IntPtr CredentialBlob;
            /// <summary>
            /// <para>Defines the persistence of this credential. This member can be read and
            /// written.</para>
            /// </summary>
            public CredentialPersist Persist;
            /// <summary>
            /// <para>The number of application-defined attributes to be associated with the
            /// credential. </para>
            /// <para>This member can be read and written. Its value cannot be greater than `<see cref="AttributeMaxLength"/>`.</para>
            /// </summary>
            public uint AttributeCount;
            /// <summary>
            /// <para>Application-defined attributes that are associated with the credential. This
            /// member can be read and written.</para>
            /// </summary>
            public IntPtr Attributes;
            /// <summary>
            /// <para>Alias for the TargetName member. This member can be read and written. It
            /// cannot be longer than `<see cref="StringMaxLength"/>` characters.</para>
            /// <para>If the credential Type is `<see cref="CredentialType.Generic"/>`, this member
            /// can be non-NULL, but the credential manager ignores the member.</para>
            /// </summary>
            [MarshalAs(UnmanagedType.LPWStr)]
            public string TargetAlias;
            /// <summary>
            /// <para>The user name of the account used to connect to TargetName.</para>
            /// <para>If the credential Type is `<see cref="CredentialType.DomainPassword"/>`, this
            /// member can be either a DomainName\UserName or a UPN.</para>
            /// <para>If the credential Type is `<see cref="CredentialType.DomainCertificate"/>`,
            /// this member must be a marshaled certificate reference created by calling
            /// `CredMarshalCredential` with a `CertCredential`.</para>
            /// <para>If the credential Type is `<see cref="CredentialType.Generic"/>`, this member
            /// can be non-NULL, but the credential manager ignores the member.</para>
            /// <para>This member cannot be longer than CRED_MAX_USERNAME_LENGTH(513) characters.</para>
            /// </summary>
            [MarshalAs(UnmanagedType.LPWStr)]
            public string UserName;
        }
        #endregion advapi32.dll
 
        #region kernel32.dll
        /// <summary>
        /// Closes an open object handle.
        /// </summary>
        /// <param name="handle">A valid handle to an open object.</param>
        /// <returns>True is successful; otherwise false.</returns>
        [DllImport(Kernel32, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "CloseHandle", SetLastError = true)]
        public static extern bool CloseHandle(IntPtr handle);
 
        /// <summary>
        /// Closes an open object handle.
        /// </summary>
        /// <param name="handle">A valid handle to an open object.</param>
        /// <returns>True is successful; otherwise false.</returns>
        [DllImport(Kernel32, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "CloseHandle", SetLastError = true)]
        public static extern bool CloseHandle(SafeHandle handle);
        #endregion kernel32.dll
 
        /// <summary>
        /// The System Error Codes are very broad. Each one can occur in one of many hundreds of
        /// locations in the system. Consequently the descriptions of these codes cannot be very
        /// specific. Use of these codes requires some amount of investigation and analysis. You
        /// need to note both the programmatic and the run-time context in which these errors occur.
        /// Because these codes are defined in WinError.h for anyone to use, sometimes the codes
        /// are returned by non-system software. Sometimes the code is returned by a function deep
        /// in the stack and far removed from your code that is handling the error.
        /// </summary>
        internal static class Win32Error
        {
            /// <summary>
            /// The operation completed successfully.
            /// </summary>
            public const int Success = 0;
            /// <summary>
            /// The system cannot find the file specified.
            /// </summary>
            public const int FileNotFound = 2;
            /// <summary>
            /// The handle is invalid.
            /// </summary>
            public const int InvalidHandle = 6;
            /// <summary>
            /// Not enough storage is available to process this command.
            /// </summary>
            public const int NotEnoughMemory = 8;
            /// <summary>
            /// The process cannot access the file because it is being used by another process.
            /// </summary>
            public const int SharingViolation = 32;
            /// <summary>
            /// The file exists.
            /// </summary>
            public const int FileExists = 80;
            /// <summary>
            /// Cannot create a file when that file already exists.
            /// </summary>
            public const int AlreadyExists = 183;
            /// <summary>
            /// Element not found.
            /// </summary>
            public const int NotFound = 1168;
            /// <summary>
            /// A specified logon session does not exist. It may already have been terminated.
            /// </summary>
            public const int NoSuchLogonSession = 1312;
        }
    }
 
    /// <summary>
    /// Helper methods to manage credential manager entries.
    /// </summary>
    public static class CredentialHelper
    {
        /// <summary>
        /// Convert an unmanaged IntPtr to a SecureString object, used for
        /// credentials.
        /// </summary>
        /// <param name="pointer">The credential blob.</param>
        /// <param name="size">The credential blob size.</param>
        /// <returns>The protected credential.</returns>
        public static SecureString IntPtrToSecureString(IntPtr pointer, uint size)
        {
            if (size > 0)
            {
                string plain = Marshal.PtrToStringUni(pointer, (int)size / 2);
 
                return StringToSecureString(plain);
            }
            else
            {
                return StringToSecureString(string.Empty);
            }
        }
 
        /// <summary>
        /// Convert a plain string to a SecureString object.
        /// </summary>
        /// <param name="plain">The plain string to protect.</param>
        /// <returns>The protected string.</returns>
        public static SecureString StringToSecureString(string plain)
        {
            SecureString secure = new SecureString();
 
            if (!string.IsNullOrEmpty(plain))
            {
                foreach (char c in plain)
                {
                    secure.AppendChar(c);
                }
            }
 
            secure.MakeReadOnly();
 
            return secure;
        }
    }
 
    /// <summary>
    /// Class representing an entry in the credential manager.
    /// </summary>
    public class CredentialEntry
    {
        /// <summary>
        /// Create a new CredentialEntry object.
        /// </summary>
        /// <param name="nativeCredential">The native credential object.</param>
        /// <param name="flags">Credential object flags.</param>
        internal CredentialEntry(NativeMethods.Credential nativeCredential, NativeMethods.CredentialEnumerateFlags flags)
        {
            // Initialize default properties
            Namespace = string.Empty;
            Type = (CredentialType)nativeCredential.Type;
            Persist = (CredentialPersist)nativeCredential.Persist;
            TargetName = nativeCredential.TargetName ?? string.Empty;
            Username = nativeCredential.UserName ?? string.Empty;
            Password = CredentialHelper.IntPtrToSecureString(nativeCredential.CredentialBlob, nativeCredential.CredentialBlobSize);
            Comment = nativeCredential.Comment ?? string.Empty;
            Attribute = string.Empty;
            TargetAlias = nativeCredential.TargetAlias ?? string.Empty;
 
            // Extract namespace, attribute and target name
            if (flags == NativeMethods.CredentialEnumerateFlags.AllCredentials)
            {
                Match match = Regex.Match(TargetName, "(.*?):(.*?)=(.*)");
 
                if (match.Success)
                {
                    if (match.Groups.Count >= 2)
                    {
                        Namespace = match.Groups[1].Value;
                    }
 
                    if (match.Groups.Count >= 3)
                    {
                        Attribute = match.Groups[2].Value;
                    }
 
                    if (match.Groups.Count >= 4)
                    {
                        TargetName = match.Groups[3].Value;
                    }
                }
            }
 
            // Use the username if provided or fallback to the target name
            if (!string.IsNullOrEmpty(Username))
            {
                Credential = new PSCredential(Username, Password);
            }
            else
            {
                Credential = new PSCredential(TargetName, Password);
            }
        }
 
        /// <summary>
        /// Entry namespace like Domain, LegacyGeneric, MicrosoftAccount, WindowsLive, etc.
        /// </summary>
        public string Namespace
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Type of the entry.
        /// </summary>
        public CredentialType Type
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Definition where it will persist the entry.
        /// </summary>
        public CredentialPersist Persist
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry target name, used to display in the credential manager.
        /// </summary>
        public string TargetName
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry username.
        /// </summary>
        public string Username
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry password.
        /// </summary>
        public SecureString Password
        {
            private set;
            get;
        }
 
        /// <summary>
        /// PowerShell credential object.
        /// </summary>
        public PSCredential Credential
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry comment.
        /// </summary>
        public string Comment
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry attribute.
        /// </summary>
        public string Attribute
        {
            private set;
            get;
        }
 
        /// <summary>
        /// Entry alias.
        /// </summary>
        public string TargetAlias
        {
            private set;
            get;
        }
    }
 
    /// <summary>
    /// Class providing methods to interact with the credential manager. This
    /// is a wrapper to unmanaged code.
    /// </summary>
    public static class CredentialStore
    {
        /// <summary>
        /// Get a credential entry based on name and type. If no or multiple
        /// credentials were found, an exception is thrown.
        /// </summary>
        /// <param name="targetName">Credential entry name.</param>
        /// <param name="type">Credential entry type.</param>
        /// <returns>The credential entry.</returns>
        public static CredentialEntry GetCredential(string targetName, CredentialType type)
        {
            IList<CredentialEntry> credentials = GetCredentials(targetName, type).ToList();
 
            if (credentials.Count == 1)
            {
                return credentials[0];
            }
            else if (credentials.Count < 1)
            {
                throw new Win32Exception("Credentials not found!");
            }
            else
            {
                throw new Win32Exception("No unique credential found!");
            }
        }
 
        /// <summary>
        /// Get all credential entries matching the specified parameters.
        /// </summary>
        /// <param name="targetName">Optional entry name.</param>
        /// <param name="type">Optional entry type.</param>
        /// <param name="persist">Optional entry persist.</param>
        /// <param name="username">Optional entry username.</param>
        /// <returns>List of al matching credential entries.</returns>
        public static IEnumerable<CredentialEntry> GetCredentials(string targetName = null, CredentialType? type = null, CredentialPersist? persist = null, string username = null)
        {
            IList<CredentialEntry> credentials = new List<CredentialEntry>();
 
            int count;
            IntPtr credentialArrayPtr;
 
            NativeMethods.CredentialEnumerateFlags flags = NativeMethods.CredentialEnumerateFlags.None;
            if (Environment.OSVersion.Version.Major >= 6)
            {
                flags = NativeMethods.CredentialEnumerateFlags.AllCredentials;
            }
 
            if (NativeMethods.CredEnumerate(null, flags, out count, out credentialArrayPtr))
            {
                for (int i = 0; i < count; i += 1)
                {
                    int offset = i * Marshal.SizeOf(typeof(IntPtr));
 
                    IntPtr credentialPtr = Marshal.ReadIntPtr(credentialArrayPtr, offset);
 
                    if (credentialPtr != IntPtr.Zero)
                    {
                        NativeMethods.Credential nativeCredential = Marshal.PtrToStructure<NativeMethods.Credential>(credentialPtr);
 
                        CredentialEntry credential = new CredentialEntry(nativeCredential, flags);
 
                        if ((string.IsNullOrEmpty(targetName) || credential.TargetName == targetName) &&
                            (!type.HasValue || credential.Type == type.Value) &&
                            (!persist.HasValue || credential.Persist == persist.Value) &&
                            (string.IsNullOrEmpty(username) || credential.Credential.UserName == username))
                        {
                            credentials.Add(credential);
                        }
                    }
                }
            }
            else
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
 
            return credentials;
        }
 
        /// <summary>
        /// Check if the credential identified by name and type exists.
        /// </summary>
        /// <param name="targetName">Credential entry name.</param>
        /// <param name="type">Credential entry type.</param>
        /// <returns>If the entry exists, return true, else false.</returns>
        public static bool ExistCredential(string targetName, CredentialType type)
        {
            IList<CredentialEntry> credentials = GetCredentials(targetName, type).ToList();
 
            return credentials.Count > 0;
        }
 
        /// <summary>
        /// Create a new credential entry objects.
        /// </summary>
        /// <param name="targetName">Credential entry name.</param>
        /// <param name="type">Credential entry type.</param>
        /// <param name="persist">Credential entry persist.</param>
        /// <param name="credential">Credential object.</param>
        /// <returns>Returns the new created entry.</returns>
        public static CredentialEntry CreateCredential(string targetName, CredentialType type, CredentialPersist persist, PSCredential credential)
        {
            NativeMethods.Credential nativeCredential = new NativeMethods.Credential()
            {
                TargetName = targetName,
                Type = (NativeMethods.CredentialType)type,
                Persist = (NativeMethods.CredentialPersist)persist,
                AttributeCount = 0,
                UserName = credential.UserName,
                CredentialBlob = Marshal.StringToCoTaskMemUni(credential.GetNetworkCredential().Password),
                CredentialBlobSize = (uint)Encoding.Unicode.GetByteCount(credential.GetNetworkCredential().Password)
            };
 
            try
            {
                if (NativeMethods.CredWrite(ref nativeCredential, 0))
                {
                    return GetCredential(targetName, type);
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (nativeCredential.CredentialBlob != IntPtr.Zero)
                {
                    Marshal.FreeCoTaskMem(nativeCredential.CredentialBlob);
                }
            }
        }
 
        /// <summary>
        /// Remove an existing credential entry.
        /// </summary>
        /// <param name="targetName">Credential entry name.</param>
        /// <param name="type">Credential entry type.</param>
        public static void RemoveCredential(string targetName, CredentialType type)
        {
            if (!NativeMethods.CredDelete(targetName, (NativeMethods.CredentialType)type, 0))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }
}