P / Invoke CryptUnprotectData Breaks SqlConnection Constructor

I am trying to use CryptUnprotectData to read a password protected with CryptProtectData in SecureString and use it to connect to the database. I can get the correct password, but try to create a new SqlConnection , after which the following will not be done:

 System.TypeInitializationException was unhandled HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlConnection' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlConnection StackTrace: at System.Data.SqlClient.SqlConnection..ctor() at System.Data.SqlClient.SqlConnection..ctor(String connectionString, SqlCredential credential) at System.Data.SqlClient.SqlConnection..ctor(String connectionString) at ProtectedSqlTest.Program.Main() in C:\Git\ProtectedSqlTest\ProtectedSqlTest\Program.cs:line 16 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlConnectionFactory' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlConnectionFactory StackTrace: at System.Data.SqlClient.SqlConnection..cctor() InnerException: HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlPerformanceCounters' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlPerformanceCounters StackTrace: at System.Data.SqlClient.SqlConnectionFactory..cctor() InnerException: HResult=-2147024809 Message=The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG)) Source=mscorlib StackTrace: at System.Globalization.TextInfo.InternalChangeCaseString(IntPtr handle, IntPtr handleOrigin, String localeName, String str, Boolean isToUpper) at System.Globalization.TextInfo.ToLower(String str) at System.String.ToLower(CultureInfo culture) at System.Diagnostics.PerformanceCounterLib.GetPerformanceCounterLib(String machineName, CultureInfo culture) at System.Diagnostics.PerformanceCounterLib.IsCustomCategory(String machine, String category) at System.Diagnostics.PerformanceCounter.InitializeImpl() at System.Diagnostics.PerformanceCounter.set_RawValue(Int64 value) at System.Data.ProviderBase.DbConnectionPoolCounters.Counter..ctor(String categoryName, String instanceName, String counterName, PerformanceCounterType counterType) at System.Data.ProviderBase.DbConnectionPoolCounters..ctor(String categoryName, String categoryHelp) at System.Data.SqlClient.SqlPerformanceCounters..ctor() at System.Data.SqlClient.SqlPerformanceCounters..cctor() InnerException: 

Just call CryptUnprotectData to fail SqlConnection , the connection itself does not need to use the returned SecureString .

I am using extension methods here , as described in this post for my minimal replay:

 class Program { const string ProtectedSecret = /* SNIP - base 64 encoded protected data here */; static void Main() { // calling AppendProtectedData breaks the following SqlConnection // without the following line the application works fine new SecureString().AppendProtectedData(Convert.FromBase64String(ProtectedSecret)); using (var conn = new SqlConnection("Server=(localdb)\\MSSqlLocalDb;Trusted_Connection=true")) using (var cmd = new SqlCommand("select 1", conn)) { conn.Open(); cmd.ExecuteNonQuery(); } } } 

If I create a new SqlConnection before uploading the password, I can create a new SqlConnection penalty for the entire duration of the application, since it seems to use the same SqlConnectionFactory , but that means that I have to do something as a workaround similar at the beginning of the application:

 new SqlConnection().Dispose(); 

... which I would like to avoid.

The following steps do not help:

  • Debugging vs Release build
  • Visual Studio debugging versus command line
  • The CryptProtectFlags change that is passed to CryptUnprotectData .
  • Removing RuntimeHelpers.PrepareConstrainedRegions() from the protection method.

Windows 10, VS Enterprise 2015, console application (.NET 4.6.1)

UPDATE: Running data protection code on other threads gives a similar exception with another main reason:

 System.TypeInitializationException was unhandled HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlConnection' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlConnection StackTrace: at System.Data.SqlClient.SqlConnection..ctor() at System.Data.SqlClient.SqlConnection..ctor(String connectionString, SqlCredential credential) at System.Data.SqlClient.SqlConnection..ctor(String connectionString) at ProtectedSqlTest.Program.Main() in C:\Git\ProtectedSqlTest\ProtectedSqlTest\Program.cs:line 17 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException: HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlConnectionFactory' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlConnectionFactory StackTrace: at System.Data.SqlClient.SqlConnection..cctor() InnerException: HResult=-2146233036 Message=The type initializer for 'System.Data.SqlClient.SqlPerformanceCounters' threw an exception. Source=System.Data TypeName=System.Data.SqlClient.SqlPerformanceCounters StackTrace: at System.Data.SqlClient.SqlConnectionFactory..cctor() InnerException: BareMessage=Configuration system failed to initialize HResult=-2146232062 Line=0 Message=Configuration system failed to initialize Source=System.Configuration StackTrace: at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey) at System.Configuration.ClientConfigurationSystem.PrepareClientConfigSystem(String sectionName) at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName) at System.Configuration.ConfigurationManager.GetSection(String sectionName) at System.Configuration.PrivilegedConfigurationManager.GetSection(String sectionName) at System.Diagnostics.DiagnosticsConfiguration.Initialize() at System.Diagnostics.DiagnosticsConfiguration.get_SwitchSettings() at System.Diagnostics.Switch.InitializeConfigSettings() at System.Diagnostics.Switch.InitializeWithStatus() at System.Diagnostics.Switch.get_SwitchSetting() at System.Data.ProviderBase.DbConnectionPoolCounters..ctor(String categoryName, String categoryHelp) at System.Data.SqlClient.SqlPerformanceCounters..ctor() at System.Data.SqlClient.SqlPerformanceCounters..cctor() InnerException: HResult=-2147024809 Message=Item has already been added. Key in dictionary: 'MACHINE' Key being added: 'MACHINE' Source=mscorlib StackTrace: at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) at System.Collections.Hashtable.Add(Object key, Object value) at System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(String configPath) at System.Configuration.ClientConfigurationSystem.EnsureInit(String configKey) InnerException: 
+5
source share
2 answers

I recently experienced similar symptoms using the same code from http://www.griffinscs.com/?p=12 : any call to CryptUnprotectData will throw an exception in some unrelated code. Interestingly, the failure occurred only on a machine running Windows 10; the same code worked fine on a windows 7 machine.

I fixed the problem by changing the szDataDescr parameter szDataDescr both CryptProtectData and CryptUnprotectData from string to IntPtr and passing IntPtr.Zero instead of string.Empty in two calls.

+1
source

Interestingly, the fault code:

 internal static PerformanceCounterLib GetPerformanceCounterLib(string machineName, CultureInfo culture) { SharedUtils.CheckEnvironment(); string lcidString = culture.LCID.ToString("X3", CultureInfo.InvariantCulture); if (machineName.CompareTo(".") == 0) machineName = ComputerName.ToLower(CultureInfo.InvariantCulture); else machineName = machineName.ToLower(CultureInfo.InvariantCulture); ... 

the line calling ComputerName.ToLower(CultureInfo.InvariantCulture) throws an exception.

You can reproduce the same behavior, only the calling code

 new SecureString().AppendProtectedData(Convert.FromBase64String(ProtectedSecret)); string lower = "Something".ToLower(CultureInfo.InvariantCulture); 

Somehow in the constructor of the TextInfo class

 this.m_dataHandle = CompareInfo.InternalInitSortHandle(m_textInfoName, out handleOrigin); 

returns invalid data if it is not called before the CryptUnprotectData function.

This is like a structure error. You can send it to Microsoft. In the meantime, you can call this line in advance to prevent an error.

 "".ToLower(CultureInfo.InvariantCulture); 
+3
source

Source: https://habr.com/ru/post/1261565/


All Articles