65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Microsoft Visual C++ 环境中编译和集成 Crypto++

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (45投票s)

2006 年 11 月 15 日

CPOL

16分钟阅读

viewsIcon

541039

downloadIcon

990

使用 Crypto++ 和 VC++ 时避免常见陷阱。

引言

Crypto++ 用户组》偶尔会收到关于 Crypto++ 和 Microsoft 环境的问题。这些问题通常很简单,例如:“VC++ 找不到头文件 - 我该怎么办(在哪里)……”或者更模糊的说法:“我无法编译。”本文将尝试解决这些常见问题并提供基本背景信息。

源代码

您可以从《Crypto++ 主页》下载 Crypto++ 源代码。如果网站暂时无法访问,请使用《SourceForge 页面》,或者使用归档服务,例如 archive.org 的《此处》。

文档

Crypto++ 有三种文档来源:源代码、Wiki、用户组。Crypto++ Wiki 可以在《此处》找到。Wiki 包含许多主题和大量示例代码。Crypto++ 用户组可以在 Google Groups 上找到,地址是《http://groups.google.com/group/cryptopp-users》。

源代码的在线版本可以在《Crypto++ 库参考手册》和《Crypto++ 类索引》中找到。Dennis Bider 撰写了一份用户指南和帮助文件,可以在《此处》找到。Bider 的帮助指南有些过时,但许多概念仍然适用。

基准测试

Crypto++ 库通过测试套件中的基准测试功能,使用 `cryptest b` 命令行进行基准测试。此外,基准测试还可以接受参数,例如每个测试的边界时间和 CPU 速度。有关详细信息,请参阅 *usage.dat* 文件,或直接运行 `cryptest` 命令。

网上至少有三份关于基准测试结果的公开报告。请参阅《Crypto++ 5.5 基准测试》、《开源加密库和编译器标志的速度测试和比较》以及《eBACS: ECRYPT 加密系统基准测试》。

Windows Mobile

Ugo Chirico 维护了一个 Crypto++ 5.5.2 到 Windows Mobile 2005 的移植版本,地址是《Crypto++ 5.5.2 在 Windows Mobile 2005 上》。Chirico 博士会在 Crypto++ 用户组发布公告。他的初步帖子可以在《Crypto++ 用户》中找到。

此外,Crypto++ Wiki 上现在也包含了一个 Windows Mobile 移植版本,地址是《http://www.cryptopp.com/wiki/Windows_mobile》。

iOS

Crypto++ 的 iOS 移植版本可以在 Wiki 上找到,地址是《http://www.cryptopp.com/wiki/IOS》。Wiki 页面讨论了针对 iPhone、iPad 和 iPod Touch 的更改。根据《http://groups.google.com/group/cryptopp-users/browse_thread/thread/e1e2da3cf92c488》,Panama 自检的测试套件中存在一个对齐问题。

托管代码

使用托管代码时无需特殊步骤。请注意,如果我们使用 FIPS DLL,可能会发现某些功能缺失(请参阅下面的讨论)。有关一个写得很好的示例,请参阅 Mike Sinclair 在 CodeProject.com 上的文章《使用 .NET 2.0 加密服务并以托管 C++ 类库的形式封装 Crypto++ 进行 RSA 加密》。

FIPS 合规性

FIPS 140-2》认证意味着该库能够产生正确的结果并符合某些加密协议。协议包括在使用完密钥材料后将其内存清零。当我们提到 FIPS 合规 DLL 时,我们将指出 **FIPS DLL**。

必须以 Wei 分发的二进制形式使用 FIPS DLL(即使我们拥有源代码并且可以构建相同的二进制文件)。这意味着我们不能在构建 Crypto++ DLL 后声称它是 FIPS 合规的。

版本 5.5.2 是 Wei 计划提交测试和验证的最后一个库版本。详细信息可以在《此处》找到。

模块认证 FAQ 可以在《加密模块验证计划》下找到。

单线程与多线程

