Can anyone give example usage for using System.Security.Cryptography.RijndaelManaged
from native Win32 using COM?
Here is a functional example of how to use .NET's System.Security.Cryptography.SHA256Managed
Note: This is an example of how to call SHA256Managed, and not RijndaelManaged**. i show this code to introduce some of the tricky concepts of calling COM objects with early and late-binding, and to show that calling .NET objects with COM Callable Wrappers is possible. Some people might throw up that hands when hearing i want to mix .NET and Win32 COM.
Note: The language is Delphi (which is what i wrote and tested it in), but transcoded to make it look slightly more like C# or Java, so the question is more accessible to a wider audience:
public static string HashStr256(String szPlaintext)
{
OleVariant sha = CreateOleObject('System.Security.Cryptography.SHA256Managed');
OleVariant inputBuffer = VarByteArrayOf(szPlaintext);
Integer l = VarArrayLowBound(inputBuffer, 1);
Integer h = VarArrayHighBound(inputBuffer, 1);
ICryptoTransform crypto = IDispatch(sha) as ICryptoTransform;
crypto.TransformFinalBlock(
PSafeArray(TVarData(inputBuffer).VArray), //inputArray,
l, //input array offset
h-l+1); //input array count
OleVariant digest := sha.Hash;
Result := ToBase64String(BytesOfVarByteArray(digest));
end;
With the helper function:
function VarByteArrayOf(const s: string): OleVariant;
var
Data: Pointer;
begin
//Create variant byte array to hold the bytes
Result := VarArrayCreate([0, Length(s)-1], varByte);
//Copy from Delphi array to Variant Array
if Length(s) > 0 then
begin
Data := VarArrayLock(Result);
try
System.Move(s[1], Data^, Length(s));
finally
VarArrayUnlock(Result);
end;
end;
end;
The real reason i show code calling SHA256Managed
is to demonstrate the difficulties i'm encountering when passing arrays
to COM in Delphi. For example, if you simply relied on IDispatch
late binding:
sha: OleVariant;
inputBuffer: OleVariant;
...
sha.TransformFinalBlock(inputBuffer, i, count);
Then at runtime you get The parameter is incorrect; something about IDispatch
late binding doesn't support passing an OleVariant
that is an array of bytes.
That's why i have to instead pass the safearray
that is inside the OleVariant
. As we all know a Variant is just a union structure, where we care about the SAFEARRAY
member:
data: PSafeArray;
data := PSafeArray(TVarData(inputBuffer).VArray);
sha.TransformFinalBlock(data, i, count);
Except trying to pass a PSafeArray
using IDispatch
late-binding causes Delphi to throw-up:
Type not allowed in OLE Automation call
Which is why we're forced to partially abandon IDispatch
late binding for ICryptoTransform
early binding:
ICryptoTransform_Native = interface(IDispatch)
['{8ABAD867-F515-3CF6-BB62-5F0C88B3BB11}']
...
function TransformFinalBlock(inputBuffer: PSafeArray; inputOffset: Integer; inputCount: Integer): PSafeArray; safecall;
...
end;
with
crypto: ICryptoTransform;
sha: OleVariant;
inputBuffer: OleVariant;
crypto = IDispatch(sha) as ICryptoTransform;
crypto.TransformFinalBlock(
PSafeArray(TVarData(inputBuffer).VArray), //inputArray,
i, n);
So that should be it, right? We've solved how to pass arrays to COM .NET objects? i should be all set to use Rijndael
now; use early-binding with PSafeArray
.
This is what we want to try with late-binding:
OleVariant aes := CreateOleObject('System.Security.Cryptography.RijndaelManaged');
OleVariant keyBytes = VarByteArrayOf(Key);
aes.Key := key;
that fails for the same reason above - you cannot passed OleVariant
arrays to COM objects (The parameter is incorrect).
So we we should just use the early binding of SymmetricAlgorithm.Key
and pass the Key
as a PSafeArray
:
var
symmetric: ISymmetricAlgorithm;
aes: OleVariant;
keyBytes: OleVariant;
begin
aes := CreateOleObject('System.Security.Cryptography.RijndaelManaged');
symmetric:= (IDispatch(aes) as ISymmetricAlgorithm;
symmetric.Key := PSafeArray(TVarData(inputBuffer).VArray);
...
end;
Except there is no ISymmetricAlgorithm
interface; i made it up.
If you import the type library from mscorelib.tlb
, which contains ICryptoTransform
, there is no early binding ISymmetricAlgorithm
. There is a late-bound ISymmetricAlgorithm
:
IID__SymmetricAlgorithm: TGUID = '{05BC0E38-7136-3825-9E34-26C1CF2142C9}';
CLASS_SymmetricAlgorithm: TGUID = '{5B67EA6B-D85D-3F48-86D2-8581DB230C43}';
_SymmetricAlgorithm = interface;
SymmetricAlgorithm = _SymmetricAlgorithm;
_SymmetricAlgorithm = interface(IDispatch)
['{05BC0E38-7136-3825-9E34-26C1CF2142C9}']
end;
_SymmetricAlgorithmDisp = dispinterface
['{05BC0E38-7136-3825-9E34-26C1CF2142C9}']
end;
So how can i set the RijndaelManaged Key
and IV
from native Win32 COM? What late-binding syntax can i use?
How to call use .NET RijndaelManaged from native COM?
Further Attempts
Since there is no early-bound interface
i can use, i have to focus on how to pass a byte array through IDispatch
. The secret is what should be in the Variant
that i pass to the COM object.
Since the early-bound version used safearrays
, i'll start with that. First of all i will manually set OleVariant
structure myself.
For each of the following attempts the following is true:
key: OleVariant;
data: PSafeArray;
data := PSafeArray(TVarData(keyBytes).VArray);
setting the variant enum myself.
Attempt 1:
VT_ARRAY
A SAFEARRAY pointer.TVarData(key).VType := VT_ARRAY; TVarData(key).VArray := data;
Attempt 2:
VT_ARRAY
A SAFEARRAY pointer.TVarData(key).VType := VT_ARRAY or VT_I1; TVarData(key).VArray := data;
Attempt 3:
VT_SAFEARRAY
A safe array. Use VT_ARRAY in VARIANT.TVarData(key).VType := VT_SAFEARRAY or VT_I1; TVarData(key).VArray := data;
Attempt 4:
VT_SAFEARRAY
A safe array. Use VT_ARRAY in VARIANT.TVarData(key).VType := VT_SAFEARRAY; TVarData(key).VArray := data;
All of them fail with a The parameter is incorrect
error.
Get i get the sense that VT_ARRAY
(a safearray) and VT_SAFEARRAY
(a safearray) are just not what the COM Callable wrapper IDispatch interface is prepared to accept. Perhaps it needs to be VT_CARRAY
.
Or maybe not. Maybe someone smarter than me can figure it out.
Bonus Reading
This is, essentially, a re-phrasing of a question i asked in 2008 on the Borland Newsgroups
Delphi
tag. Also using Crypto API, or a 3rd party library wasn't my question. The question applies to any native code programmer - even assembly language. – TenchIV
as opposed toCrypto
. i need to be able to encrypt in one and decrypt in another, and vice versa. – Tench