3

We are trying to connect to IBM MQ Server v 9.1.0.x. from RHEL 8.3 using .net core 3.1 and IBMMQDotnetClient 9.2.0.1 standard library from nuget.

We are stuck on getting CompCode: 2 Reason: 2059 error.

We know what is the reason: during ssl negotiation cipher spec is set to TLS_RSA_WITH_AES_128_CBC_SHA256 but the channel has it set to TLS_RSA_WITH_AES_256_CBC_SHA256.
ERR*.TRC

CommentInsert2(TLS_RSA_WITH_AES_256_CBC_SHA256)
CommentInsert3(TLS_RSA_WITH_AES_128_CBC_SHA256)
AMQ9631E: The CipherSpec negotiated during the SSL handshake does not match the required CipherSpec for channel

We had the same issue on Windows but doing Disable-TlsCipherSuite -Name TLS_RSA_WITH_AES_128_CBC_SHA256 has resolved the issue.

What we've done already:

  1. We are specifying SSL_CIPHER_SPEC_PROPERTY as TLS_RSA_WITH_AES_256_CBC_SHA256.

Unfortunately it is ignored, as we are using managed client (MQC.TRANSPORT_MQSERIES_MANAGED) it's the only valid option for managed library. All security calls are transferred to internal security providers (Schanel/SslStreams on Windows and OpenSsl on Linux).
We know that we should alter cipher policy on Linux, do you have any idea how to:

  1. Disable TLS_RSA_WITH_AES_128_CBC_SHA256 on RHEL?

UPDATE
Disable-TlsCipherSuite -Name TLS_RSA_WITH_AES_128_CBC_SHA256 is a Powershell cmd.

UPDATE 1

On Linux (RHEL8.3) During handshake my frame looks like:

    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)  
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)  
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)  
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)  
    Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)  
    Cipher Suite: TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xccaa)  
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x006b)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x0067)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
    Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
    Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
    **Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)**
    **Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)**
    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
    Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)

UPDATE 2
We managed to force cipher used during negotiation and then during MakeSecuredConnection. Unfortunately it's only doable on .NET5.0 and hack in MakeSecureConnection is needed.
I tried the same approach using .net core 3.1 but failed probably because of use of AuthenticateAsClientAsync.

Here is .NET5.0 IBM MQ Client 9.2.0.1 hack

  1. Decompile the code of IBMMQClient (amqmdnetstd.dll)
  2. Mark .csproj as .NET5.0 <TargetFramework>net5.0</TargetFramework>
  3. Edit MakeSecureConnection method in class MQEncryptedSocket as follows

only .NET5.0

                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    stream.AuthenticateAsClient("*", x509Certificate2Collection, sslProtocol, sslCertRevocationCheck);
                }
                else
                {
                    //stream.AuthenticateAsClient("*", x509Certificate2Collection, sslProtocol, sslCertRevocationCheck);
                    var sslClientOptions = new SslClientAuthenticationOptions()
                    {
                        CertificateRevocationCheckMode = sslCertRevocationCheck ? X509RevocationMode.Offline   : X509RevocationMode.NoCheck,
                        ClientCertificates = x509Certificate2Collection,
                        EnabledSslProtocols = sslProtocol, 
                        TargetHost = "*",  
                        RemoteCertificateValidationCallback =ClientValidatingServerCertificate,
                        LocalCertificateSelectionCallback = FixClientCertificate,
                        CipherSuitesPolicy = new CipherSuitesPolicy(new List<TlsCipherSuite>() { Enum.Parse<TlsCipherSuite>(cipherSpec) })
                    };
                    TrText(method, $"Setting Cipher for AuthenticateAsClient {string.Join(':',sslClientOptions.CipherSuitesPolicy.AllowedCipherSuites)}");
                    stream.AuthenticateAsClient(sslClientOptions);
                }

for .net core 3.1 you have to call

    stream.AuthenticateAsClientAsync(sslClientOptions); 

