您的位置:首页 > 理论基础 > 计算机网络

VC++网络安全编程范例(8)-摘要签名和验证编程实现

2011-12-17 13:27 676 查看
是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数, 数字摘要
也就是Hash函数。   数字摘要就是采用单项Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,儿同样的明文其摘要必定一致。   一个Hash函数的好坏是由发生碰撞的概率决定的。如果攻击者能够轻易地构造出两个消息具有相同的Hash值,那么这样的Hash函数是很危险的。一般来说,安全Hash标准的输出长度为160位,这样才能保证它足够的安全。 这一加密方法亦称安全Hash编码法(SHA:Secure Hash Algorithm)或MD5(MD Standards for Message Digest),由Ron Rivest所设计。该编码法采用单向Hash函数将需加密的明文“摘要”成一串128bit的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。这样这摘要便可成为验证明文是否是“真身”的“指纹”了。

基本原理
  (1) 被发送文件用SHA编码加密产生128bit的数字摘要(见上节)。

(2) 发送方用自己的私用密钥对摘要再加密,这就形成了数字签名。   

(3) 将原文和加密的摘要同时传给对方。   

(4) 对方用发送方的公共密钥对摘要解密,同时对收到的文件用SHA编码加密产生又一摘要。

  (5) 将解密后的摘要和收到的文件在接收方重新加密产生的摘要相互对比。如两者一致,则说明传送过程中信息没有被破坏或篡改过。否则不然。

代码实现如下

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/err.h>

void print(const char *promptStr,unsigned char *data,int len)
{
int i;
printf("\n===%s[长度=%d]======\n",promptStr,len);
for(i = 0; i < len; i++) printf("%02x", data[i]);
printf("\n=======================\n");
}

void print_errors()
{
/*
int flags, line;
char *data, *file;
unsigned long code;
code = ERR_get_error_line_data(&file, &line, &data, &flags);
while (code)
{
printf("error code: %lu in %s line %d.\n", code, file, line);
if (data && (flags & ERR_TXT_STRING))
printf("error data: %s\n", data);
code = ERR_get_error_line_data(&file, &line, &data, &flags);
}
*/
}
//如果成功返回EVP_PKEY,否则返回NULL

EVP_PKEY* getDSA()
{
EVP_PKEY* pkey=NULL;
int ret=0;
DSA* dsa=DSA_generate_parameters(1024,NULL,0,NULL,NULL,NULL,NULL);
if(NULL==dsa)
{
printf("生成DSA参数失败\n");
return NULL;
}
printf("\nDSA 参数成功产生\n");
ret=DSA_generate_key(dsa);
if(ret==0)
{
printf("DSA_generate_key 失败\n");
goto err;
}
printf("\nDSA 钥对成功产生\n");

pkey=EVP_PKEY_new();
if(NULL==pkey)
{
printf("EVP_PKEY_new 失败\n");
goto err;
}

EVP_PKEY_assign_DSA(pkey,dsa);
return pkey;

err:
DSA_free(dsa);
return NULL;
}
//如果成功返回EVP_PKEY,否则返回NULL
EVP_PKEY* getRSA()
{
EVP_PKEY* pkey=NULL;
RSA* rsa=RSA_generate_key(1024,RSA_3,NULL,NULL);
if(NULL==rsa)
{
printf("生成RSA 密钥对失败\n");
return NULL;
}
// 盲化rsa 密钥以抵御时间攻击
RSA_blinding_on(rsa,NULL);
printf("\n生成RSA 密钥对成功\n");
pkey=EVP_PKEY_new();
if(NULL==pkey)
{
printf("EVP_PKEY_new 失败\n");
RSA_free(rsa);
return NULL;
}

EVP_PKEY_assign_RSA(pkey,rsa);
return pkey;
}

void main(int argc, char *argv[])
{
EVP_MD_CTX mdctx;
const EVP_MD *md;

char msgs[][64]={"It's just for test",
"Author: Jian Shen","Hello World from openssl"};
//确切的说,我们应该从 EVP_PKEY_size()获得
//应该注意不要使用EVP_MAX_MD_SIZE,它太小了
unsigned char sig_value[1024];

char mdName[]="dss1";//dss1是唯一被DSA支持的信息摘要算法
//char mdName[]="sha1";

unsigned int sig_len;
int i;

DSA* dsa=NULL;
EVP_PKEY* pkey=NULL;

OpenSSL_add_all_digests();
//不要遗漏下面的步骤否则EVP_SignFinal会出错!!
OpenSSL_add_all_ciphers();

md = EVP_get_digestbyname(mdName);

if(!md) {
printf("错误的摘要算法名称 %s\n", mdName);
exit(1);
}

pkey=getDSA(); //获取DSA对象
//pkey=getRSA();
if(pkey==NULL)
{
exit(-1);
}

EVP_MD_CTX_init(&mdctx);//初始化摘要计算上下文
EVP_SignInit(&mdctx, md);//初始化签名算法
//EVP_SignInit_ex(&mdctx, md,NULL);

for( i=0;i<sizeof(msgs)/sizeof(msgs[0]);i++ )
{
EVP_SignUpdate(&mdctx, msgs[i], strlen(msgs[i]));
}
//计算签名:实际上该步先计算出摘要值,然后对该值用私钥签名
i=EVP_SignFinal(
&mdctx,//摘要计算上下文对象
sig_value,//输出的签名值
&sig_len,//签名值长度
pkey //签名所用私钥
);
//i=EVP_DigestFinal(&mdctx,sig_value,&sig_len);//successfully
if(i==0)
{
printf("计算签名失败");
exit(1);
}
EVP_MD_CTX_cleanup(&mdctx);//释放摘要上下文对象
EVP_PKEY_free(pkey);//释放公钥对象

print("签名值是: ",sig_value,sig_len);

printf("\n click any key to continue.");
//相当于“暂停”,以便观察运行结果
getchar();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: