应用 Crypto++:使用 RSA 数字签名系统(第一部分)






4.97/5 (15投票s)
使用 Crypto++ 创建和验证 RSA 数字签名。
引言
RSA 数字签名是数字安全领域最常见的签名之一。RSA 是 Ron Rivest、Adi Shamir 和 Leonard Adleman 的工作成果。该系统于 1977 年开发,并由麻省理工学院获得专利。尽管 Rivest、Shamir 和 Adleman 通常被认为发现了该系统,但 Clifford Cocks(GCHQ 的首席数学家——英国的 NSA 对等机构)在 1973 年就描述了该系统。然而,Cocks 没有发表(该工作被视为机密),因此功劳归于 Rivest、Shamir 和 Adleman。
这个 RSA 数字签名示例是一个带附录的数字签名方案,这意味着必须将原始消息提供给 Verify 函数来执行验证。
这与带恢复的数字签名方案不同,在带恢复的数字签名方案中,原始消息会与签名连接或交错。带恢复的数字签名方案不需要原始消息进行验证,因为它包含在签名中。本示例的第二部分将重点介绍生成带恢复签名代码。
数字签名
数字签名是电子世界的书面签名的等价物。数字签名为密码学家提供以下功能:
请注意,MAC(消息认证码)虽然与数字签名相似,但不提供不可否认性,因为签名者和验证者使用相同的密钥。
编译和集成Crypto++

