Cipher suite selection in SChannel

1.3k Views Asked by At

I have implemented (Windows 10.0.17763.0/VS2017/C++) a client/server app which does secure communication using schannel. Now the requirement is to use only a set of cipher suites for the communication between certain clients and servers.

Using BCryptAddContextFunction/BCryptRemoveContextFunction APIs i can change the supported ciphers in SChannel but that is a system wide setting and not just for my application. In order to control it programmatically, i tried using the ALG_IDs in AcquireCredentialsHandle. Below are the only cipher suites my app is supposed to support.

  • TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

So i constructed the ALG_IDs like below.

std::vector<ALG_ID> algos       = { CALG_AES_256 , CALG_AES_128 , CALG_SHA_384 , CALG_SHA_256,CALG_ECDH_EPHEM,CALG_DH_EPHEM };
schannelCred.cSupportedAlgs     = static_cast<DWORD>(algos.size());
schannelCred.palgSupportedAlgs  = &algos[0];

Using wireshark i figured that below are the cipher suites my app is proposing in the client hello using the above ALG_IDs,

  • 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_DHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_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

The problem is,

  1. How do i allow only AES_GCM bulk encryption ? when i add CALG_AES to the list, both AES_GCM and AES_CBC are getting allowed.
  2. How do i control the signature ? Adding CALG_ECDH_EPHEM enabled both TLS_ECDHE_ECDSA as well as TLS_ECDHE_RSA where i need only TLS_ECDHE_RSA. adding CALG_RSA_SIGN to the ALG_ID removed TLS_ECDHE_ECDSA but it stared allowing TLS_RSA_* cipher suites.
2

There are 2 best solutions below

1
On

Use CNG functions: Examples here https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/SecAuthN/prioritizing-schannel-cipher-suites.md

Also, "When negotiating a cipher suite, the client sends a handshake message with a list of cipher suites it will accept. The server chooses from the list and sends a handshake message back indicating which cipher suite it will accept. Although the client may order the list with what it considers to be the strongest cipher suites listed first, the server may ignore the preference order and choose any of the cipher suites proposed by the client. The server may have its own cipher suite preference order, and it may be different from the client’s. Therefore, there is no guarantee that the negotiation will settle on the strongest common suite. If no cipher suites are common to the client and server, the connection is aborted." -

NIST Special Publication 800-52 Revision 2 Guidelines for the Selection, Configuration, and Use of Transport Layer Security (TLS) Implementations, Section 3.3.1 Cipher Suites. Kerry A. McKay David A. Cooper Computer Security Division Information Technology Laboratory This publication is available free of charge from: https://doi.org/10.6028/NIST.SP.800-52r2

0
On

You can use the SCH_CREDENTIALS structure instead of the SCHANNEL_CRED when calling AcquireCredentialsHandle function.

You create TLS_PARAMETERS with an array of CRYPTO_SETTINGS structures. Each CRYPTO_SETTINGS structure define restrictions for key exchange, signature, digest, and bulk cipher algorithms . Then you can select a combination that satisfies your application requirements. This structure (SCH_CREDENTIALS) work with the SCHANNEL_USE_BLACKLIST flag /approach, disabling what you do not need for the specified TLS session. With the right combination of eAlgorithmUsage and strCngAlgId members of the CRYPTO_SETTINGS structures I think you achieve the desired behavior of the TLS session.

See https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-sch_credentials

https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-tls_parameters

For example, If you include AES_CBC in one of the restriction with the appropriate eAlgorithmUsage member value, in this case TlsParametersCngAlgUsageCipher, it should be excluded from the session as bulk encryption algorithm. Make sure to exclude AES_GCM from the restrictions with that eAlgorithmUsage.

Regarding controlling signature algorithms, the CRYPTO_SETTINGS structure eAlgorithmUsage member with the value of TlsParametersCngAlgUsageSignature restrict the use of the algorithm assigned to the strCngAlgId member as a signature algorithms for that session. You can also determine the minimum and maximum lengths to restrict.

Finally,

"The DisabledByDefault value in the registry keys under the Protocols key does not take precedence over the grbitEnabledProtocols value that is defined in the SCHANNEL_CRED structure that contains the data for an Schannel credential." (https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/restrict-cryptographic-algorithms-protocols-schannel).

That is also to be true on the grbitDisabledProtocols member of the TLS_PARAMETER structure for that TLS session.

If you elaborate the right combination of restrictions for signature algorithms, key exchange, bulk encryption, and digest you can filter the undesired algorithms out of a specific position in the cipher suite.