Wednesday, March 10, 2021

Installing ISV licenses

 

When it comes to license codes received from Independent Software Vendors (ISV), the right way to install these are through the LifeCycle Services (LCS) Asset Library by adding them to your build pipeline like this: https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/dev-tools/pipeline-add-license-package

But usually, you do not want to install you builds on a developer box (tier 1), because that will deploy a lot of binary files that you do not need. So, what can we do instead? As I see it there are three choices and I will explain each of them a little deeper:

  • Install using dedicated license deployable package
  • Install using command line import
  • Install by running SQL script directly on the database

Install using dedicated license deployable package

This method still requires you to install a deployable package as if you were installing a solution, but it will only contain one or more ISV licenses.

To manually create this package, go to you developer environment and the directory K:\AosService\PackagesLocalDirectory\bin\CustomDeployablePackage (might be different drive on you machine).



In here we will find a ImportISVLicense.zip file that we will extract to our favorite working directory, like C:\temp and copy your ISV license files that you received from your supplier into the license directory: C:\Temp\ImportISVLicense\AosService\Scripts\License



Re-zip the file and upload it to you LCS Asset Library and wait for it to validate. Make sure that the Package type says, “ISV License Package” and that it is valid, otherwise something was done wrong.



From hereon you can install the package to your environment by applying the package in the maintenance.

Of course, you can also skip the Asset Library if you prefer to manually apply the deployable package from the command line in you environment: https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/deployment/install-deployable-package

Install using command line import

Your license text file can also be applied to your environment if you have elevated privileges, which we did not have on Microsoft managed Tier 1 environment, but since they are history it should be possible on you own hosted machines.

You will need you license file in an accessible location, like C:\temp and the credentials of a SQL user with administrative access to you location database. Usually that would be the axdbadmin account from the environment details in LCS.



Open a command prompt as administrator and run the following scripts (Check that the drive letter, directories, and database match your environment):

K:\AOSService\PackagesLocalDirectory\Bin\Microsoft.Dynamics.AX.Deployment.Setup.exe --setupmode importlicensefile --metadatadir K:\AOSService\PackagesLocalDirectory --bindir K:\AOSService\PackagesLocalDirectory --sqlserver . --sqldatabase AxDB --sqluser axdbadmin --sqlpwd [axdbadmin password] --licensefilename c:\temp\[LicenseFileName].txt

Wait for the execution to finish which should not take many minutes and the restart your “World Wide Web Publishing Service”.

 


Install by running SQL scripts directly on the database

The last method is a little more hard-core and has only been proven to update an already installed license code. Like the last method you will need administrative access to your local database.

If you open your ISV license text file, you will find one or more rows of XML with license information like this:



So, the license if nothing more than text that we can copy into a SQL script, but we just need to find out where to put it.

The license information in D365FO is in the SysConfig table so running a SELECT * FROM SYSCONFIG will give you the current installed licenses.



We can see that there are some similarities between the table fields and the XML tags in the license file, so what we need is the ID value, which relates to the ISV solution license code element in the AOT.

Lookup you ISV solution license code and check out the properties to find the PublicKey ID. That is what we will use to find the right license code in the SysConfig table.

 


 

So running the script below would update the existing license with new signature and expiration date:

update SYSCONFIG

set VALUE = '[Signature string from file]',

       SHADOWVALUE = '[Signature string from file]',

       EXPIRATION = '[Expire date from file]',

       TIMESTAMP = '[Timestamp date from file]'

       where ID = 100001

You should synchronize your database from Visual Studio afterwards, but this should update your ISV license as well.

Disclaimer:

As I wrote in the beginning of the section, I have only tried this to update an existing code and not installing of brand new one. There might be something that will need attendance, regarding the certification serial number that is taken care of automatically when installing through a deployable package.

Monday, February 22, 2021

Dealing with TLS Cipher suites in HTTPS calls on cloud hosted environments (CHE)

Another fine day at the office gave me a challenge that I met before but never really investigated. It has to do with HTTPS calls that fails due to difference between supported TLS cipher suites on server that host a web service and the client consuming it.

I always though that if I ask the client to make a TLS 1.2 call and the server supports TLS 1.2 then everything should be in order. But that is not necessarily correct because of the Cipher suites that each end supports.

An explanation of ciphers is here https://blog.keyfactor.com/cipher-suites-explained if you are into that kind of stuff.

My assignment was to make a call to a semi-public webservice (just need a API-key) from D365 FO but as usually I did a few tests from Postman and C# from my own PC before hitting the D365 FO CHE.

In Postman I immediately got a valid response so I went to my trusted VS 2019 IDE and wrote this test program and again I got the expected answer:

 

However, when I moved the test program to my CHE and ran it, I got the error message:

“The request was aborted: Could not create SSL/TLS secure channel”


I used the Qualys SSL Labs (https://www.ssllabs.com/ssltest/analyze.html) tool to check that the service actually supported TLS 1.2 and it did.


I fired up Fiddler Everywhere (https://www.telerik.com/download/fiddler/fiddler-everywhere-windows) and found that even though the request was made with TLS 1.2 there was no overlapping supported ciphers.

My CHE client did not have any TLS_ECDHE_ECDSA_* ciphers which was all the service supported.


So, I checked that the Window server 2016 supported the needed cipher through this link: https://docs.microsoft.com/en-us/windows/win32/secauthn/cipher-suites-in-schannel

And that D365 FO apps also supported the right ciphers through this link: https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/sysadmin/encryption

 

Everything seems to be alright and supported, but still the call would only used unsupported ciphers.

Both Windows server 2016 and D365 FO supported these 4 ciphers, but the client did not:

  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)                                         
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)                                       
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)                                         
  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)                                       

At last, I got a hint from Microsoft support that I should check the registry: HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002\ and there I found that only part of the server supported ciphers was listed.

I added the 4 ciphers above like this:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002]

"Functions"="TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA"

That made a difference, and everything worked.