在所有情况下,Crypto++ 库都应使用多线程库进行构建。这是 Visual C++ 6.0 及之前版本的一个问题。有时,项目向导会输出一个单线程项目(/ML 和 /MLd)。

静态链接与动态链接

C 运行时库

集成 Crypto++ 时,Crypto++ 库必须使用与我们的 Visual Studio 项目相同的运行时库链接器设置。运行时库指的是 **C 运行时库**。标准的 Crypto++ 分发版使用静态链接运行时库(/MT 和 /MTd)。

由向导创建的默认 Visual Studio 项目将使用动态运行时链接。项目将指定 /MD 或 /MDd 链接器开关进行动态运行时链接,而 Crypto++ 库将从其构建中使用静态链接(/MT 或 /MTd)。这将导致大量 LINK2005 错误。要解决此问题,必须重新构建 Crypto++ 库。下表 1 总结了此讨论。

项目设置 项目链接开关 所需的运行时链接 需要执行的操作
静态运行时链接 /MT 或 /MTd 静态
动态运行时链接 /MD 或 /MDd 动态 重新构建 Crypto++ 库

重新构建 Crypto++ 库意味着在更改设置以编译和链接到运行时动态版本后进行重新构建。另请参阅下面的**不支持的配置**部分。

静态运行时链接

Crypto++ 库以静态链接库的形式分发。通常首选静态链接(而不是 DLL 或 FIPS DLL)。据 Wei Dai 所述

...链接 DLL 到 DLL 应该没有问题。但是使用静态库可以节省代码大小,我建议使用它,除非出于某种原因需要使用 DLL(例如 FIPS 合规性)。

创建静态链接的 Visual Studio 项目时,无需更改其他设置。

动态运行时链接

当动态链接到 C 运行时(/MD 或 /MDd)时,必须使用与主项目相同的链接设置重新构建 Crypto++ 库。打开 Crypto++ 项目,并更改 **Cryptlib** 和 **CryptDll** 的 Crypto++ 项目设置。应将设置更改为 /MD 或 /MDd。为了万无一失,还应为 **Cryptest** 更改设置(并重新运行验证测试:发出 `cryptest v` 命令)。

运行时库冲突与 Crypto++ DLL/FIPS DLL

运行时链接为软件作者带来了两种棘手的情况。第一种是希望与 C 运行时动态链接,同时使用 FIPS DLL。该问题 derives from 如下:动态 C 运行时链接由应用程序使用(/MD 或 /MDd),但分发的 FIPS DLL 是静态的(/MT 或 /MTd)。最佳解决方案是使用 `/NODEFAULTLIB` 开关,并希望不会出现运行时问题。这种情况将导致难以追踪的运行时错误和《晦涩的初始化失败》。请注意,使用动态运行时链接重新构建 DLL 是不可用的,因为我们使用的是 FIPS DLL。

第二种情况是使用其他第三方组件时产生的。这包括 MFC 和 ATL。假设我们需要使用 FIPS DLL。我们将项目设置更改为使用运行时的静态版本,并链接 FIPS DLL。接下来,我们添加仅提供 C 运行时动态链接的第三方组件。现在我们就面临前面描述的情况。首选的解决方法是联系第三方供应商,并获取一个使用与我们的项目和 Crypto++ 相同的运行时设置的库。

未解析的外部符号

当使用 DLL 或 FIPS DLL 时,新闻组会偶尔收到 LNK2001 和 LINK2019 报告,如下所示。这是因为 DLL 和 FIPS DLL 仅包含经过认证的算法。因此 DLL 导出了 AES,但不导出 DES(DES 在各种标准中已被弃用)。FIPS DLL 中也缺少支持类,例如 `Base64Encoder`。

Crypto.obj : error LNK2019: unresolved external symbol
"public: virtual void __thiscall CryptoPP::Base64Encoder::IsolatedInitialize
(class CryptoPP::NameValuePairs const &)"
(? IsolatedInitialize@Base64Encoder@CryptoPP@@UAEXABVNameValuePairs@2@@Z)
referenced in function "public: __thiscall CryptoPP::Base64Encoder::Base64Encoder
(class CryptoPP::BufferedTransformation *,bool,int)"
(??0Base64Encoder@CryptoPP@@QAE@PAVBufferedTransformation@1@_NH@Z)

