8

I used following method to encrypt connectionstrings section of my app.config in my WinForms project(I'm using Code First EF in my project):

public static void EncryptConfig(string exeConfigName)
{
    var config = ConfigurationManager.OpenExeConfiguration(exeConfigName);
    var section = config.GetSection("connectionStrings") as ConnectionStringsSection;
    if (section != null || !section.SectionInformation.IsProtected)
    {
       section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
       config.Save();
    }
}

and I use following method to decrypt the connectionstrings section, too:

public static void DecryptConfig(string exeConfigName)
{
    var config = ConfigurationManager.OpenExeConfiguration(exeConfigName);
    var section = config.GetSection("connectionStrings") as ConnectionStringsSection;
    if (section != null && section.SectionInformation.IsProtected)
        section.SectionInformation.UnprotectSection();
}

this method works in my machine, but when I deploy my application to another machine I get following exception:

System.Configuration.ConfigurationErrorsException: Failed to decrypt using provider 'DataProtectionConfigurationProvider'. Error message from the provider: Key not valid for use in specified state. (Exception from HRESULT: 0x8009000B) (D:\l4test\Level4UI.exe.config line 82) ---> System.Runtime.InteropServices.COMException: Key not valid for use in specified state. (Exception from HRESULT: 0x8009000B)

at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)

at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode)

at System.Configuration.DpapiProtectedConfigurationProvider.DecryptText(String encText)

at System.Configuration.DpapiProtectedConfigurationProvider.Decrypt(XmlNode encryptedNode)

at System.Configuration.ProtectedConfigurationSection.DecryptSection(String encryptedXml, ProtectedConfigurationProvider provider)

at System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.DecryptSection(String encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection)

at System.Configuration.Internal.DelegatingConfigHost.DecryptSection(String encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection)

at System.Configuration.Internal.DelegatingConfigHost.DecryptSection(String encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfigSection)

at System.Configuration.BaseConfigurationRecord.CallHostDecryptSection(String encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfig)

at System.Configuration.BaseConfigurationRecord.DecryptConfigSection(ConfigXmlReader reader, ProtectedConfigurationProvider protectionProvider)

--- End of inner exception stack trace ---

at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)

at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)

at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)

at System.Configuration.Configuration.GetSection(String sectionName)

at IASCo.Infrastructure.Common.Utilities.Configuration.ConfigurationEncryption.DecryptConfig(string exeConfigName)

In this thread, Jeremy said:

You need to publish with the section decrypted. The key that is used to encrypt/decrypt is machine specific.

My app will be installed on a network share and run from there but there will be more than one person who may access the app from their work stations, How can I specify a single key to decrypt the connectionString section that will work from all the machines used to access the app.

I'm looking for a way to do this job(encrypt in my machine and decrypt in user's machines) using c#.

Community
  • 1
  • 1
Masoud
  • 8,020
  • 12
  • 62
  • 123
  • 1
    The key must already be present on the computer, either attached to the user or to the machine. If you have no key on the computer that runs the code you get this exception. – Lasse V. Karlsen Sep 21 '16 at 09:03
  • 2
    Your code fine. The problem is, as @LasseV.Karlsen mentioned, that the `DataProtectionConfigurationProvider` will encrypt data specific to a machine. You need to use `RsaProtectionConfigurationProvider` and import the key to the machines that will run the app. See [this](https://msdn.microsoft.com/en-us/library/yxw286t2.aspx) on how to export & import the key. – Howwie Sep 29 '16 at 08:21
  • Like others told earlier, you must use RSAProtectedConfigurationProvider in your case. Please visit this page for more information https://msdn.microsoft.com/en-us/library/ff647398.aspx – Cinchoo Sep 29 '16 at 14:24
  • @Masoud Did you resolve this issue? I facing same issue – Prageeth Liyanage Jan 18 '22 at 05:08
  • 1
    @PrageethLiyanage I changed my approach, I encrypt my con.string using my encrption method then put it in App.Config, then at my program startup put following code:`if (ConfigurationManager.ConnectionStrings != null) {var conString =ConfigurationManager.ConnectionStrings["name"]; if (conString != null) {MyAppContext.ConnectionString = conString.ConnectionString; if (!DatabaseUtils.IsValidSqlServerConnectioString(MyAppContext.ConnectionString)) {MyAppContext.ConnectionString=DecryptStr(MyAppContext.ConnectionString);}}}` and inject MyAppContext.ConnectionString in to my DbContext constructor – Masoud Jan 19 '22 at 08:33

1 Answers1

1

Your code looks fine to me - other than needing to change from the user of

 if (section != null || !section.SectionInformation.IsProtected)
{
   section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
   config.Save();
}

to

 if (section != null || !section.SectionInformation.IsProtected)
    {
       section.SectionInformation.ProtectSection("RsaProtectionConfigurationProvider");
       config.Save();
    }

When you do create the RSA keys make sure you use the -exp switch to enable exporting of the keys as per the docs: https://msdn.microsoft.com/en-us/library/yxw286t2.aspx

aspnet_regiis -pc "KeysetName"–exp

As stated by the earlier answerers. In addition to this, if the users of the application are on an IIS network, you may user ASP.NET Impersonation vis your organisations access control lists (ACLs). This will allow you to remove the need to authenticate on a machine level, which doesn't suit all applications. See: https://msdn.microsoft.com/en-us/library/xh507fc5.aspx

  • I changed "DataProtectionConfigurationProvider" to "RsaProtectionConfigurationProvider" but the problem exists yet, another thing that my project is a winForms project and not a web application. – Masoud Oct 02 '16 at 11:05