在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的不会出错。