Search code examples
c#passwordsequalssecurestringpasswordbox

C# - compare two SecureStrings for equality


I have a WPF application with two PasswordBoxes, one for the password and another for the password to be entered a second time for confirmation purposes. I was wanting to use PasswordBox.SecurePassword to get the SecureString of the password, but I need to be able to compare the contents of the two PasswordBoxes to ensure equality before I accept the password. However, two identical SecureStrings are not considered equal:

var secString1 = new SecureString();
var secString2 = new SecureString();
foreach (char c in "testing")
{
    secString1.AppendChar(c);
    secString2.AppendChar(c);
}
Assert.AreEqual(secString1, secString2); // This fails

I was thinking comparing the Password property of the PasswordBoxes would defeat the point of accessing only SecurePassword because I'd be reading the plain-text password. What should I do to compare the two passwords without sacrificing security?

Edit: based on this question, I'm checking out this blog post about "using the Marshal class to convert the SecureString to ANSI or Unicode or a BSTR", then maybe I can compare those.


Solution

  • It looks like you could use this to compare the two SecureStrings.

    It uses unsafe code to iterate through the strings:

    bool SecureStringEqual(SecureString s1, SecureString s2)  
    {  
        if (s1 == null)  
        {  
            throw new ArgumentNullException("s1");  
        }  
        if (s2 == null)  
        {  
            throw new ArgumentNullException("s2");  
        }  
    
        if (s1.Length != s2.Length)  
        {  
            return false;  
        }  
    
        IntPtr bstr1 = IntPtr.Zero;  
        IntPtr bstr2 = IntPtr.Zero;  
    
        RuntimeHelpers.PrepareConstrainedRegions();  
    
        try 
        {  
            bstr1 = Marshal.SecureStringToBSTR(s1);  
            bstr2 = Marshal.SecureStringToBSTR(s2);  
    
            unsafe 
            {  
                for (Char* ptr1 = (Char*)bstr1.ToPointer(), ptr2 = (Char*)bstr2.ToPointer();  
                    *ptr1 != 0 && *ptr2 != 0;  
                     ++ptr1, ++ptr2)  
                {  
                    if (*ptr1 != *ptr2)  
                    {  
                        return false;  
                    }  
                }  
            }  
    
            return true;  
        }  
        finally 
        {  
            if (bstr1 != IntPtr.Zero)  
            {  
                Marshal.ZeroFreeBSTR(bstr1);  
            }  
    
            if (bstr2 != IntPtr.Zero)  
            {  
                Marshal.ZeroFreeBSTR(bstr2);  
            }  
        }  
    } 
    

    I have modified it below to work without unsafe code (note however you are able to see the string in plain text when debugging):

      Boolean SecureStringEqual(SecureString secureString1, SecureString secureString2)
      {
         if (secureString1 == null)
         {
            throw new ArgumentNullException("s1");
         }
         if (secureString2 == null)
         {
            throw new ArgumentNullException("s2");
         }
    
         if (secureString1.Length != secureString2.Length)
         {
            return false;
         }
    
         IntPtr ss_bstr1_ptr = IntPtr.Zero;
         IntPtr ss_bstr2_ptr = IntPtr.Zero;
    
         try
         {
            ss_bstr1_ptr = Marshal.SecureStringToBSTR(secureString1);
            ss_bstr2_ptr = Marshal.SecureStringToBSTR(secureString2);
    
            String str1 = Marshal.PtrToStringBSTR(ss_bstr1_ptr);
            String str2 = Marshal.PtrToStringBSTR(ss_bstr2_ptr);
    
            return str1.Equals(str2);
         }
         finally
         {
            if (ss_bstr1_ptr != IntPtr.Zero)
            {
               Marshal.ZeroFreeBSTR(ss_bstr1_ptr);
            }
    
            if (ss_bstr2_ptr != IntPtr.Zero)
            {
               Marshal.ZeroFreeBSTR(ss_bstr2_ptr);
            }
         }
      }