将.NET SecureString传递给COM Interop

时间:2018-09-07 14:18:21

标签: .net com marshalling bstr securestring

您如何手动将BSTR从.NET封送至COM?

我试图将存储在SecureString中的密码从我的.NET应用程序传递到COM对象上的方法。我想避免将SecureString转换为String,因为那不是您应该使用SecureString的方式。然后,秘密将以明文形式保存在托管内存中,并在其中徘徊很长时间。相反,我尝试使用Right Way™:将其转换为非托管内存中的BSTR,将其传递给COM方法,然后使用Marshal.ZeroFreeBSTR清除BSTR,以便仅对时间有限。

TblImp.exe为此COM对象生成的.NET接口期望将机密作为String传递,然后将其封送给BSTR。这不是我想要的,因为这意味着我必须将自己的秘密保存在String中,因此在遵循此Lean Method for Invoking COM之后,我创建了自己的界面,以便可以对其进行自定义并摆脱{ {1}}。这就是我的开始:

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

...但是它不起作用:错误的值正在传递给COM方法。它不会因错误而失败-只会给出不同的结果。

问题:

  1. 我可以像这样通过IntPtr直接传递BSTR吗?我在做什么错了?
  2. 是否可以调试封送处理过程以查看传递了什么值?

1 个答案:

答案 0 :(得分:2)

假设您在.idl中有此内容:

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

.NET的tlbimp(如您所见)将变成这样:

[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参数,则不能直接使用它。 解决方案是重新定义C#中的COM接口,例如以下示例(请注意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);
}