Question

I am trying to re-order/remove cipher suites due to compliance reasons (I want to use 256 bit AES and ephemeral keys) in .Net. However, using WCF TCP Transport Security, I cede all control over the security to Windows' TLS implementation and its preferred ciphers. I don't want to change the system-level ciphers.

I found this great Microsoft page that lists how to do it at the application level using BCryptEnumContextFunctions, BCryptAddContextFunction, BCryptRemoveContextFunction, but it's all C and I don't have enough P/Invoke experience to even know where to begin. I googled but didn't find anyone doing it.

The C++ code snippets on the MSDN page are below, and I need help converting them to .Net P/Invoke calls:

BCryptEnumContextFunctions

#include <stdio.h>
#include <windows.h>
#include <bcrypt.h>


void main()
{

   HRESULT Status = ERROR_SUCCESS;
   DWORD   cbBuffer = 0;
   PCRYPT_CONTEXT_FUNCTIONS pBuffer = NULL;

    Status = BCryptEnumContextFunctions(
        CRYPT_LOCAL,
        L"SSL",
        NCRYPT_SCHANNEL_INTERFACE,
        &cbBuffer,
        &pBuffer);
    if(FAILED(Status))
    {
        printf_s("\n**** Error 0x%x returned by BCryptEnumContextFunctions\n", Status);
        goto Cleanup;
    }

    if(pBuffer == NULL)
    {
        printf_s("\n**** Error pBuffer returned from BCryptEnumContextFunctions is null");
        goto Cleanup;
    }

    printf_s("\n\n Listing Cipher Suites ");
    for(UINT index = 0; index < pBuffer->cFunctions; ++index)
    {
        printf_s("\n%S", pBuffer->rgpszFunctions[index]);
    }

Cleanup:
    if (pBuffer != NULL)
    {
        BCryptFreeBuffer(pBuffer);
    }
}

BCryptAddContextFunction

#include <stdio.h>
#include <windows.h>
#include <bcrypt.h>


void main()
{

    SECURITY_STATUS Status = ERROR_SUCCESS;
    LPWSTR wszCipher = (L"RSA_EXPORT1024_DES_CBC_SHA");

    Status = BCryptAddContextFunction(
                CRYPT_LOCAL,
                L"SSL",
                NCRYPT_SCHANNEL_INTERFACE,
                wszCipher,
                CRYPT_PRIORITY_TOP);
}

BCryptRemoveContextFunction

#include <stdio.h>
#include <windows.h>
#include <bcrypt.h>


void main()
{

    SECURITY_STATUS Status = ERROR_SUCCESS;
      LPWSTR wszCipher = (L"TLS_RSA_WITH_RC4_128_SHA");

    Status = BCryptRemoveContextFunction(
                CRYPT_LOCAL,
                L"SSL",
                NCRYPT_SCHANNEL_INTERFACE,
                wszCipher);
}

Could someone please help me convert these to .Net so I can call them from managed code to adjust the ciphers? Thanks!

Edit:

