|
/*
Secure WPF Authentication Sample
History:
29-09-08 1.0 First version
Visit http://blogs.ugidotnet.org/leonardo for updates
*/
using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Security;
using System.Reflection;
namespace SecureWpfLogOn
{
/// <summary>
/// Interaction logic for SecureLogOn.xaml
/// </summary>
public partial class SecureLogOn : Window
{
// Fake char to display in Visual Tree
private const char PWD_CHAR = '●';
/// <summary>
/// Only copy of real password
/// </summary>
/// <remarks>For more security use System.Security.SecureString type instead</remarks>
private StringBuilder pwd = new StringBuilder();
public SecureLogOn()
{
InitializeComponent();
InitPwdBox(passwordBox1);
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("You have inserted \"" + pwd.ToString() + "\" password");
}
private void passwordBox1_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pwdBox = (PasswordBox)sender;
SecurePasswordChanged(pwdBox);
}
/// <summary>
/// PasswordChanged event handler for secure storing of password into Visual Tree
/// </summary>
/// <param name="pwdBox">PasswordBox that will be secured</param>
/// <remarks>Warning: This code require FullTrust and may not work with future release of .Net framework</remarks>
private void SecurePasswordChanged(PasswordBox pwdBox)
{
// If Password is empty clear also this.pwd
if (pwdBox.Password == "")
{
this.pwd = new StringBuilder();
return;
}
// If Password is modified at non last position or any character is deleted then clear it
// because we can't retrieve what is changed due to PasswordBox control.
// Only manual insert by one characted at time is allowed
if (this.pwd.Length != pwdBox.Password.Length - 1)
{
pwdBox.Password = "";
return;
}
for (int i = 0; i < pwdBox.Password.Length - 1; i++)
{
if (pwdBox.Password[i] != PWD_CHAR)
{
pwdBox.Password = "";
return;
}
}
// Append last char from user input to this.pwd field to keep one copy of password
this.pwd.Append(pwdBox.Password[this.pwd.Length]);
// To avoid that PasswordChanged event fires again when we clear VisualTree password
// from PasswordBox (we replace each char with PWD_CHAR), we set directly internal value
// of password (of type SecureString) with reflection.
using (SecureString ss2 = StringToSecureString(new string(PWD_CHAR, pwd.Length)))
{
_password.SetValue(_textContainer, ss2.Copy());
}
}
/// <summary>
/// Reference to _textContainer private field of PasswordBox, keept here for performance reason
/// </summary>
private Object _textContainer;
/// <summary>
/// Referemce to _password private field of _textContainer, keept here for performance reason
/// </summary>
private FieldInfo _password;
/// <summary>
/// Get references to _password and _textContainer fields of PasswordBox to avoid overheading on each PasswordChanged event call
/// </summary>
private void InitPwdBox(PasswordBox pwdBox)
{
_textContainer = pwdBox.GetType().GetField("_textContainer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(pwdBox);
_password = _textContainer.GetType().GetField("_password", BindingFlags.NonPublic | BindingFlags.Instance);
}
/// <summary>
/// Convert string into SecureString
/// </summary>
/// <param name="str">string to convert</param>
/// <returns>SecureString copy of str parameter</returns>
private static SecureString StringToSecureString(string str)
{
if (str == null)
{
str = string.Empty;
}
using (SecureString secStr = new SecureString())
{
for (int i = 0; i < str.Length; i++)
{
secStr.AppendChar(str[i]);
}
return secStr.Copy();
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.