【问题标题】:How to Call Schannel Functions from .Net/C#如何从 .Net/C# 调用 Schannel 函数
【发布时间】:2013-10-30 23:15:49
【问题描述】:

由于合规性原因(我想在 .Net 中使用 256 位 AES 和临时密钥),我正在尝试重新排序/删除密码套件。但是,使用 WCF TCP 传输安全性,我将所有对安全性的控制权交给了 Windows 的 TLS 实现及其首选密码。我不想更改系统级密码。

我发现这个great Microsoft page 列出了如何使用BCryptEnumContextFunctionsBCryptAddContextFunctionBCryptRemoveContextFunction 在应用程序级别执行此操作,但这全是 C,我什至没有足够的 P/Invoke 经验知道从哪里开始。我用谷歌搜索,但没有发现有人这样做。

MSDN 页面上的 C++ 代码 sn-ps 如下,我需要帮助将它们转换为 .Net P/Invoke 调用:

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);
}

有人可以帮我将这些转换为 .Net,以便我可以从托管代码中调用它们来调整密码吗?谢谢!

编辑:

昨晚晚些时候,我在一个测试程序中尝试了以下内容(仍然不知道我在 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);

到目前为止,我得到的唯一方法的输出是:

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

【问题讨论】:

  • 您能展示一下到目前为止您尝试过的内容吗?
  • @DavidHeffernan 除了我从 PInvoke.net 复制的内容之外,我对 P/Invoke 真的不太了解,但也就是说,我昨晚确实尝试过,我添加到质疑我对检索功能的尝试。不用说,它不起作用。

标签: c# .net ssl pinvoke schannel


【解决方案1】:

这是一个文档记录不佳的库。比如CRYPT_CONTEXT_FUNCTION_PROVIDERS的声明其实是:

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

这是直接从bcrypt.h提取的。

不管怎样,这里有一份 C++ 代码翻译给你:

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();
        }
    }
}

在我的机器上输出是:

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

我敦促您掌握一些简单的 C++ 以取得进步。从 MSDN 中获取示例并编译它。在调试器下运行它并了解它。使用 Visual Studio 查找定义和声明。例如,Visual Studio 直接将我带到了NCRYPT_SCHANNEL_INTERFACE 的定义。

【讨论】:

  • 在弄清楚如何添加 bcrypt.lib 之后,我确实让它在 VS 中以 C++ 模式编译并找到了结构,但我不知道如何将 IntPtr 转换为结构,因为我得到了 Status==0 和一个非 void 结构。我收到pStr += IntPtr.Size 的错误:“运算符'+=' 不能应用于'System.IntPtr' 和'int' 类型的操作数”。另外,您能否转换问题中的另外两个:BCryptAddContextFunctionBCryptRemoveContextFunction?我可能可以自己做这些,因为它们的签名相似,但没有返回结构,而是“用于谷歌”。 :)
  • 我也尝试使用/clr:safe 进行编译,但是我遇到了很多关于不兼容开关的错误,并且我无法获得一个可以使用它的调用方法。 /clr 有效,但是它生成的代码,当我在 DotPeek 中查看它时,它周围只有 [NativeCppMethodAttribute] 并且没有实际的 P/Invoke 代码。
  • 我相信你可以自己做这两个。它们更容易,不需要编组技巧。参数都是普通的stringuintCharset.Unicode 你是金子。我不知道/clr:safe 和你的第二条评论。
  • 你看到关于错误的部分了吗(所以不会让我在评论中有换行符)? pStr += IntPtr.Size 不会为我编译。我能够使它像pStr = new IntPtr(pStr.ToInt64() + IntPtr.Size); 一样工作,但我不确定这是否是最好的方法。
  • 哦,还有一件事。不要使用SetLastError = true。这些函数不会设置 Win32 错误。
猜你喜欢
  • 2011-04-25
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
相关资源
最近更新 更多