Я пытаюсь использовать 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:
Есть ли какая-либо конкретная причина, по которой вы должны P/Invoke на 'CryptUnprotectData' вместо использования управляемой оболочки [' ProtectedData'] (https://msdn.microsoft.com/library/system.security.cryptography.protecteddata) ? –
Если есть какие-либо проблемы с кодом P/Invoke (что кажется вероятным), диагностировать это проще, если вы включите всех [помощников по удаленной отладке] (https://msdn.microsoft.com/library/d21c150d) («Настройки исключения» «от VS). Даже это не будет диагностировать все проблемы, но это помогает. –
@JeroenMostert Я бы хотел избежать обработки незащищенных данных в управляемой памяти. @stuartd Устранение «SecureString» не помогает. –