dlltest.obj : error LNK2001: unresolved external symbol "__declspec(dllimport)
public: __thiscall CryptoPP::DH_Domain<class 
CryptoPP::DL_GroupParameters_GFP_DefaultSafePrime, struct 
CryptoPP::EnumToType<enum CryptoPP::CofactorMultiplicationOption,0>
...
DLL_Debug/dlltest.exe : fatal error LNK1120: 1 unresolved externals

要解决此问题,首先请阅读 *readme.txt* 中的“MSVC 特定信息”。Wei 提供了《解决此问题的两种方法》。

你有两种方法可以处理这个问题:一种是修改 Crypto++ 以导出这些类,使用 `CRYPTOPP_DLL` 宏;另一种是链接 DLL 导出库和一个包含非 DLL 类和函数的静态库。后者可以通过使用 cryptlib 项目的“DLL-Import”配置来构建。

请注意,在 Wei 的第一种解决方案中,我们正在重新构建 DLL(不是 FIPS DLL),以便使用 `CRYPTO_DLL` 宏导出我们需要的其他函数。我们可能还必须将源文件添加到解决方案中的 cryptdll 项目。在第二种方法中,我们使用的是 FIPS DLL,并用 Crypto++ 的静态库中的功能对其进行补充,因为 FIPS DLL 不包含 Crypto++ 的所有必要功能。

Crypto++ DLL - New 和 Delete

有时,我们可能需要强制 Crypto++ 使用另一个库中的 new 和 delete,而不是标准的运行时。为此,我们将调用 `SetNewAndDeleteFunctionPointers`。有关 Wei 如何设置标准运行时函数指针的示例,请参阅 *dll.cpp*。

还要注意,这通常需要在初始化过程的早期完成。这会带来与 Crypto++ 无关的其他问题,例如链接库的顺序错误。请参阅知识库文章 148652,《在 Visual C++ 中,当 CRT 库和 MFC 库链接顺序错误时,会发生 LNK2005 错误》。

编译 Crypto++

访问《Crypto++ 主页》并下载一个版本。本文基于版本 5.2.1,但任何版本都可以。选择 Crypto++ 发行版时,应查看“平台”矩阵。使用 Visual Studio 6.0 时,对于 Crypto++ 5.1 及更早版本,建议使用《Microsoft 处理器包》;对于 Crypto++ 5.2 及更高版本,则必需使用。

在硬盘上创建一个目录来存放下载的 Crypto++ 发行版。找到 *cryptest.dsw* 或 *cryptest.sln* 文件并打开它。在此示例中,Zip 文件已解压到 *C:\CryptoPP 5.2.1\*。

默认情况下,会构建 32 位二进制文件。要构建 64 位二进制文件,请使用下拉菜单或配置管理器更改平台。

从“构建”菜单中,选择“批量生成”。

为本文的目的,只构建静态库,方法是取消选中相应的选项。

Sample screenshot

将 Crypto++ 集成到 Visual C++ 环境中

本文档将把库添加到 Visual Studio 环境,而不是项目环境。这意味着设置会影响每个项目,而不仅仅是 Crypto++ 项目。有关不这样做的原因,请参阅 John Deters 的《评论》。这是一篇精心制作且解释得当的评论,应该会很有用。构建完成后,进入 *C:\CryptoPP 5.2.1\Debug\* 文件夹。将库文件从 *cryptlib.lib* 重命名为 *cryptlib**d**.lib*(注意添加了表示 Debug 的“d”)。

将 Debug 库移动到头文件和源文件所在的目录。在此示例中,位置是 *C:\CryptoPP 5.2.1\*。同时,将 Release 版本移动到同一文件夹(不重命名)。

Visual C++ 集成