Since there is no method AuthenticateAsClient overload accepting SslClientAuthenticationOptions parameter.
.net standard 2.0 and 2.1 don't have CipherSuitesPolicy parameter in class SslClientAuthenticationOptions that's why I had to move the library from .net standard 2.0 to .net5.0 (with success) and .net core 3.1 without.
Do you have any clue how is it possible to make it work on .net core 3.1?

Tom Ash
  • 158
  • 11
  • The client specifies the TLS version and TLS version has to be compatible with the encryption mode (https://en.wikipedia.org/wiki/Transport_Layer_Security). So if c# does not specify the TLS version the default version is used in windows (which may not be correct). So then you need to add in c# client the TLS version : System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 – jdweng Nov 20 '20 at 13:37
  • As part of TLS the server sends a certificate block with the names of the certificates the server accepts. Then client checks which certificate are in the certificate stores to find a matching certificate. So the certificate has to be in both the client and server. In Windows the certificate has to be in both the machine certificate store and the user certificate store. – jdweng Nov 20 '20 at 13:39
  • Thank you for the answer. We are using tls12, we are using Linux. – Tom Ash Nov 20 '20 at 13:53
  • You can use a sniffer like wireshark or fiddler and look at the TLS messages from server to client and find the certificate block with names of the certificate. Something is wrong with the certificates loaded on server and/or client. There must be a matching certificate on server and client that meets the TLS 1.2 requirements. – jdweng Nov 20 '20 at 14:05
  • May be certificate is expired. Here is an interesting article : https://learn.microsoft.com/en-us/security/sdl/cryptographic-recommendations – jdweng Nov 20 '20 at 14:14
  • Certificate is correct. We can run integration tests on Windows with pass and they fail on Linux. As I wrote above, we were facing the same behavior on Windows, but we managed to disable the cipher – Tom Ash Nov 20 '20 at 14:18
  • Is the certificate bad? The encryption mode is determine from the certificate. If the certificate has the wrong mode for the key you are using then you will fail. Disabling the cipher mode is just a work-around for a bad certificate. Using a sniffer you should verify the TLS version is correct and you are not trying TLS 1.0/1.1. – jdweng Nov 20 '20 at 14:27
  • TLSv1.2 Record Layer: Handshake Protocol: Client Hello Handshake Type Client Hello (1) Version: TLS 1.2 (0x0303) – Tom Ash Nov 20 '20 at 15:03
  • Hi Tom, I don't know how to fix it on Linux, but could you edit and provide more context around how you use `Disable-TlsCipherSuite -Name TLS_RSA_WITH_AES_128_CBC_SHA256` on windows to obtain the result, this could help me on some windows setups. – JoshMc Nov 20 '20 at 15:51
  • JoshMc sorry but I don't understand. What do you mean by provide more context? I would love to do it but I have no idea what should I write about. This powershell command disables cipher suite from the list that I found in group policy. – Tom Ash Nov 20 '20 at 16:09
  • Did it remove it Server wide or can it be limited to a specific user or process? Knowing that it is a powershell command provides the context I was looking for. – JoshMc Nov 20 '20 at 16:11
  • Did you see in sniffer the names of the certificate? One of the TLS messages contains the list of certificates. – jdweng Nov 20 '20 at 18:15
  • 1
    https://github.com/dotnet/runtime/issues/23818 gives some details on why the IBM MQ managed .NET client is not able to harrow down the the specific cipher like it does in the other clients, it only uses the cipher to figure out what version to use (TLS1.2 for example), the negotiation is then against all the client allows not a single cipher unless you modify the policy on windows. If the windows server was on 9.2 they could eliminate the AES_128 cipher from the last that is presented by the server and this would force he negotiation to use a different value. – JoshMc Nov 20 '20 at 21:45
  • Another option would be to setup a local MQIPT instance as a proxy, this instance could advertise to your client ONLY the cipher you want, and then be configured to re-encrypt with the same when sending on to the MQ server. – JoshMc Nov 20 '20 at 21:46
  • Is the list you added from the client or server? Can you add the other list of ciphers? – JoshMc Nov 22 '20 at 17:03
  • The list is from the client side. Server side has Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c) but I understand that this is for creating secure communication between client and QM. When we connect to QM -> CHANNEL which has different cipher set. – Tom Ash Nov 22 '20 at 20:09
  • I was interested in the full list and order that is presented from the server. – JoshMc Nov 23 '20 at 08:36
  • There was just one cipher during server hello Handshake Protocol: Server Hello Handshake Type: Server Hello (2) Length: 91 Version: TLS 1.2 (0x0303) Random: Y… Session ID Length: 32 Session ID: X… Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c) Compression Method: null (0) Extensions Length: 19 Extension: ec_point_formats (len=2) Extension: encrypt_then_mac (len=0) Extension: extended_master_secret (len=0) Extension: renegotiation_info (len=1) this is how Server Hello frame works as RFC5246 it has only selected cipher – Tom Ash Nov 23 '20 at 08:46
  • 1
    Yes you are correct, the client sends the full list and the server will pick backed on which one is first in its own list (I had thought this list was also sent back to the client). on MQ v9.1 the server side order is with TLS1.0 disabled (by default) is `TLS_RSA_WITH_AES_128_CBC_SHA256 TLS_RSA_WITH_AES_256_CBC_SHA256 TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`. – JoshMc Nov 23 '20 at 09:58
  • 1
    Based on that output and your client side list you are receiving the expected negotiation. At MQ v9.2, this list by default was re-ordered from what I recall and the AES_128 is no longer first on the list, also at 9.2 the admin can change the order and allowed ciphers. – JoshMc Nov 23 '20 at 10:18
  • Do you mean MQ Server 9.2 by saying MQ v9.2 or client? Server is 9.1.0.5, client is 9.2.0.1 – Tom Ash Nov 23 '20 at 10:26
  • 1
    Correct, at 9.1 server the result you see is expected, at 9.2 you would likely see a different result. My only other suggestion is to it may be possible to recompile the openssl that the MQ libraries on Linux is using so that the `TLS_RSA_WITH_AES_128_CBC_SHA256` is not offered. Or as I mentioned put a MQIPT instance between your client and the MQ server to control what ciphers are allowed from your client to MQIPT and from MQIPT to the MQ server. – JoshMc Nov 23 '20 at 10:32
  • JoshMc can you tell me where the ordered list of ciphers on server side is located? – Tom Ash Nov 23 '20 at 11:18
  • For MQ 9.1, you can find the list here: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.dev.doc/q113220_.htm – Roger Nov 23 '20 at 17:34
  • Only at 9.2 did IBM add the ability to influence the server side cipher order and list. If you want to see what order is returned then try http://testssl.sh, it is a simple shell script that uses openssl to run tests against any ip and port, one result from the script is the server's cipher list and preference order. I noted the IBM 9.2 knowledge center page [CipherSpec order in TLS handshake](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.sec.doc/q134760_.htm) also lists the pre-9.2 order. Please @ tag if you want me to get notified of your reply. – JoshMc Nov 24 '20 at 06:44

1 Answers1

1

I had the same issue when connecting with .NET MQ client to a channel using TLS_RSA_WITH_AES_256_CBC_SHA256 cipher.

In the MQ server logs I saw that my client tried to connect with TLS_RSA_WITH_AES_256_GCM_SHA384 even if I was setting the cipher spec to TLS_RSA_WITH_AES_256_CBC_SHA256 in my code.

cf.SetStringProperty(XMSC.WMQ_SSL_CIPHER_SPEC, “TLS_RSA_WITH_AES_256_CBC_SHA256”);

To solve it I had to set the system default cipher in the file /etc/ssl/openssl.cnf to AES256-SHA256.

[system_default_sect]
…
CipherString = AES256-SHA256

My application is built on .NET 6.