Later last night, I tried the following in a test program (still have no idea what I'm doing in P/Invoke):

// I found this in bcrypt.h
const uint CRYPT_LOCAL = 0x00000001;
// I can't find this anywhere in Microsoft's headers in my SDK,
// but I found some random .c file with it in there. No idea
// what this constant actually is according to Microsoft
const uint NCRYPT_SCHANNEL_INTERFACE = 0x00010002;

public static void DoStuff()
{
    PCRYPT_CONTEXT_FUNCTIONS pBuffer = new PCRYPT_CONTEXT_FUNCTIONS();
    pBuffer.rgpszFunctions = String.Empty.PadRight(1500);
    uint cbBuffer = (uint)Marshal.SizeOf(typeof(PCRYPT_CONTEXT_FUNCTIONS));

    uint Status = BCryptEnumContextFunctions(
            CRYPT_LOCAL,
            "SSL",
            NCRYPT_SCHANNEL_INTERFACE,
            ref cbBuffer,
            ref pBuffer);

    Console.WriteLine(Status);
    Console.WriteLine(pBuffer);
    Console.WriteLine(cbBuffer);
    Console.WriteLine(pBuffer.cFunctions);
    Console.WriteLine(pBuffer.rgpszFunctions);
}
/*
    typedef struct _CRYPT_CONTEXT_FUNCTIONS {
        ULONG cFunctions;
        PWSTR rgpszFunctions;
    } CRYPT_CONTEXT_FUNCTIONS, *PCRYPT_CONTEXT_FUNCTIONS;
*/

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PCRYPT_CONTEXT_FUNCTIONS
{
    public uint cFunctions;
    public string rgpszFunctions;
}

/*
    NTSTATUS WINAPI BCryptEnumContextFunctions(
    ULONG dwTable,
    LPCWSTR pszContext,
    ULONG dwInterface,
    ULONG *pcbBuffer,
    PCRYPT_CONTEXT_FUNCTIONS *ppBuffer
    );
*/
[DllImport("Bcrypt.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint BCryptEnumContextFunctions(uint dwTable, string pszContext, uint dwInterface, ref uint pcbBuffer, ref PCRYPT_CONTEXT_FUNCTIONS ppBuffer);

The output right now of the only method I got even this far with is:

0
MyClass+PCRYPT_CONTEXT_FUNCTIONS
2400
8934576
[1500 spaces that I initialized rgpszFunctions with, not the cipher functions]
Was it helpful?

Solution

This is a poorly documented library. For instance, the declaration of CRYPT_CONTEXT_FUNCTION_PROVIDERS is in fact:

typedef struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS
{
    ULONG cProviders;
    PWSTR *rgpszProviders;
}
CRYPT_CONTEXT_FUNCTION_PROVIDERS, *PCRYPT_CONTEXT_FUNCTION_PROVIDERS;

This is lifted directly from bcrypt.h.

Anyway, here's a translation of the C++ code for you:

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
        static extern uint BCryptEnumContextFunctions(uint dwTable, string pszContext, uint dwInterface, ref uint pcbBuffer, ref IntPtr ppBuffer);

        [DllImport("Bcrypt.dll")]
        static extern void BCryptFreeBuffer(IntPtr pvBuffer);

        [DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
        static extern uint BCryptAddContextFunction(uint dwTable, string pszContext, uint dwInterface, string pszFunction, uint dwPosition);

        [DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
        static extern uint BCryptRemoveContextFunction(uint dwTable, string pszContext, uint dwInterface, string pszFunction);

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_CONTEXT_FUNCTIONS
        {
            public uint cFunctions;
            public IntPtr rgpszFunctions;
        }

        const uint CRYPT_LOCAL = 0x00000001;
        const uint NCRYPT_SCHANNEL_INTERFACE = 0x00010002;
        const uint CRYPT_PRIORITY_TOP = 0x00000000;
        const uint CRYPT_PRIORITY_BOTTOM = 0xFFFFFFFF;

        public static void DoStuff()
        {
            uint cbBuffer = 0;
            IntPtr ppBuffer = IntPtr.Zero;
            uint Status = BCryptEnumContextFunctions(
                    CRYPT_LOCAL,
                    "SSL",
                    NCRYPT_SCHANNEL_INTERFACE,
                    ref cbBuffer,
                    ref ppBuffer);
            if (Status == 0)
            {
                CRYPT_CONTEXT_FUNCTIONS functions = (CRYPT_CONTEXT_FUNCTIONS)Marshal.PtrToStructure(ppBuffer, typeof(CRYPT_CONTEXT_FUNCTIONS));
                Console.WriteLine(functions.cFunctions);
                IntPtr pStr = functions.rgpszFunctions;
                for (int i = 0; i < functions.cFunctions; i++)
                {
                    Console.WriteLine(Marshal.PtrToStringUni(Marshal.ReadIntPtr(pStr)));
                    pStr += IntPtr.Size;
                }
                BCryptFreeBuffer(ppBuffer);
            }
        }

        static void Main(string[] args)
        {
            DoStuff();
            Console.ReadLine();
        }
    }
}

On my machine the output is:

30
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_RC4_128_SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256_P256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256_P256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384_P384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384_P384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P384
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_RC4_128_MD5
SSL_CK_RC4_128_WITH_MD5
SSL_CK_DES_192_EDE3_CBC_WITH_MD5
TLS_RSA_WITH_NULL_SHA256
TLS_RSA_WITH_NULL_SHA

I urge you to get to grips with some simple C++ to make progress. Take the example from MSDN and compile it. Run it under the debugger and get to know it. Use Visual Studio to locate definitions and declarations. For instance, Visual Studio took me straight to the definition of NCRYPT_SCHANNEL_INTERFACE.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top