将头文件、源文件和库的路径添加到 VC++ 环境。选择“工具”|“选项”,然后选择“目录”选项卡。在下拉菜单中为前面提到的项目进行相应的条目。

项目集成

将头文件、源文件和库的路径添加到 VC++ 项目。

此步骤将允许我们使用 `#include "ecp.h"`,而不是包含 *ecp.h* 及其所有依赖项,并指定完整或相对路径。另外,由于库已在路径中(并命名正确),我们现在可以发出以下 `#pragma` 命令让链接器包含该库。

// Crypto++ Library
#ifdef _DEBUG
#  pragma comment ( lib, "cryptlibd" )
#else
#  pragma comment ( lib, "cryptlib" )
#endif

其他(不受支持的)配置

如果您感兴趣,存在一个项目配置,允许使用静态和动态运行时链接构建 Crypto++。但是,不要期望 Wei 支持它。作者们在使用 Visual Studio 8.0 和 Visual Studio 9.0 时已经使用了相当长一段时间。

不幸的是,IDE 已移除 x64 构建设置,因为在安装过程中未安装 x64 编译器和头文件。幸运的是,可以使用配置管理器轻松地将它们重新添加到项目中。

备用配置将 **cryptlib** 项目重命名为 **cryptlib.static**。它还添加了 **cryptlib.dynamic**(设置从 cryptlib.static 复制)。静态和动态项目都构建调试和发行版库。“dynamic”和“static”的选择而不是“d”和“s”后缀,是为了避免调试构建与动态 C 运行时链接构建混淆。配置提供的文件包括:

  • cryptlib.static.lib
  • cryptlibd.static.lib
  • cryptlib.dynamic.lib
  • cryptlibd.dynamic.lib

要使用该配置,请将文件解压缩到 Crypto++ 文件之上,进行必要的替换。对于关心库大小(在链接器移除未使用函数之前)的人来说,动态运行时链接会产生明显更小的代码。

构建完这四种变体后,使用 *copylib.cmd* 将库复制到 Crypto++ 发行版的根目录。最后,在项目中包含 *CryptlibInclude.h*。*CryptlibInclude.h* 会根据 `_DLL`(使用动态运行时链接时定义)和 `DEBUG` 进行切换。

另外请注意,静态和动态库都使用 *dll.cpp*,即使我们没有构建 DLL。如果我们不使用 *dll.cpp*,我们将收到 LNK2001 错误,声称一个函数(使用 `_thiscall`)找不到,尽管我们在源文件的属性中指定了 `_cdecl` 进行编译。下面,《VerifyHash》(一个需要动态运行时链接的 ATL 项目)在使用 cryptlib.dynamic 时未使用 *dll.cpp*。

Missing Adhoc.cpp

我们收到 20 个错误(10 对),如下所示。第一个错误声称程序因 *VerifyHash.obj* 中缺少函数而无法链接。第二个错误(指同一个函数)声称在 Crypto++ 库中找不到该函数。

VerifyHash.obj : error LNK2001: unresolved external symbol 
  "public: virtual void __thiscall CryptoPP::IteratedHashBase<unsigned int,class 
  CryptoPP::HashTransformation>::Update(unsigned char const *,unsigned int)" 
  (?Update@?$IteratedHashBase@IVHashTransformation@CryptoPP@@@CryptoPP@@UAEXPBEI@Z)

cryptlibd.dynamic.lib(md5.obj) : error LNK2001: unresolved external symbol 
   "public: virtual void __thiscall CryptoPP::IteratedHashBase<unsigned int,class 
   CryptoPP::HashTransformation>::Update(unsigned char const *,unsigned int)" 
   (?Update@?$IteratedHashBase@IVHashTransformation@CryptoPP@@@CryptoPP@@UAEXPBEI@Z)

我无法解释为什么 *iterbase.h* 和 *iterbase.cpp* 中的函数会受到包含或排除 *dll.cpp* 的影响。

常见错误

