In one of Jenerate’s recent projects, I had to work out how we would call some static libraries from a .NET web service.
There’s a number of hoops to jump through. Once they are done, it works well, but it took a bit of digging and learning. Hopefully this post will help others who have to do the same.
Firstly a bit of background. The project involved integrating to nCipher’s pretty impressive hardware security module. NCipher offer a number of ways of integrating into this including Java libraries and static libraries for Windows (and Unix, Linux, etc.), but unfortunately, no .NET API. Our customer had already mandated that we develop the particular piece of software using .NET, so our choices on how to achieve this integration were pretty much set for us.
There are a number of ways of integrating static libraries with .NET, but here’s how I went about this.
- Using the nCipher headers and libraries, I created a Windows DLL that exposed any features I needed at a high level. I.E. I did most of the difficult work in straight C, only exposing a small number of functions that provided the features I needed.
- Within this DLL, any data structures I required, I made global to the DLL, but returned a “Handle” to this to the caller (the caller stored this, and used in in subsequent calls to the DLL).
- In the C# world, I wrote a class which statically imported the DLL functions for calls by other C# code. For ease of access, I also wrapped the high level functions calls as public methods in my caller class. This class uses functions of the System.Runtime.Interop namespace.
- There was also a bit more work on marshalling structures between C# and the DLL, and also if I had to retrieve data from a pointer, but that is shown in the example below.
Some examples may make this clearer. I’ll not go into too much detail, but hopefully enough to explain the main concepts. For the purposes of my examples, I’ll have just two exposed functions. One to initialise the data structures in the DLL (we’ll call this init), and another to carry out an encryption; This is what I did, but I’ll leave out key management, etc. for the sake of clarity of the example.
The initialise method had the following signature in C:
__declspec(dllexport) PMYHANDLE __stdcall init(int *pRc, int p1, int p2);
where
PMYHANDLE is a pointer to a MYHANDLE structure (which we don’t need details on)
pRc is a pointer to an integer to receive a return code if something doesn’t work
p1 and p2 don’t matter as such.
The encrypt function had the following signature:
__declspec(dllexport) M_Status __stdcall simpleEncryptString(
MYHANDLE *pHandle, char *pszStringToEncrypt,
PCIPHERTEXT pCipherText);
Where
pHandle should be obtained from a prevoius init call
PCIPHERTEXT is a pointer to a structure to store the cipher text. This was defined as
typedef struct tagCipherText {
int numBytes;
char *pData;
} CIPHERTEXT, *PCIPHERTEXT;
The C# code that imports and uses this looks a bit like this:
StructLayout(LayoutKind.Sequential)]
// Copy the structure format from the C code
public struct CIPHERTEXT
{
public int numBytes;
public IntPtr pData;
}
[DllImport("mydll.dll")]
public static extern IntPtr init(ref int i, int p1, int p2);
[DllImport("mydll.dll")]
public static extern int simpleEncryptString(IntPtr pHandle,
string pszStringToEncrypt, ref CIPHERTEXT cipherText);
Points to note:
In init, we can pass the pRc by ref using “ref int i”. We do something similar for ref CIPHERTEXT in simpleEncryptString.
So, putting it together we have something like the following
public class EncryptCaller {
IntPtr m_handle;
public EncryptCaller(int p1, int p2) {
int rc;
m_handle = init(ref rc, p1, p2);
if (rc != 0) {
// Error handle here
}
}
public byte[] simpleEncrypt(string s) {
CIPHERTEXT ct = new CIPHERTEXT();
int rc;
byte[] ret;
rc = simpleEncryptString(m_handle, s, ref ct);
ret = new byte[ct.numBytes];
Marshal.Copy(ct.pData, ret, 0, ct.numBytes);
freeCipherText(ref ct);
}
}
And that is more or less it. There are two things worth noting from the example
- The data member within the CIPHERTEXT structure was malloc’d within the C code. So, there’s also a function exposed which issues the corresponding free. I didn’t show its definition,etc. here, but you see where it was called.
- Knowing that I would free this pointer, I copied the data from it into a local byte array. I used the Marshal class to do that. There’s plenty of online help from MSDN on this.