Last active
June 8, 2023 16:43
-
-
Save Rene-Sackers/dc99ec903024a05ea2146f19c664e65a to your computer and use it in GitHub Desktop.
.NET 5+ WindowsCredentialVault access
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Security; | |
public static class ConsolePasswordInput | |
{ | |
public static SecureString? ReadPassword() | |
{ | |
var securePassword = new SecureString(); | |
while (true) | |
{ | |
var inputKey = Console.ReadKey(); | |
switch (inputKey.Key) | |
{ | |
case ConsoleKey.Enter: | |
securePassword.MakeReadOnly(); | |
return securePassword; | |
case ConsoleKey.Escape: | |
securePassword.Clear(); | |
return null; | |
case ConsoleKey.Backspace: | |
if (securePassword.Length <= 0) | |
{ | |
Console.CursorLeft++; | |
continue; | |
} | |
securePassword.RemoveAt(securePassword.Length - 1); | |
Console.Write(" "); | |
Console.CursorLeft--; | |
continue; | |
} | |
securePassword.AppendChar(inputKey.KeyChar); | |
Console.CursorLeft--; | |
Console.Write("•"); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Install package System.Runtime.InteropServices | |
using System.Net; | |
using System.Runtime.InteropServices; | |
using System.Security; | |
public static class WindowsCredentialVault | |
{ | |
public static bool WriteCredential(string credentialName, string username, SecureString password) | |
{ | |
// Encrypt the password | |
password.MakeReadOnly(); | |
var credentialBlob = new byte[password.Length * 2]; | |
Marshal.Copy(Marshal.SecureStringToGlobalAllocUnicode(password), credentialBlob, 0, | |
credentialBlob.Length); | |
// Create the Credential struct | |
var credential = new Credential | |
{ | |
Flags = 0, | |
Type = CredentialType.Generic, | |
TargetName = Marshal.StringToCoTaskMemUni(credentialName), | |
Comment = nint.Zero, | |
LastWritten = new System.Runtime.InteropServices.ComTypes.FILETIME(), | |
CredentialBlobSize = (uint)credentialBlob.Length, | |
CredentialBlob = Marshal.AllocHGlobal(credentialBlob.Length), | |
Persist = (uint)CredentialPersist.LocalMachine, | |
AttributeCount = 0, | |
Attributes = nint.Zero, | |
TargetAlias = nint.Zero, | |
UserName = Marshal.StringToCoTaskMemUni(username), | |
}; | |
Marshal.Copy(credentialBlob, 0, credential.CredentialBlob, credentialBlob.Length); | |
// Write the credential to the Windows Credential Manager | |
var success = CredWrite(ref credential, 0); | |
// Free the allocated memory | |
Marshal.ZeroFreeGlobalAllocUnicode(credential.TargetName); | |
Marshal.ZeroFreeGlobalAllocUnicode(credential.UserName); | |
Marshal.FreeHGlobal(credential.CredentialBlob); | |
return success; | |
} | |
public static NetworkCredential ReadCredential(string credentialName) | |
{ | |
// Read the credential from the Windows Credential Manager | |
var success = CredRead(credentialName, CredentialType.Generic, 0, out var credentialPtr); | |
if (success) | |
{ | |
var credential = (Credential)Marshal.PtrToStructure(credentialPtr, typeof(Credential)); | |
var username = Marshal.PtrToStringUni(credential.UserName); | |
var password = Marshal.PtrToStringUni(credential.CredentialBlob, (int)credential.CredentialBlobSize / 2); | |
// Free the allocated memory | |
CredFree(credentialPtr); | |
return new NetworkCredential(username, password); | |
} | |
var errorCode = Marshal.GetLastWin32Error(); | |
if (errorCode == 1168) | |
throw new InvalidOperationException("The credential was not found."); | |
throw new InvalidOperationException($"Failed to read credential: Error code {errorCode}"); | |
} | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
public struct Credential | |
{ | |
public uint Flags; | |
public CredentialType Type; | |
public nint TargetName; | |
public nint Comment; | |
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; | |
public uint CredentialBlobSize; | |
public nint CredentialBlob; | |
public uint Persist; | |
public uint AttributeCount; | |
public nint Attributes; | |
public nint TargetAlias; | |
public nint UserName; | |
} | |
public enum CredentialPersist : uint | |
{ | |
Session = 1, | |
LocalMachine = 2, | |
Enterprise = 3, | |
} | |
public enum CredentialType : uint | |
{ | |
Generic = 1, | |
DomainPassword = 2, | |
DomainCertificate = 3, | |
DomainVisiblePassword = 4, | |
GenericCertificate = 5, | |
DomainExtended = 6, | |
Maximum = 7, | |
MaximumEx = (Maximum + 1000), | |
} | |
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
private static extern bool CredRead(string target, CredentialType type, int reservedFlag, out nint credential); | |
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
private static extern bool CredWrite([In] ref Credential userCredential, [In] uint flags); | |
[DllImport("Advapi32.dll", SetLastError = true)] | |
private static extern void CredFree(nint credential); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment