Put SecureString into PasswordBox - c#

I have an existing SecureString that I would like to put into a PasswordBox without revealing the .Password. Can this be done? For example:
tbPassword.SecurePassword = DecryptString(Properties.Settings.Default.proxyPassword);
In this case DecryptString produces a SecureString. However, SecurePassword is a read-only property so I can't assign a value to it.

You can't.
However, what you can do is put placeholder text in it's place (it can even be "placeholder", we are only using it to make a few dots to show up in the box).
After you put the placeholder in, when you go to retrieve the "current password" somewhere in your program first check if the PasswordChanged event has fired since you entered the placeholder password. If the event has not fired use the old stored password, if the event has fired use the current password from the SecurePassword property of PasswordBox.

Sorry for the late addition, but it might be an idea for people who also walk into this.
(At least I ended up on this page in 2021 looking for it)
Looking at the source of PasswordBox, we can see how its properties are implemented. The Password property setter just copies the String into a temporary SecureString and forwards it to its internal storage.
The readonly SecurePassword property returns a copy of the internal SecureString, so calling .Clear() / .AppendChar(char) on it will only change this copy, if .MakeReadonly() has not been called on it.
[TemplatePart(Name = "PART_ContentHost", Type = typeof (FrameworkElement))]
public sealed class PasswordBox : Control, ITextBoxViewHost
{
public SecureString SecurePassword => this.TextContainer.GetPasswordCopy();
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public unsafe string Password
{
[SecurityCritical] get { /* left out to reduce space */ }
[SecurityCritical] set
{
if (value == null)
value = string.Empty;
// We want to replicate this, but copy a SecureString instead of creating one from a String
using (SecureString secureString = new SecureString())
{
for (int index = 0; index < value.Length; ++index)
secureString.AppendChar(value[index]);
this.SetSecurePassword(secureString);
}
}
}
}
It may be a bit hacky, but calling the private SetSecurePassword may be closest to bypassing conversion to clear text in order to use the Password setter : (we make a temporary copy just like in the .Password setter as we are not responsible for managing lifetime of the provided SecureString, which could even be readonly)
// option 1: streight reflection
var setPasswordMethod = typeof(PasswordBox).GetMethod("SetSecurePassword", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] {typeof(SecureString)}, null);
using (var copy = mySecurePassword.Copy())
setPasswordMethod.Invoke(PasswordControl, new[] {copy});
// option 2: compiled delegate so reflection will only kick in once
Action<PasswordBox, SecureString> setSecurePassword = null; // this would be a cache lookup instead of a local variable.
if (setSecurePassword == null)
{
var passwordBox = Expression.Parameter(typeof(PasswordBox), "passwordBox");
var password = Expression.Parameter(typeof(SecureString), "securePassword");
//// if we want to include code for making the temporary copy in the delegate, use this instead to create its body
//var passwordCopy = Expression.Variable(typeof(SecureString));
//var makePasswordCopy = Expression.Call(password, nameof(SecureString.Copy), Type.EmptyTypes);
//var body = Expression.Block(new[] {passwordCopy},
// Expression.Assign(passwordCopy, makePasswordCopy),
// Expression.TryFinally(
// Expression.Call(passwordBox, "SetSecurePassword", Type.EmptyTypes, passwordCopy),
// Expression.Call(Expression.Convert(passwordCopy, typeof(IDisposable)),
// nameof(IDisposable.Dispose), Type.EmptyTypes)));
var body = Expression.Call(passwordBox, "SetSecurePassword", Type.EmptyTypes, password);
setSecurePassword = Expression.Lambda<Action<PasswordBox, SecureString>>(body, passwordBox, password).Compile();
}
using (var copy = mySecurePassword.Copy()) // if we would make the copy inside the delegate, we won't need to do it here.
setSecurePassword(PasswordControl, copy);
I hope this still helps anyone.

Related

Exception has been thrown by the target of an invocation ERROR when trying to changind AD user with ASP.net C#

