在C#工程中使用OPENSSL
C#与C++/CLI的结合
建议读者范围:有一定的C#2.0 和C或者C++的语言基础,并且对.Net互操作性有一定的了解
总述
OPENSSL是一个开源的广为使用的开源信息安全SDK,然而该SDK的官方版本是用C/C++语言编写的,因此该SDK的程序不能直接在.Net的虚拟机上运行。笔者最近的一个项目需要生成PKCS#12(*.pfx)的数字证书,并且要提供PKCS#12证书到X509(*.cer)证书的转换功能.由于.NET的功能不够完善等诸多原因,笔者不能够很好的使用.Net Framework 2.0 来完成上述功能。OPENSSL可以实现笔者需要的功能,但是无法做到与我们的C#程序交互,经过多次试验,笔者最终采用了C#与C++/CLI结合的方式来实现上述功能——C#程序生成pfx证书并提供pfx到cer证书的转换。
C++/CLI
在.Net Framework 2.0中,微软将Manage C++升级为C++/CLI,我们且不关注该改进的优缺点,在这里我们只关心我们需要的功能。C++/CLI之所以能够满足我们的需要,是因为他提供了一个很好的源代码级的.Net与本机代码的互操作功能。.Net与本机互操作可以有三种方式[2]:
(1)基于P/Invoke的DLL互操作;
(2)COM互操作;
(3) C++ Interop.
第三种C++ Interop是C++/CLI独有的源代码级在操作,该机制允许我们在有源代码的情况下轻松的高性能的实现本机(Native)与托管(.Net)程序的交互。当然,尽管C++ Interop是三者中性能最高的,但是在没有源代码的情况下是无法使用这中机制的。对于笔者的这个项目,C++ Interop刚好能满足笔者的需要。
项目实例
Native C++ 使用OPENSSL 生成数字证书
笔者使用的OPENSSL版本为0.9.8 下载页面为http://www.openssl.org/source/
下载并编译好OPENSSL后导入我们的C++ 工程中,我们使用如下的代码来生成证书
具体的源代码请参考附件
Code
/***********************************************************************************************************
FileName: CertificateCLI.h
Author: Lu Wei
CreateDate:2008-07-19
Description: class defination of certificate implement
**********************************************************************************************************/
#pragma once
using namespace System;
using namespace System::Runtime::InteropServices;
namespace CertificateCLI {
// C++/CLI class implement certificate operation
static class CertificateNative
{
private:
CertificateNative(){}
private:
//Generate Priva key , in this project it used for generate CA private key
static int GeneratePrivateKey(BYTE *bpPriKey,int *ipPriKeyLen);
//Generate PKCS#12 certificate
static int CreateP12FromPrivateKey(BYTE *bpCAPriKey, int iCAPriKeyLen, int iValidDay, BYTE *bpP12, int *ipP12Len, char *pszP12Pwd,char* pszName, char *pszSubjectName, char *pszOrganizational,char *pszOrganizationName, char *pszEmail, char *pszCountryRegion );
//Export PKCS#12 information to X509 certificate
static int ParseP12Certificate(char *szP12Pwd,BYTE *bpP12,int iP12Len,BYTE *bpOutCert,int *ipCertLen);
public:
// mid-interface with .NET lanuguage and native C++
static int GenerateP12Certificate(char* strName,char* strOrganizational,char* strOrganizationName,char* strEmail,char* strCountryRegion,char* strPassword,char* strSavePath);
static int ExportX509Certificate(char* strSource,char* strSavePath,char* strPassword);
};
// .NET class that supply Interface used by .Net language
public ref class CertificateNet
{
private:
CertificateNet(){}
public:
static System::Int32 GenerateP12Certificate(System::String^ strName, System::String^ strOrganizational, System::String^ strOrganizationName, System::String^ strEmail, System::String^ strCountryRegion,
System::String^ strPassword, System::String^ strSavePath);
static System::Int32 ExportX509Certificate(System::String^ strSource,System::String^ strSavePath,System::String^ strPassword);
};
}
大家可以看到我使用了两个类来封装需要的功能,一个是native C++类,用来实现native C++与openssl的交互,另一个是C++/CLI类,用来实现Native C++到.Net Framework的交互。从下面这小段代码可以看出我是如何做的交互(CertificateCLI.cpp):
对了,就是这个static_cast<char*> 。实现了我们需要的功能,不过笔者的交互比较简单,如果需要更深入交互,可能带来更多的问题,比如内存泄露之类的,更深入的研究需要读者参考MSDN。现在我们来看看怎么在C#中使用它。
Code
System::Int32 CertificateNet::ExportX509Certificate(System::String ^strSource, System::String ^strSavePath, System::String ^strPassword)
{
IntPtr ipSource = Marshal::StringToHGlobalAnsi(strSource);
IntPtr ipSavePath = Marshal::StringToHGlobalAnsi(strSavePath);
IntPtr ipPassword = Marshal::StringToHGlobalAnsi(strPassword);
char *szSource = static_cast<char*>(ipSource.ToPointer());
char *szSavePath = static_cast<char*>(ipSavePath.ToPointer());
char *szPassword = static_cast<char*>(ipPassword.ToPointer());
System::Int32 status = CertificateNative::ExportX509Certificate(szSource,szSavePath,szPassword);
return status;
}
在C#中使用封装后的代码
.Net的公共语言接口(CLI)使得我们很容易能够用C#来调用上面的代码
Code
/// <summary>
/// Export PKCS#12 file to X509 certificate
/// </summary>
/// <param name="strSource">the pfx file path</param>
/// <param name="strSavePath">where to save exported certificate</param>
/// <param name="strPassword">password</param>
/// <returns></returns>
public static int ExportP12Certificate(string strSource, string strSavePath, string strPassword)
{
int status = CertificateNet.ExportX509Certificate(strSource, strSavePath, strPassword);
return status;
}
这段代码显示了我们是如何调用C++/CLI生成的DLL中的方法。至此,我们实现了在C#工程中使用OPENSSL。
结束
在笔者的项目中,只使用很小的一部分OPENSSL代码,因此维护起来较为容易,对于更复杂的代码,需要读者更加仔细。因为在Visual Studio 2005中,当你在写C#程序时,调式程序是不可以进入到C++/CLI的DLL的源代码中去的,从而使得使用笔者的方法来使用OPENSSL时必须尽可能更加严格的保证C++/CLI写的DLL的不会出错。