Encrypt CrmConnection + Enterprise Library 5.0
It’s no secret that Microsoft Dynamics CRM 2011 (MSCRM) allows developers to programmaticly pretty much anything an end user can do manually. All of the operations you perform (with very limited exceptions) are supposed to happen through the MSCRM Web Services (OrganizationService, DiscoveryService). One of the reasons for this mandate is so that MSCRM can enforce access rights through its security model. This makes perfect sense. If you have a Sql Server account that allows you to read/write and delete records, it would render the security model effectively useless.
Storing PlainText credentials in a code file or .config file is never a good idea but it’s a particularly bad idea with MSCRM Development. That’s b/c whichever way you chose to authenticate, you need to specify quite a bit of information and for OnPrem installs, you have to provide much more information than you otherwise would if you were just connecting to the database directly using a trusted connection. That information includes a valid UserName, a Domain Name and a Password. Unlike other IDBConnection objects, the CrmConnection is really just syntactical sugar that wraps all the information you need to make web service calls. I’ve beaten the whole “how to use CrmConnection” discussion into the ground but just for a quick reference, here’s what a typical OnPrem CrmConnection value would look like in your .config file:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <clear/> <add name="Primary" connectionString="Url=https://crminstance/OrgName; Username=**; password=**; /> </connectionStrings> </configuration>
B/c MSCRM development is still in its infancy in many ways, it’s very common to see MSCRM developers do things they would never dream of doing when they’re doing other .NET development. The thing is though, MSCRM development is .NET development in every respect. Any bad development practice with respect to say ASP.NET is a bad practice when done with MSCRM. Sure, b/c there aren’t nearly as many books, articles, applications or experienced developers in the MSCRM world, one can get away with doing ‘bad’ things without worrying about all the negative repercussions you otherwise would, but bad is still bad. I’ve personally seen a couple of different instances where the above code style was used and cleared a security audit b/c “You have to do it this way in MSCRM.” And in some ways, it’s true. If you encypt the connectionstring directly, you can’t use the CrmConnection.Parse method the way you normally would. You can’t instantiate the CrmConnection with the overloaded constructor that takes in the ConnectionString name. yes, you can still ‘get there’ but it’s very awkward and completely offsets most of the benefits associated with using the CrmConnection in the first place. Fortunately, there’s a very simple solution that doesn’t carry with it any of the baggage a ‘rolling your own’ approach would. One such solution is using the Enterprise library.
So assume that I have the connectionString setting shown above. Opening up the Enterprise Library 5.0 Configuration Tool you have access to the Database Settings component. In this case, our CrmConnection ‘s ConnectionString is named “Primary“. Below is what the config tool looks like:
So first you need to specify the Default Database (in this case named ‘Primary‘). Next, you need to chose the Protection Provider. OOB you can use the RsaProtectedConfigurationProvider to the DataProtectionConfigurationProvider. (You can click through the respective links with help deciding which one is best suited for your scenario. I can say with certainty though, choosing either of them is a more secure solution than not encrypting your CrmConnection so comparatively, you can’t really get it wrong). The only other option is whether or not you want to force someone to input a password to change the values (I can’t think of a case where you wouldn’t want to password protect it – if you don’t, nothing would stop someone from injecting information in the config file by simply copying and pasting over the existing values).
Once you’ve done this, you just need to Save the configuration and you’re pretty much done. After you save, the exact same information shown in the first example turns into the following:
<?xml version="1.0"?> <configuration> <configSections> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> </configSections> <dataConfiguration configProtectionProvider="RsaProtectedConfigurationProvider"> <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <KeyName>Rsa Key</KeyName> </KeyInfo> <CipherData> <CipherValue>iHv/Y1Vn7V6NSZT4Et5adE137Vm8z3vnRxiJSl5SMX6iK1aEjUL8rttdtJMuryn2oEhjup8O20j4KpjoRsVHYNIRyaZ7KRlpkqn5aDbv6rGRHvsOHANE53TYTT77IId7fGUHgAAY159G8WGlCJ73vbSEN/muJsRzB34qaPZ+QfY=</CipherValue> </CipherData> </EncryptedKey> </KeyInfo> <CipherData> <CipherValue>ynramhCcXmc3M7nAQns+hUErLTBxT5IgK43E/sg9AlbGtMhK1AtL2JN1qNEkRln9NZY2VSOt12k=</CipherValue> </CipherData> </EncryptedData> </dataConfiguration> <connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider"> <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <KeyName>Rsa Key</KeyName> </KeyInfo> <CipherData> <CipherValue>PTBvWPczFyGMrLmFad8xtV1iokZLP6QWnUPv74EMR9u3ZNVyfP5SrcGx+IO6JEkwz0155PAvPmj6UzsAcPudI2Edvflkf4CjifMllwIEksOzK71On3WihURqL6C5KtJ7fdIZpX+Hh7BVI/7ZEuu3aOwrZry0lMeF4UoOIcWnvu4=</CipherValue> </CipherData> </EncryptedKey> </KeyInfo> <CipherData> <CipherValue>jdSzONKEqttMHeapu16JgkYLP2py9DIrjwshbB0YmMZjztXaaU4DKZBJmF544e4JZhjZ8OWSBmUATZghc7JSfY2rwo2wtfjjL5F/7PUieutty7UJbQUJ6Fu5CZli1REC1KxG8Mct8rluWujV7EstaslR0Zk9cQwK0Mv7S4+sEZ/ZQFOCbSDaK20a1N1zBrokqsXGktPlrhYYwsvP1G+5Z+KZixg4LEs6HaC1uR3/AIuJeII2bNl3h/ZLqHaWP5Rm</CipherValue> </CipherData> </EncryptedData> </connectionStrings> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
Now you can use the following code to verify that everything is decrypted correctly and your old code will run correctly without any other modifications:
CrmConnection Primary = new CrmConnection("Primary"); Console.WriteLine(Primary.ServiceUri); Console.WriteLine(ConfigurationManager.ConnectionStrings.ConnectionString); Console.WriteLine(ConfigurationManager.ConnectionStrings.Name);
Running the above code returns the following values:
“Url=http://****:5555/**; Domain=**.com; Username=**; Password=**”
For the pedantic, you may notice that the actual values in the initial config section and the output differ – in reality, they match exactly – for security purposes I replaced sensitive information with asterisks.
Leave any questions in the Comments section, or write me directly. firstname.lastname@example.org– /b/ill
KeyWords: Dynamics CRM 2011,RSAProtectedConfigurationProvider, DataProtectionConfigurationProvider, William Ryan , Bill Ryan, RelatedEntities, CrmConnection, Microsoft.Xrm.Client, Microsoft.Xrm.Client.CrmConnection, OrganizationServiceContext , AssignResponse, AssignRequest, Enterprise Library 5.0, DiscoveryService, Microsoft.Xrm.Client.Services.OrganizationService, Dynamics4, Dynamics4.com