I've tryed a lot of others solutions, and I still didn't make it work. Can someone help me please.
my code is like that:
I saw something about secureString, I tryed to use it but it still didn't work.
I saw too another solution that says to use var rather than string in the variables. Didn't work
I dont know if I'm doing something wrong or if those solutions that dosoen't work.
public bool RedefinirSenha(string pUsuario, string pSenhaAtual, string pNovaSenha)
{
var NovaSenha = pNovaSenha;
var SenhaAtual = pSenhaAtual;
var Usuario = pUsuario;
//string Pwd = String.Format(#"""{0}""", NovaSenha);
//byte[] pwdCerto = System.Text.Encoding.Unicode.GetBytes(Pwd);
try
{
string LDAP = myLDAPpath;
DirectoryEntry ADcon = new DirectoryEntry(LDAP, Usuario, SenhaAtual, AuthenticationTypes.Secure);
if (ADcon != null)
{
DirectorySearcher search = new DirectorySearcher(ADcon);
search.Filter = "(SAMAccountName=" + Usuario + ")";
SearchResult result = search.FindOne();
if (result != null)
{
DirectoryEntry userEntry = result.GetDirectoryEntry();
if (userEntry != null)
{
try
{
userEntry.Invoke("ChangePassword", new object[] { SenhaAtual, NovaSenha }, AuthenticationTypes.Secure);
userEntry.Properties["LockOutTime"].Value = 0;
userEntry.CommitChanges();
userEntry.Close();
return true;
}
catch (Exception INex)
{
this.Erro = INex.Message + "COD:\r\n" + INex.InnerException;
userEntry.Close();
return false;
}
}
}
}
return true;
}
catch (Exception ex)
{
this.Erro = ex.Message;
return false;
}
}
First, there will be no difference at runtime if you declare the variables as var or string. Using the var keyword lets the compiler decide what the type is. Because you're assigning a string to it, then it is a string too. In most cases, var is fine. There are only very rare cases when you need to explicitly specify the type.
Second, DirectoryEntry.Invoke is defined like this:
public object Invoke (string methodName, params object[] args);
That may seem like you need to pass an object array, but that is not the case. The params keyword is a way to allow you to pass multiple parameters that get used inside the method as an array. So when you call it like this:
userEntry.Invoke("ChangePassword", new object[] { SenhaAtual, NovaSenha }, AuthenticationTypes.Secure);
The first parameter is an object array and the second parameter is AuthenticationTypes.Secure, then both of those get put inside the args array for use inside the Invoke method. But that is not what ChangePassword looks for. If this doesn't make sense to you, read the documentation for the params keyword and it should help.
When you call .Invoke("ChangePassword", ...), it calls the native Windows IADsUser.ChangePassword method. That takes two parameters: a string with the old password and a string with the new password - not an object array and an AuthenticationTypes value. So you should be calling it like this:
userEntry.Invoke("ChangePassword", SenhaAtual, NovaSenha);
You don't need to worry about the authentication because the password can only be changed over a secure connection. In the documentation, it says it behaves the same way as (IADsUser.SetPassword](https://learn.microsoft.com/en-ca/windows/win32/api/iads/nf-iads-iadsuser-setpassword), where it attempts several different ways to achieve a secure connection for you.
There is another way to change the password if the DirectoryEntry connection is already over a secure connection. A secure connection can either be using Kerberos, which can be done using AuthenticationTypes.Sealing (this is best if you are on the same network as the domain controller):
var ADcon = new DirectoryEntry(LDAP, Usuario, SenhaAtual, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
Or if by using LDAPS (LDAP over SSL), which you can use just by specifying port 636 in the LDAP path (this is the only way if you are not on the same network as the domain controller):
var ADcon = new DirectoryEntry("LDAP://example.com:636", Usuario, SenhaAtual);
If you do that, then you can change the password by updating the unicodePwd attribute directly, in the very specific way it wants it (enclosed in quotes and encoded in UTF-16), like this:
userEntry.Properties["unicodePwd"].Remove(Encoding.Unicode.GetBytes($"\"{SenhaAtual}\""));
userEntry.Properties["unicodePwd"].Add(Encoding.Unicode.GetBytes($"\"{NovaSenha}\""));
This should perform slightly faster since all of the work (changing the password and setting lockOutTime) is done over one network request instead of two.

Selenium entering invalid text (Half text) in text box

While running multiple test cases, selenium is entering invalid text as shown in the attachment.
Below are the two Examples showing the error:
Instead of entering AutomationTest123_6035633258972, it just enters 6035633258972. .
Instead of Entering AutomationTest123_636010703068635512, it just
enters utomationTest123_636010703068635512. .
Code
//StaticVariable is class which has static values
var username = StaticVariable.username;
var password = StaticVariable.password;
driver.FindElement(By.Id("username")).SendKeys(username); //putting wrong values
driver.FindElement(By.Id("password")).SendKeys(password); //putting wrong values
driver.FindElement(By.Id("login")).Click();
Can anyone help me on this? Any help would appreciated.
first of all make sure that
var username = StaticVariable.username;
is assigning the right value.
use a
print
to check what the value of username after assign.
than try by using the below code:
driver.FindElement(By.Id("username")).click()
driver.FindElement(By.Id("username")).clear()
driver.FindElement(By.Id("username")).SendKeys(username);
There can be a couple of reasons I've seen that this can be an issue. I'd first try to clear out the text input first and make sure it has focus.
var username = StaticVariable.username;
var password = StaticVariable.password;
// Fill out user name
var userElem = driver.findElement(By.id("username"));
userElem.click();
userElem.clear();
userElem.sendKeys(userName);
// Fill out password
var passElem = driver.findElement(By.id("password"));
passElem.click();
passElem.clear();
passElem.sendKeys(password);
// Click login button
driver.findElement(By.id("login"))
.click();
If, however, that doesn't fix it, I've had situations where splitting the string up and sending keys a few at a time has worked. That does have a performance penalty so I wouldn't do it unless you really really need to.
public void fooTest() {
// Do stuff to get to the correct page, etc
var username = StaticVariable.username;
var password = StaticVariable.password;
// Fill out user name
var userElem = driver.findElement(By.id("username"));
sendKeys(userElem, userName);
// Fill out password
var passElem = driver.findElement(By.id("password"));
sendKeys(passElem, password);
// Click login button
driver.findElement(By.id("login"))
.click();
// do assertions, etc
}
private void sendKeys(WebElement elem, String keys) {
elem.click();
elem.clear();
for (int i = 0; i < keys.length(); i) {
elem.sendKeys(keys.charAt(i));
}
}
Note : sorry for any syntax / c# errors, I've barely used the language ... I'm much more familiar with Java ;-)
Use the clear method then type the text: driver.findElement(By.cssSelector("").clear();
It may help.
Rather than using 'var' , you might want to try putting in the specific type , such as 'string'. The problem has to be in what is being passed to .SendKeys() or how it is being received by the function.
string username = StaticVariable.username;
string password = StaticVariable.password;

Sharepoint Client Object Model setting ModifiedBy field

I am trying to update the "ModifiedBy" field in a Sharepoint discussion board using the Client Object Model. By changing the "Editor" and "Author" fields, I can change the "ModifiedBy" that appears on the list view. However, once you click on a discussion post, the "ModifiedBy" field that appears there (the one with the picture above it) does not reflect the changes. After experimenting, I discovered that the field I need to change to correct this is called "MyEditor". Unfortunately, this field is read-only.
In the code below, I try to change the read-only settings of the field to false. When I look at the MyEditor field in Visual Studio's debugger after the ExecuteQuery() line at the bottom of the first block, it shows that the ReadOnlyField value has in fact been set to false.
sharepointContext.Load(discussionList);
sharepointContext.ExecuteQuery();
var fields = discussionList.Fields;
sharepointContext.Load(fields);
sharepointContext.ExecuteQuery();
var field = fields.GetByInternalNameOrTitle("MyEditor");
field.ReadOnlyField = false;
field.Update();
sharepointContext.Load(field);
sharepointContext.ExecuteQuery();
The code above executes with no problems. The problem comes with this next block:
//...Code to initialize discussionItem...
discussionItem["MyEditor"] = 0;
discussionItem["Editor"] = 0;
discussionItem["Author"] = 0;
discussionItem["Body"] = "Testing";
discussionItem["Title"] = "Hello Worlds";
discussionItem.Update();
sharepointContext.Load(discussionItem);
sharepointContext.ExecuteQuery();
When the code reaches the ExecuteQuery() at the bottom of the second block, it throws a ServerException with the following message:
Invalid data has been used to update the list item.
The field you are trying to update may be read only.
To make sure that the MyEditor field was the one causing the exception to be thrown, I commented out the line where I set it and ran the code again. Everything worked fine. I don't understand what is wrong, can someone help me?
In case someone needs to find the user by name, it goes like this:
private static FieldUserValue GetUser(ClientContext clientContext, string userName)
{
var userValue = new FieldUserValue();
var newUser = clientContext.Web.EnsureUser(userName);
clientContext.Load(newUser);
clientContext.ExecuteQuery();
userValue.LookupId = newUser.Id;
return userValue;
}
The returned value can be set via item["Author"]
ModifiedBy and CreadtedBy calculated automatically from Author and Editor you need to change only Author and Editor fields like this:
using (var clientContext = new ClientContext(#"http://server"))
{
var web = clientContext.Web;
var lst = web.Lists.GetByTitle("Discus");
var item = lst.GetItemById(2);
item["Author"] = 3;
item["Editor"] = 2;
item.Update();
clientContext.ExecuteQuery();
Console.WriteLine("done");
}

I need help converting VB.Net code block to C# (Setting Property with DataTable Value)

I am writing my first C# application, which in this case is just a "learning exercise" This example is a simplified block of code that I have used many times in VB.Net so I know that it works correctly. This is what the VB code looks like.
Public Class User
Private Const CN_LoginId As String = "Login"
Private Const CN_Password As String = "Password"
Private _password As String
Public Property Password() As String
Get
Return _password
End Get
Set(ByVal value As String)
_password = value
End Set
End Property
Public Shared Function Create(ByVal Login As String) As User
Dim usr = New User()
Using dt As DataTable = DAC.ExecuteDataTable("usp_PasswordSelect", _
DAC.Parameter(CN_LoginId, Login))
With dt.Rows(0)
usr.Password = CStr(.Item(CN_Password))
End With
End Using
Return usr
End Function
End Class
So in C# I have tried converting it by hand and by using Telerik's online conversion utility, which is what I am posting below because I am assuming that it is closer to the right answer then what I did myself.
public class User
{
private const string CN_LoginId = "Login";
private const string CN_Password = "Password";
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
public static User Create(string Login)
{
object usr = new User();
using (DataTable dt = DAC.ExecuteDataTable("usp_PasswordSelect",
DAC.Parameter(CN_LoginId, Login)))
{
{
usr.Password = Convert.ToString(dt.Rows(0).Item(CN_Password));
}
}
return usr;
}
}
The first error I get is on this line usr.Password = Convert.ToString(dt.Rows(0).Item(CN_Password));. The error is "Error 1 'object' does not contain a definition for 'Password' and no extension method 'Password' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
At this point I am assuming the second error will go away when I fix the first one. So my question is how do I correctly set the property for this Object using the DataTable in C#?
Here's how I'd write it:
public class User
{
private const string CN_LoginId = "Login";
private const string CN_Password = "Password";
public string Password { get; set; }
public static User Create(string Login)
{
User usr = new User();
using (DataTable dt = DAC.ExecuteDataTable("usp_PasswordSelect",
DAC.Parameter(CN_LoginId, Login)))
{
usr.Password = Convert.ToString(dt.Rows[0][CN_Password]);
}
return usr;
}
}
Some notes:
C# accesses arrays using square brackets, not parens. So Rows(0) isn't correct - it should be Rows[0].
I agree with Bernard that you should declare the User object as a User, and not as an object. You'll lose all visibility into it's properties. You can also use var, but if you're learning C#, you may be better off explicitly declaring your variable types. It can make for duplicate declarations (e.g. User u = new User()) but at least you'll see clearly what types your variables are.
You don't need to access array items using Item...just get it as an element in the row element you're working with. That's why I have the double array for dt.Rows[0][CN_Password]. Again, C# uses square brackets, not parens for accessing array elements.
This is just a style thing, but I removed your _password field and just used an automatic property for Password. I didn't see _password being used in your code and thought it would just clutter things up. Automatic properties have a backing field automatically created by the compiler, so you don't have to keep track of the variable and the property. If you're using a lot of properties, this can be a big time saver.
You need to declare the type of user (in your code user is an object that is at runtime a USer, but not at compiletime). You can do the following:
var user = new User();
or
User user = new User();
In addition to the comments.
As Jonathan Dickinson correctly says ,
user.Password = dt.Rows[0]["Password"];
should do it since this is an indexer.

C# Cut/Copy & Paste objects

This is a 2 parter.
First I am having a heck of a time getting the paste part of a copy and paste operation to work.
I have a method that copies information to the Clipboard, works perfectly.
private void CopyData(string format, object data, string text)
{
bool addedData = false;
DataObject copyData = new DataObject();
if (!string.IsNullOrEmpty(text))
{
copyData.SetData(DataFormats.Text, text);
addedData = true;
}
if (!string.IsNullOrEmpty(format) && data != null)
{
copyData.SetData(format, false, data);
addedData = true;
//this is only for testing
object obj = null;
if (copyData.GetDataPresent(format))
obj = (object)copyData.GetData(format);
}
if (addedData)
Clipboard.SetDataObject(copyData, true);
}
When I check that the data was added the object (obj) is not null.
However when I then go to paste the data from a different method using the same format key I get null, every time.
private void PasteFromClipboard()
{
object obj = null;
IDataObject paste = null;
if (Clipboard.GetDataObject().GetDataPresent("mydatatype"))
obj = (object)Clipboard.GetDataObject().GetData("mydatatype");
else
return;
if (obj == null)
throw new NullReferenceException("Could not gather information from the
}
I have tried everything that I can think of and it just doesn't make sense. I created an array of strings to capture all of the format keys the DataObject was holding and "mydatatype" was the first one. I have tried casting, not casting, using (Clipboard.GetDataObject().GetData("mydatatype") as object) and I just cannot figure it out. I know that there is data there because I can go to NotePad and paste the text that I copied along with the object.
Any thoughts as to why I would be able to get the data in one method, but not another?
Secondly I was wondering how I would go about making a Cut & Paste operation work between two of my windows. I am thinking about something like Excel, where if only the text is pasted the data will remain, however if the objects are pasted then the source will be deleted.
Thanks
Patrick.
Try pulling the data out as Text (instead of "mydatatype") - at least to confirm you can read from the clipboard. This is most likely what Notepad is reading. Also, does it matter that you're copying with "format" but pasting with "mydatatype"?
Could it be that text parameter always has a value and gets set. Then possibly the second if the one that would set the object does not get executed. Or if it does since the data was set in the first if statement the second set fails to set it correctly.
My recommendation would be to walk the code in the debugger during the copy operation.
Before the paste use GetDataObject().GetFormats() to enumerate the list of formatting codes.
Perhaps your using the wrong one.. just an idea
Try using reflection like this:
private static T TryGetClipboardData<T>(IDataObject clipboardData, string dataFormat)
{
System.Reflection.FieldInfo fieldInfo = clipboardData.GetType().GetField("innerData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var outerData = fieldInfo.GetValue(clipboardData);
if (outerData == null)
{
return default(T);
}
fieldInfo = outerData.GetType().GetField("innerData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var innerData = fieldInfo.GetValue(outerData);
if (innerData is System.Runtime.InteropServices.ComTypes.IDataObject)
{
// It is (probably) necessary to wrap COM IDataObject to Windows.Forms.IDataObject
System.Windows.Forms.DataObject wrappedDataObject = new System.Windows.Forms.DataObject(innerData);
var data = wrappedDataObject.GetData(dataFormat);
if (data is T)
{
return (T)data;
}
}
return default(T);
}
I suspect the COM object of your data in the Clipboard had a difficult time converting itself to the format you specified. I'm also playing safe with the input format string so that it is registered as a proper clipboard format.
HTH

Categories