diff --git a/LibGit2Sharp.Tests/TestHelpers/Constants.cs b/LibGit2Sharp.Tests/TestHelpers/Constants.cs
index 450746ff6..5a389136c 100644
--- a/LibGit2Sharp.Tests/TestHelpers/Constants.cs
+++ b/LibGit2Sharp.Tests/TestHelpers/Constants.cs
@@ -22,6 +22,9 @@ public static class Constants
// ... return new UsernamePasswordCredentials { Username = "username", Password = "swordfish" };
//
// Or:
+ // ... return new SecureUsernamePasswordCredentials() { Username = "username", Password = StringToSecureString("swordfish") };
+ //
+ // Or:
// public const string PrivateRepoUrl = "https://tfs.contoso.com/tfs/DefaultCollection/project/_git/project";
// ... return new DefaultCredentials();
@@ -68,5 +71,21 @@ public static string BuildPath()
Trace.TraceInformation("Test working directory set to '{0}'", testWorkingDirectory);
return testWorkingDirectory;
}
+
+ // To help with creating secure strings to test with.
+ private static System.Security.SecureString StringToSecureString(string str)
+ {
+ var chars = str.ToCharArray();
+
+ var secure = new System.Security.SecureString();
+ for (var i = 0; i < chars.Length; i++)
+ {
+ secure.AppendChar(chars[i]);
+ }
+
+ secure.MakeReadOnly();
+
+ return secure;
+ }
}
}
diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj
index 60b352522..856d2433b 100644
--- a/LibGit2Sharp/LibGit2Sharp.csproj
+++ b/LibGit2Sharp/LibGit2Sharp.csproj
@@ -143,6 +143,7 @@
+
diff --git a/LibGit2Sharp/SecureUsernamePasswordCredentials.cs b/LibGit2Sharp/SecureUsernamePasswordCredentials.cs
new file mode 100644
index 000000000..5e1d49314
--- /dev/null
+++ b/LibGit2Sharp/SecureUsernamePasswordCredentials.cs
@@ -0,0 +1,50 @@
+using System;
+using LibGit2Sharp.Core;
+using System.Security;
+using System.Runtime.InteropServices;
+
+namespace LibGit2Sharp
+{
+ ///
+ /// Class that uses to hold username and password credentials for remote repository access.
+ ///
+ public sealed class SecureUsernamePasswordCredentials : Credentials
+ {
+ ///
+ /// Callback to acquire a credential object.
+ ///
+ /// The newly created credential object.
+ /// 0 for success, < 0 to indicate an error, > 0 to indicate no credential was acquired.
+ protected internal override int GitCredentialHandler(out IntPtr cred)
+ {
+ if (Username == null || Password == null)
+ {
+ throw new InvalidOperationException("UsernamePasswordCredentials contains a null Username or Password.");
+ }
+
+ IntPtr passwordPtr = IntPtr.Zero;
+
+ try
+ {
+ passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(Password);
+
+ return NativeMethods.git_cred_userpass_plaintext_new(out cred, Username, Marshal.PtrToStringUni(passwordPtr));
+ }
+ finally
+ {
+ Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
+ }
+
+ }
+
+ ///
+ /// Username for username/password authentication (as in HTTP basic auth).
+ ///
+ public string Username { get; set; }
+
+ ///
+ /// Password for username/password authentication (as in HTTP basic auth).
+ ///
+ public SecureString Password { get; set; }
+ }
+}