How to call use .NET RijndaelManaged from native COM?
Asked Answered
T

0

2

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 compiler has built-in knowledge of COM SafeArrays?

Tench answered 15/8, 2012 at 16:2 Comment(13)
Would you move the answer to an answer and accept it? If you don't, then I'll have to close this as NARQ.Hygrothermograph
@Hygrothermograph i don't have an answer, that's why i'm asking the question /confused (Confusing failed research effort with an answer?)Tench
It's hard to see what the question is in what seems like an answer after the first horizontal rule.Hygrothermograph
@Hygrothermograph i was separating my question from the research effort and background information. Granted it is a lot of information, but interacting with COM from native code can get pretty verbose.Tench
@IanBoyd, why do you want use the .Net Cryptography classes, instead of the Native Windows CryptoAPI?Sandfly
@Sandfly a) it never occurred to me that there might be a native implementation, and b) i have no idea how to use it (whereas .NET version has documentation and code examples for AES/Rijndael)Tench
If not the native Windows Crypto API, then at least a native Rijndael algo in Delphi code, such as DCPcrypt. If you really want to do .Net interop from Delphi XE2 and later, you'd best buy RemObject's Hydra. Voted to close as not a real question, because I don't think anybody wants to a Delphi question and answer that's not coded in Delphi. Either tag it delphi, and write it in Delphi, or don't.Illsuited
@IanBoyd, If you want, I can post a sample using the JEDI API Library & Security Code Library :)Sandfly
@WarrenP You can back away from the ledge; i removed the 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.Tench
Though you got other means for your specific question, I like the general underlying question: how to use complex .NET functionality from the Win32/COM World. I hope other people can see through the specifics and try to answer the generic question. +1Sleep
@JeroenWiertPluimers i took the underlying problem and and re-asked just there here. Hopefully by hiding the example people can focus on the question. But make no mistake; i also want to call other COM objects (that are exposed through COM Callable Wrappers from .NET)Tench
@IanBoyd Great idea. I think this answer can be closed and pointed to the other question then. I upvoted the other one.Sleep
@JeroenWiertPluimers Unfortunately this is still the problem i need solved. While i have written the code using CrypoApi's AES, i don't know how RijndalManaged handles the IV as opposed to Crypto. i need to be able to encrypt in one and decrypt in another, and vice versa.Tench

© 2022 - 2024 — McMap. All rights reserved.