将 .NET SecureString 传递给 COM Interop
Pass .NET SecureString to COM Interop
如何手动将 BSTR 从 .NET 编组到 COM?
我正在尝试将存储在 SecureString
中的秘密从我的 .NET 应用程序传递到 COM 对象上的方法。我想避免将 SecureString
转换为 String
,因为这不是您应该使用 SecureString
的方式。然后,秘密将以明文形式保存在托管内存中,并在那里停留很长时间。相反,我试图以正确的方式™ 做到这一点:将其转换为非托管内存中的 BSTR,将其传递给 COM 方法,然后使用 Marshal.ZeroFreeBSTR
清除 BSTR,以便仅公开密码限时
TblImp.exe 为该 COM 对象生成的 .NET 接口期望秘密作为 String
传递,然后将其编组为 BSTR。这不是我想要的,因为这意味着我必须将我的秘密放在 String
中,所以在这个 Lean Method for Invoking 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 方法。它不会因错误而失败 - 它只是给出不同的结果。
问题:
- 我可以像这样直接将 BSTR 作为 IntPtr 传递吗?我做错了什么?
- 有没有办法调试编组过程以查看正在传递的值?
假设您在 .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);
}
如何手动将 BSTR 从 .NET 编组到 COM?
我正在尝试将存储在 SecureString
中的秘密从我的 .NET 应用程序传递到 COM 对象上的方法。我想避免将 SecureString
转换为 String
,因为这不是您应该使用 SecureString
的方式。然后,秘密将以明文形式保存在托管内存中,并在那里停留很长时间。相反,我试图以正确的方式™ 做到这一点:将其转换为非托管内存中的 BSTR,将其传递给 COM 方法,然后使用 Marshal.ZeroFreeBSTR
清除 BSTR,以便仅公开密码限时
TblImp.exe 为该 COM 对象生成的 .NET 接口期望秘密作为 String
传递,然后将其编组为 BSTR。这不是我想要的,因为这意味着我必须将我的秘密放在 String
中,所以在这个 Lean Method for Invoking 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 方法。它不会因错误而失败 - 它只是给出不同的结果。
问题:
- 我可以像这样直接将 BSTR 作为 IntPtr 传递吗?我做错了什么?
- 有没有办法调试编组过程以查看正在传递的值?
假设您在 .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);
}