本节将讨论使用 Crypto++ 库时遇到的常见编译器和链接器错误。这些错误通常是由于疏忽未能遵循这些说明而引起的。在您确认已遵循这些说明后,请将您的问题发布到《Crypto++ 用户组》。

编译错误 1001

如果您收到内部编译错误 COMPILE 1001 (C1001),如下所示:

您应该执行以下操作:

  • 验证您的源代码是否正确
  • 验证 *config.h*(见下文)
  • 重新排列函数(注意:CL 错误将输出编译器遇到问题的源代码行 - 见下文)
  • 禁用预编译头文件
  • 更新库
  • 更新编译器
  • 发布到《Crypto++ 新闻组
  • 联系 Microsoft 支持

验证 config.h

根据 Visual Studio 的版本,会调用一些编译时更改。一个需要特别注意的怀疑对象是 `_MSC_VER < 1300`。如果使用 Visual Studio 7.1 或更高版本,将其更改为以下内容可能有效。

// how to declare class constants
#if defined(_MSC_VER) && _MSC_VER <= 1300
#  define CRYPTOPP_CONSTANT(x) enum {x};

#else
#  define CRYPTOPP_CONSTANT(x) static const int x;
#endif

重新排列函数

Function A
   { ... }
Function B
   { ... }

将以上更改为:

Function B
   { ... }
Function A
   { ... }

我无法解释这一点,但过去在使用 Crypto++ 椭圆曲线函数时,它对我有效。

编译错误 1083

如果您没有将 Crypto++ 头文件的路径添加到 Visual C++ 环境或您的项目中,您将看到类似如下的 COMPILE 1083 (C1083) 错误。

编译错误 1189

如果您错误地匹配了 C 运行时与 MFC(例如,MFC 作为 DLL,Crypto++ 和 C 运行时作为静态库),您将看到类似如下的 COMPILE 1189 (C1189) 错误。

您应该重新审视“静态链接与动态链接”部分。MFC 使用动态运行时链接。

编译警告 4530

如果您收到编译警告 COMPILE 4530 (C4530)……

……则应启用异常处理。

链接错误 1103

如果您在使用 Visual C++ 6.0 和最新的 Platform SDK(如下所示)以及库 *uuid.lib*、*shell32.lib*、*msxml2.lib* 等时收到 LINK 1103(“调试信息已损坏”)……

……则应回滚 Platform SDK 到 2003 年 2 月版本。您可能可以更改 Visual C++ 6.0 中的目录顺序。

链接错误 1104

如果您未安装 MFC Unicode 库版本(*mfc42u.lib*、*mfc42ud.lib* 等),但定义了 UNICODE,则会出现 LINK 1104 (LNK1104) 错误,显示类似如下的错误。这是 Visual C++ 6.0 及更早版本的一个错误,因为 Visual Studio 7.0 及更高版本不允许非 Unicode 安装。

要解决此问题,请重新运行安装程序并安装 Unicode 库(执行自定义安装)。

链接错误 1104

如果您未将 Crypto++ 库放置在 VC++ IDE 路径中,则会出现类似如下的 LINK 1104 (LNK1104) 错误。

链接错误 2001

如果您未将 Crypto++ 库链接到项目中,您将看到类似如下的 LINK 2001 (LNK2001) 错误。

要解决此问题,请将以下内容添加到 *StdAfx.h*(如果使用预编译头文件)或使用 Crypto++ 的源文件中。

// Crypto++ Library
#ifdef _DEBUG
#  pragma comment ( lib, "cryptlibd" )
#else
#  pragma comment ( lib, "cryptlib" )
#endif

或者,可以通过 Visual C++ IDE 添加 Crypto++ 库。

链接错误 2001

Crypto++ 的输出库位于 *Win32\output\debug\cryptlib.lib* 和 *Win32\output\release\cryptlib.lib*。DLL 及其导出库位于 *Win32\dll_output*。注意路径名称中的“dll_”。请验证您使用的是正确的路径。

链接错误 2005

