数字签名技术是信息安全领域中的一项重要技术,它类似于现实世界中的手写签名,但用于电子文档。数字签名能够提供认证、数据完整性和非否认性。不同于消息认证码(MAC),数字签名由于使用了非对称加密技术,因此提供了非否认性。本文将介绍RSA算法的基本概念、数字签名的生成和验证过程,以及如何在C++中使用Crypto++库实现RSA数字签名。
RSA算法是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1977年提出的。虽然他们通常被认为是RSA的发明者,但实际上,英国政府通信总部(GCHQ)的首席数学家克利福德·科克斯(Clifford Cocks)在1973年就已经描述了这个系统,但由于工作是保密的,所以直到1977年才被公开。
数字签名的实现可以分为两种类型:带附录的数字签名方案和带恢复的数字签名方案。带附录的数字签名方案需要在验证过程中提供原始消息,而带恢复的数字签名方案则将原始消息嵌入到签名中,因此不需要提供原始消息即可进行验证。本文将重点介绍带附录的数字签名方案的实现。
数字签名的过程包括两个主要步骤:首先是对文档进行哈希处理,然后使用私钥对哈希值进行解密,得到数字签名。这个过程可能与直觉相反,因为“公共结果”(即数字签名)是由私钥而不是公钥派生的。
验证签名的过程包括对文档进行哈希处理,然后使用公钥对之前生成的文档哈希值进行加密,并验证加密后的哈希值是否与计算出的哈希值匹配。这个过程需要提供原始文档,因为这是一个带附录的签名系统。
Crypto++是一个开源的C++加密库,提供了包括RSA在内的多种加密算法的实现。本文将介绍如何使用Crypto++库来生成和验证RSA数字签名。
Crypto++库可以从Wei Dai的Crypto++页面下载。对于编译和集成的问题,可以参考“将Crypto++集成到Microsoft Visual C++环境”的文章。本文基于之前提到的文章中的基本假设。
以下是使用Crypto++库实现签名过程的示例代码:
#include
#include
#include
#include
#include
#include
using namespace CryptoPP;
using namespace std;
int main() {
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 384);
RSASS::Signer signer(parameters);
byte* signature = new byte[signer.MaxSignatureLength()];
if (NULL == signature) {
return -1;
}
string message = "I think computer viruses should count as life. I think it says something about human nature that the only form of life we have created so far is purely destructive. We've created life in our own image.";
size_t length = signer.SignMessage(rng, (const byte*)message.c_str(), message.length(), signature);
RSASS::Verifier verifier(signer);
bool result = verifier.VerifyMessage((const byte*)message.c_str(), message.length(), signature, length);
if (true == result) {
cout << "Message Verified" << endl;
} else {
cout << "Message Verification Failed" << endl;
}
if (NULL != signature) {
delete[] signature;
}
return 0;
}
PKCS #1标准定义了使用MD2、MD5和SHA的三种RSA签名方案。也可以参考RFC 3447以获取额外的指导。这些方案在Crypto++的RSAFunction类中进行了定义。MD2和MD5不再被认为是密码学上安全的,因此建议使用SHA作为摘要函数。
如果读者希望单独加载p、q、n、d和e,可以使用InvertibleRSAFunction类的SetPrime1()、SetPrime2()、SetModulus()、SetPublicExponent()和SetPrivateExponent()方法。
与ESIGN不同,RSA签名器和验证器对象需要添加SignatureStandard。SignatureStandard指定了签名器和验证器对象将使用的协议。由于本文使用的是带附录的RSA,因此选择了PKCS1v15。
关于签名长度的最后细节。签名缓冲区是使用MaxSignatureLength()分配的。稍后,签名被传递给验证器,使用length指定生成的签名的大小。length是从Signer::SignMessage()方法返回的。在ESIGN中,这两个值是相同的。如果使用带恢复的系统,这可能会有所不同。在这种情况下,将使用从SignMessage()返回的结果作为实际的签名长度。
密钥生成、签名和验证的数学原理在许多文本中有详细描述。读者可以参考Wikipedia的RSA条目、PKCS #1规范或RFC 3447。