Передача .NET SecureString в COM-взаимодействие - PullRequest
0 голосов
/ 07 сентября 2018

Как вручную перенаправить BSTR из .NET в COM?

Я пытаюсь передать секрет, хранящийся в SecureString, из моего приложения .NET в метод COM-объекта. Я хочу избежать преобразования SecureString в String, потому что вы не должны использовать SecureString. Секрет тогда будет в открытом тексте в управляемой памяти и будет там висеть надолго. Вместо этого я пытаюсь сделать это Правильным способом & trade;: преобразовать его в BSTR в неуправляемой памяти, передать его методу COM, а затем очистить BSTR с помощью Marshal.ZeroFreeBSTR, чтобы пароль был открыт только в течение ограниченного времени. .

Интерфейс .NET, который генерирует TblImp.exe для этого COM-объекта, ожидает, что секрет будет передан как String, который затем маршалирует в BSTR. Это не то, что я хочу, так как это означает, что я должен поместить свой секрет в String, поэтому, следуя этому Lean-методу для вызова COM , я создал свой собственный интерфейс, чтобы я мог настроить его и получить избавиться от String. Вот с чего я начинаю:

[ComImport, Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITheComObjectDispatcher
{                                                     // +--- the naughty string
    [return: MarshalAs(UnmanagedType.BStr)]           // |
    [DispId(1)]                                       // V
    string TheMethod([In, MarshalAs(UnmanagedType.BStr)] string secret);
}

[ComImport, Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")]
public class TheComObject
{}

Это работает - но я должен преобразовать SecureString в String, чтобы использовать его, например, так:

private string UseTheMethod(SecureString secret)
{
    var comObject = new TheComObject();
    var comObjectDispatcher = (ITheComObjectDispatcher)comObject;
    var secretAsString = NaughtilyConvertSecureStringToString(secret);
    return comObjectDispatcher.TheMethod(secretAsString);
}

private static string NaughtilyConvertSecureStringToString(SecureString value)
{
    var valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
    try
    {
        return Marshal.PtrToStringUni(valuePtr); // now the secret is in managed memory :(
    }
    finally
    {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
    }
}

Вместо этого я хочу использовать метод, подобный Marshal.SecureStringToBSTR, и пропустить преобразование в String. Я пробовал это ...

    [return: MarshalAs(UnmanagedType.BStr)]
    [DispId(1)]
    string TheMethod([In, MarshalAs(UnmanagedType.BStr)] IntPtr secret);

... и это ...

    [return: MarshalAs(UnmanagedType.BStr)]
    [DispId(1)]
    string TheMethod([In] IntPtr secret);

... с этим кодом вызова ...

private string UseTheMethod(SecureString secret)
{
    var comObject = new TheComObject();
    var comObjectDispatcher = (ITheComObjectDispatcher)comObject;
    var secretPtr = Marshal.SecureStringToBSTR(secret);
    try
    {
        return comObjectDispatcher.TheMethod(secretPtr);
    }
    finally
    {
        Marshal.ZeroFreeBSTR(secretPtr);
    }
}

... но это не работает: неверное значение передается методу COM. Он не завершается с ошибкой - он просто дает другой результат.

Вопросы:

  1. Могу ли я передать BSTR напрямую как IntPtr, как это? Что я делаю не так?
  2. Есть ли способ отладки процесса маршалинга, чтобы увидеть, какое значение передается?

1 Ответ

0 голосов
/ 07 сентября 2018

Предположим, у вас есть это в .idl:

interface ITheComObjectDispatcher : IDispatch
{
    HRESULT TheMethod(BSTR secret, [out, retval] BSTR *pOut);
};

Это будет примерно так с tlbimp .NET (как вы видели):

[ComImport, TypeLibType((short) 0x10c0), Guid("D4089F1D-5D83-4D1C-92CD-5941B35D43AA")]
public interface ITheComObjectDispatcher
{
    [return: MarshalAs(UnmanagedType.BStr)]
    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60020000)]
    string TheMethod([MarshalAs(UnmanagedType.BStr)] string secret);
}

Таким образом, вы не можете использовать это напрямую, если не хотите использовать небезопасные string аргументы. Решение состоит в том, чтобы переопределить интерфейс COM в C #, например, так (обратите внимание на параметр InterfaceIsDual):

[InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("D4089F1D-5D83-4D1C-92CD-5941B35D43AA")]
public interface ITheComObjectDispatcher2
{
    [DispId(0x60020000)]
    IntPtr TheMethod(IntPtr secret);
}

И используйте это так:

var comObject = new TheComObject();
var comObjectDispatcher = (ITheComObjectDispatcher2)comObject;
var ptr = Marshal.SecureStringToBSTR(mySecureString);
try
{
    var outPtr = doc.TheMethod(ptr);

    // get the output string
    var output = Marshal.PtrToStringBSTR(outPtr);

    // free the callee-allocated BSTR
    Marshal.FreeBSTR(outPtr);
}
finally
{
    // free the secure string allocated BSTR
    Marshal.ZeroFreeBSTR(ptr);
}
...