提供的示例使用了 Crypto++ RSA 算法。对于那些对其他 C++ 加密库感兴趣的人,请参阅 Peter Gutmann 的 Cryptlib 或 Victor Shoup 的 NTL。
Crypto++ 可从 Wei Dai 的 Crypto++ 页面下载。有关编译和集成问题,请参阅 在 Microsoft Visual C++ 环境中编译和集成 Crypto++。本文基于前面提到的文章中提出的基本假设。
签名过程
签名过程有点违反直觉。这是因为“公共结果”是从私钥而不是公钥派生的。“公共结果”就是数字签名。普通人通常将签名称为“用公钥加密”。然而,仅用公钥加密并不是一个有效的加密操作。
签名过程
当使用附录方案签署文档时,会发生两个步骤:
- 哈希文档
- 使用私钥解密文档的哈希值,就好像它是一个密文实例一样
在第二步中,文档(哈希)之前并未加密,尽管立即发生了解密。正如普通人所要求的,这是“用私钥加密”的第一阶段。
验证过程
要验证签名,需要执行以下步骤。请注意,由于这是一个附录系统,因此有原始文档可供验证过程使用:
- 哈希文档
- 使用公钥加密先前生成的文档哈希值(来自签名过程的第二步)
- 验证第二步中恢复的哈希值是否与验证第一步中计算的哈希值匹配
验证过程的第一步是哈希文档。由于这是一个带附录的签名方案,验证过程需要文档。如果使用带恢复的方案,则会从签名中恢复嵌入的文档。
PKCS #1, 版本 1.5
PKCS 定义了三种使用 MD2、MD5 和 SHA 的 RSA 签名方案。您也可以参考 RFC 3447 获取更多指导。这些方案在 Crypto++ 的 RSAFunction
类中进行了类型定义,以便于使用。MD2 和 MD5 已不再被认为在密码学上是安全的。应使用 SHA 作为摘要函数。
typedef RSASS<PKCS1v15, SHA>::Signer RSASSA_PKCS1v15_SHA_Signer;
typedef RSASS<PKCS1v15, SHA>::Verifier RSASSA_PKCS1v15_SHA_Verifier;
typedef RSASS<PKCS1v15, MD5>::Signer RSASSA_PKCS1v15_MD5_Signer;
typedef RSASS<PKCS1v15, MD5>::Verifier RSASSA_PKCS1v15_MD5_Verifier;
typedef RSASS<PKCS1v15, MD2>::Signer RSASSA_PKCS1v15_MD2_Signer;
typedef RSASS<PKCS1v15, MD2>::Verifier RSASSA_PKCS1v15_MD2_Verifier;
本文将跳过类型定义,直接使用 RSASS<PKCS1v15, SHA>
。由于签名声明使用了模板,欧洲的读者可以通过创建使用 RSASS<PKCS1v15, Whirlpool>
的 RSASS
对象来使用 Whirlpool 作为哈希函数。
PKCS1v15
为签名方案指定了额外的参数,例如可选的填充。如果 128 位或 192 位的哈希值太大(并且不需要数据完整性但需要基本的错误检测),则可以使用 CRC 来实例化 RSASS
对象:RSASS<PKCS1v15, CRC32>
。这些更改显然偏离了 RFC 3447。
PKCS 的最新版本是 2.1。Crypto++ 不支持 2.0 及更高版本。这是因为 2.0 版本引入了多素数 RSA。Crypto++ 不支持多素数 RSA。多素数 RSA 使用可能具有两个以上素数因子的模数。额外的素数因子会影响私钥操作,并降低解密和签名原语的计算成本。
Crypto++ 实现
Crypto++ 实现基于 Wei Dai 在 validate2.cpp 中的代码。感兴趣的函数是 ValidateRSA()
。下面的代码是本文示例的简化版本。完整版本包括用于打印公钥和私钥以及签名十六进制编码的程序代码。使用以下代码时需要注意的几点是:
AutoSeededRandomPool
构建一个伪随机数生成器InvertibleRSAFunction
仅包含密钥以及一些附加信息以加快求逆速度RSASS< PKCS1v15, SHA >
创建一个使用 SHA-1 的 RSA 对象byte*
signature
将接收签名(不方便的是无法在编译时获取长度,因此可以省略new
和delete
)
///////////////////////////////////////
// Quote of the Day
// Stephen Hawking
std::string message(
"I think computer viruses should count as life. I think it\n" \
" says something about human nature that the only form of\n" \
" life we have created so far is purely destructive. We've\n" \
" created life in our own image."
);
///////////////////////////////////////
// Pseudo Random Number Generator
AutoSeededRandomPool rng;
///////////////////////////////////////
// Key Generation
CryptoPP::InvertibleRSAFunction keys;
keys.GenerateRandomWithKeySize( rng, 384 );
///////////////////////////////////////
// Signature
RSASS< PKCS1v15, SHA >::Signer signer( keys );
// Set up for SignMessage()
byte* signature = new byte[ signer.MaxSignatureLength() ];
if( NULL == signature ) { return -1; }
// Sign...
size_t length = signer.SignMessage( rng, (const byte*) message.c_str(),
message.length(), signature );
///////////////////////////////////////
// Verification
RSASS< PKCS1v15, SHA >::Verifier verifier( signer );
bool result = verifier.VerifyMessage( (const byte*)message.c_str(),
message.length(), signature, length );
///////////////////////////////////////
// Result
if( true == result )
{
cout << "Message Verified" << endl;
}
else
{
cout << "Message Verification Failed" << endl;
}
if( NULL != signature ) { delete[] signature; }
RSA 参数
如果读者想单独加载 p、q、n、d 和 e,请使用 InvertibleRSAFunction
类的 SetPrime1()
、SetPrime2()
、SetModulus()
、SetPublicExponent()
和 SetPrivateExponent()
。
SignatureStandard
与 ESIGN 不同,RSA 签名者和验证者对象需要添加一个 SignatureStandard。SignatureStandard 指定签名者和验证者对象将使用的协议。由于本文使用带附录的 RSA,因此选择了 PKCS1v15。
签名长度
关于签名长度的最后一点。signature
缓冲区使用 MaxSignatureLength()
分配。稍后,签名通过 length
传递给验证者,以指定生成的签名大小。length
是从 Signer::SignMessage()
方法返回的。
ESIGN - 一个带附录的签名方案,两个值是相同的。在使用带恢复的签名系统中,这可能不同。在这种情况下,将使用 SignMessage()
返回的结果作为实际签名长度。
数学
密钥生成、签名和验证的数学原理在许多文本中有详细描述。读者可参考维基百科的 RSA 条目、PKCS #1 规范或 RFC 3447。
致谢
- Wei Dai for Crypto++ 及其在 Crypto++ 邮件列表上的宝贵帮助
- A. Brooke Stephens 博士,他为我打下了密码学基础
修订
- 2007.11.26 添加了多素数信息
- 2007.11.25 更新了 Crypto++ 信息
- 2007.09.19 添加了签名过程
- 2007.09.19 添加了关于 PKCS #1 的说明
- 2007.09.19 添加了关于 RFC 3447 的说明
- 2007.09.13 一般更新
- 2007.06.19 首次发布
校验和
Sample.zip
MD5: 61ED4B512816BF4751D56446DE99D585
SHA-1: EB0791DD23C8FF656EE1383F7550C0E89D01A768