如果您没有更改项目上的 C 运行时库设置,您将看到类似如下的 LINK 2005 (LNK2005) 错误。

您应该使用 C++ 运行时库的多线程版本。

使用 ATL 时发生链接错误 2005

如果您在使用 ATL 时遇到 LNK2005,请使用运行时库的多线程 DLL 和多线程调试 DLL 选项重新编译 Crypto++ 库。这在 Visual C++ 6.0 中不是问题,但在 Visual Studio 2005 中是问题。

使用 FIPS DLL 时发生链接错误 2019

此错误是由于 FIPS DLL 只导出认证算法(如 AES)所致。Wei 提供了《解决此问题的两种方法》。首先,您可以静态链接该库。其次,您必须使用该库的 DLL 版本(而不是 FIPS DLL)。然后,要导出缺失的感兴趣的类,请将 `CRYPTOPP_DLL` 宏添加到类声明中。然后重新构建 DLL-Import 项目。

Integer 类的 std::cout 格式化

在分发时,`Integer` 类会显示一个后缀('o'、'.' 或 'h')来表示基数。此外,库在所有十六进制输出中使用大写字符。要更改行为,使 `Integer` 不使用后缀并遵守 `ios::uppercase` 标志,请执行以下操作。首先,删除 `suffix` 的声明并更改 `switch` 语句。

switch(f)
{
  case std::ios::oct :
    base = 8;
    block = 8;
    break;
  case std::ios::hex :
    base = 16;
    block = 4;
    break;
  default :
    base = 10;
    block = 3;
}

接下来,添加以下内容:

const char* vec = NULL;
const char upper[]="0123456789ABCDEF";
const char lower[]="0123456789abcdef";

if( out.flags() & (long)std::ios::uppercase )
{ vec = upper; }
else
{ vec = lower; }

...

// No longer can return out << suffix;
return out;

Crypto++ Integer

修订

  • 2009.12.01 添加基准测试链接
  • 2008.08.07 添加 NIST 认证 FAQ 链接
  • 2008.05.27 添加 *Adhoc.cpp* 信息
  • 2008.05.27 添加 Chirico 博士的 Windows CE 移植版
  • 2008.05.25 移除 HashFilter `AlgorithmName()` 修复
  • 2008.05.25 添加备用配置(静态和动态 C 运行时项目)
  • 2008.04.20 添加备用网站和下载链接
  • 2008.03.21 添加托管代码部分
  • 2008.02.22 添加链接错误 2019 (LNK2019)
  • 2008.02.22 添加关于 `CRYPTOPP_DLL` 宏的信息
  • 2007.11.29 添加单线程与多线程部分
  • 2007.11.28 添加 FIPS 合规性部分
  • 2007.11.28 重写静态与动态链接部分
  • 2007.11.28 移除示例项目
  • 2007.06.04 在使用 ATL 时添加链接错误 2005 (LNK2005)
  • 2006.12.31 添加 Crypto++ Wiki 站点链接
  • 2006.12.27 更新 Crypto++ 类和库参考(Crypto++ 5.4 发布)
  • 2006.12.23 为内部编译器错误 (C1001) 添加其他修复
  • 2006.12.22 添加 Chris Deter 的评论链接
  • 2006.12.22 添加启动项目信息
  • 2006.12.17 添加链接错误 1103 (LNK1103)
  • 2006.12.15 更新文章图形
  • 2006.12.13 添加 HashFilter `AlgorithName()`
  • 2006.12.13 添加 FAQ、用户指南和 Crypto++ 用户新闻组
  • 2006.12.12 添加编译警告 4530
  • 2006.11.29 添加 VC7 目录对话框
  • 2006.12.09 添加 Unicode 库安装
  • 2006.12.09 添加其他内部编译器错误修复 (C1001)
  • 2006.11.21 添加内部编译器错误 (C1001)
  • 2006.11.16 添加 Integer `std::cout` 格式化
  • 2006.11.15 添加静态与动态 Crypto++ 链接
  • 2006.11.14 初始发布
© . All rights reserved.