在 C# 工程中使用 OPENSSL
C# 与 C++/CLI 的结合
建议读者范围:有一定的 C#2.0 和 C 或者 C++ 的语言基础,并且对 .Net 互操作性有一定的了解
源程序下载地址: http://files.cnblogs.com/sleepingwit/Certificate.rar
总述
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 的不会出错。
附录
[1] OPENSSL 的官方网站是 http://www.openssl.org/ ,如果读者想要进一步了解 OPENSSL 可以去访问,不过上面的资料较少, http://www.infosecurity.org.cn/forum/index.php 中国信息安全组织论坛上面有很多不错的资料,推荐给感兴趣的读者。笔者的项目中只用到了 OPENSSL 中很小的一个功能,冰山一角不足以窥全貌,有不足之处,望大家海涵。
[2] .Net 互操作性的相关资料可以参考 MSDN, 笔者的资料来源于李建忠老师的 C++/CLI 视频讲座—— Visual C++ 2005 系列教程 参考课程: VC++ 2005(9):非托管互操作
[3] 由于博客园的文件大小限制,笔者无法提供编译好的 LIB 文件( OPENSSL ),不过所有的代码都在附件里面,笔者使用的是 Visual Studio 2005 SP1 编译 C++ 和 C# 。