2016-12-20 15 views
5

Я пытаюсь использовать CryptUnprotectData, чтобы прочитать пароль, защищенный с помощью CryptProtectData, в SecureString и использовать его для подключения к базе данных. Я могу получить правильный пароль, но пытается создать новую SqlConnection после этого терпит неудачу со следующим:P/Invoke CryptUnprotectData breaks Конструктор SqlConnection

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: 

Достаточно просто позвонить CryptUnprotectData для SqlConnection потерпеть неудачу, само соединение не нужно использовать возвращенный SecureString.

Я использую методы расширения from here, как описан в this post моего минимального Repro:

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(); 
     } 
    } 
} 

Если я создать новый SqlConnectionперед тем я загружаю пароль, я могу создать новый SqlConnection s отлично подходит для продолжительность использования приложения, поскольку она, как представляется, использует то же самое SqlConnectionFactory, но это означает, что в качестве обходного пути я должен сделать что-то подобное в начале приложения:

new SqlConnection().Dispose(); 

... который я бы хотел избежать.

Следующая не помогают:

  • Debug против релиз построить
  • Отладка в Visual Studio против запуска из командной строки
  • Изменение CryptProtectFlags, который передается в CryptUnprotectData.
  • Извлечение RuntimeHelpers.PrepareConstrainedRegions() из метода защиты.

для Windows 10, VS Enterprise 2015, консольное приложение (.NET 4.6.1)

UPDATE: Выполнения кода защиты данных в других потоках дает аналогичное исключение с другой первопричиной:

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: 
+0

Есть ли какая-либо конкретная причина, по которой вы должны P/Invoke на 'CryptUnprotectData' вместо использования управляемой оболочки [' ProtectedData'] (https://msdn.microsoft.com/library/system.security.cryptography.protecteddata) ? –

+0

Если есть какие-либо проблемы с кодом P/Invoke (что кажется вероятным), диагностировать это проще, если вы включите всех [помощников по удаленной отладке] (https://msdn.microsoft.com/library/d21c150d) («Настройки исключения» «от VS). Даже это не будет диагностировать все проблемы, но это помогает. –

+0

@JeroenMostert Я бы хотел избежать обработки незащищенных данных в управляемой памяти. @stuartd Устранение «SecureString» не помогает. –

ответ

1

Я недавно возникли подобные симптомы, используя один и тот же код из http://www.griffinscs.com/?p=12: любой вызов CryptUnprotectData приведет к исключению в некоторых несвязанным кодом. Интересно, что сбой произошел только на машине под Windows 10; тот же код работал отлично на машине Windows 7.

Я исправил эту проблему путем изменения деклараций szDataDescr параметров в обоих CryptProtectData и CryptUnprotectData от string до IntPtr, и проходя IntPtr.Zero вместо string.Empty в двух вызовов.

+0

FWIW, подпись P/Invoke от pinvoke.net использует 'CharSet.Auto', но определение функции Win32 использует' LPWSTR', который всегда является Unicode. – yaakov

3

Интересно разломообразования код:

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); 
    ... 

исключение вызывает строка, вызывающая ComputerName.ToLower(CultureInfo.InvariantCulture).

Вы можете воспроизвести такое же поведение просто вызывающий код

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

Как-то в конструкторе класса TextInfo

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

возвращает неверные данные, если это не вызывается, прежде чем CryptUnprotectData функции.

Это похоже на ошибку в структуре. Вы можете отправить его в Microsoft. Тем временем вы можете позвонить в эту строку заранее, чтобы предотвратить ошибку.

"".ToLower(CultureInfo.InvariantCulture); 
+0

Выполнение вызова 'ToLower' перед кодом защиты данных, похоже, имеет такой же эффект, как и выполнение защитного кода в другом потоке. Конструктор 'SClConnection' все еще терпит неудачу, только с другой основной причиной: http://pastebin.com/LCGriSnn –

+1

То, что фактическая ошибка происходит в несвязанном коде, указывает на некоторая форма повреждения памяти (но, скорее всего, не полностью дикий указатель, потому что то ошибка не всегда будет в том же (ish) месте. – Zastai

+0

У меня нет доступа к 'pastebin.com'. Я не вижу исключения. Вы уверены, что вы вызвали' ToLower' с 'CultureInfo. InvariantCulture'? Parameterless не решает проблему.Я могу успешно создать 'SqlConnection' после' ToLower (CultureInfo.InvariantCulture) '. –