您好,欢迎光临赵帅的博客,如果有什么不足或者错误之处,感谢您留言指出!

【原创】山东大学 信息安全 LibTomCrypt RSA 公钥加密 实验二

我的大学 赵 帅 286浏览 0评论

写在前面

关于 LibTomCrypt 库的简介,大家应该比较了解了,以及如何 配置项目,如果还有不是很清楚的,可以参考 【转载】密码库 LibTomCrypt 库简介 以及 【原创】山东大学 信息安全 LibTomCrypt 对称算法加密文件 实验一 。

一、知识准备

什么是公钥密码体制

公钥密码又称为双钥密码和非对称密码,是1976年由Diffie和Hellman在其“密码学新方向”一文中提出的,见划时代的文献:

W.Diffie and M.E.Hellman, New Directrions in Cryptography, IEEE Transaction on Information Theory, V.IT-22.No.6, Nov 1976, PP.644-654

单向陷门函数是满足下列条件的函数f:

(1)给定x,计算y=f(x)是容易的;

(2)给定y, 计算x使y=f(x)是困难的。

(所谓计算x=f-1(Y)困难是指计算上相当复杂,已无实际意义。)

3)存在δ,已知δ 时,对给定的任何y,若相应的x存在,则计算x使y=f(x)是容易的。

1*.仅满足(1)、(2)两条的称为单向函数;第(3)条称为陷门性,δ 称为陷门信息。

2*.当用陷门函数f作为加密函数时,可将f公开,这相当于公开加密密钥。此时加密密钥便称为公开钥,记为Pk。 f函数的设计者将δ 保密,用作解密密钥,此时δ 称为秘密钥匙,记为Sk。由于加密函数时公开的,任何人都可以将信息x加密成y=f(x),然后送给函数的设计者(当然可以通过不安全信道传送);由于设计者拥有Sk,他自然可以解出x=f-1(y)。

3*.单向陷门函数的第(2)条性质表明窃听者由截获的密文y=f(x)推测x是不可行的

1.1 RSA 算法

在1976年,Diffie 和Hellman发表了一篇叫《密码学新动向》的文章,介绍了公匙加密的概念,给密码学的研究者带来了很大的轰动。论文的核心就是陷门单向函数:一个方向上的操作是容易的,但是如果不知道秘密的陷门,即使所有人都知道函数本身,从反方向计算是不现实的。在这里,公匙的扮演的角色和函数相同,仅被有限的一部分人知道的机关就是私匙。

两年后,也就是1978年由Rivest, Shamir 和Adleman提出了被称为RSA的公匙密码系统。RSA算法建立在对整数的分解的数学难题上的。私匙是由三个数(p,q,d)组成,其中p和q是两个素数(具有大致相同的大小),而d和(p-1)*(q-1)互质。公匙由两个数(n,e)组成,其中n=pq,而e是(p-1)(q-1)取d为模的倒数,例如:

ed = 1 mod(p-1)(q-1).

假如爱丽斯想用鲍勃的公匙(n,e)加密,传送一些文本。她首先会把明文转换成小于n的整数m,用鲍勃的公匙(n,e)加密处理:

c = me mod n,

然后把结果c送给鲍勃。在鲍勃的这边,他就会用他的私匙(p,q,d)进行解密处理:

cd mod n = med mod n = m.

对于RSA,单向的陷门函数是能够从一个小于n的整数x得到xe mod n.

RSA算法中用到的一些量是:

1. 素数p和q(保密的);

2. n=p*q(公开的);

3. φ(n)=(p-1)*(q-1)(保密的);

4. 加密密钥e(公开的)要满足0<e<φ(n), 且e和φ(n)的最大公约数为1(互素);

5. 解密密钥d(保密的)要满足0<d<φ(n), 且d*e mod φ(n) = 1

如果用M代表明文,C代表密文,E(M)代表对明文加密运算,D(C)代表对密文解密运算,则有:

E(M)=M^e mod n=C

D(C)=C^d mod n=M

RSA算法安全性的理论基础是大数的因子分解问题至今没有很好的算法,因而公开e和n不易求出p、q及d。RSA算法要求p和q是两个足够大的素数(例如100位十进制数)且长度相差比较小

1.2 单向哈希函数

在很多情况下,我们需要鉴别和认证用户。如登陆计算机(自动柜员机)时,计算机往往需要知道用户是谁,确认某个用户而不是其他人冒充,传统的方法是用口令来解决这个问题。用户在登陆计算机时输入口令,有计算机确认确认口令正确后,用户才可登陆计算机。用户和计算机均需知道口令。用户每次登陆均需输入口令。由于计算机需要知道口令,这就需要把口令保存在计算机中。这为入侵计算机偷取口令提供了可能。为此,我们不直接保存口令本身,而保存口令的散列值(口令的某种表示形式)。当用户输入口令后,计算机先计算口令的散列值并于保存在计算集中的散列值进行比较,以次来鉴别用户。由于用来计算散列的值的函数具有单向性,即根据散列值不可能逆向恢复出口令,从而即使获得了有口令产生的散列值也无法知道用户的口令

单向散列函数 H(M) 作用于一个任意长度的消息 M,它返回一个固定长度的散列值 h,其中 h 的长度为 m 。
输入为任意长度且输出为固定长度的函数有很多种,但单向散列函数还有使其单向的其它特性:
(1) 给定 M ,很容易计算 h ;
(2) 给定 h ,根据 H(M) = h 计算 M 很难;
(3) 给定 M ,要找到另一个消息 M’ 并满足 H(M) = H(M’) 很难。
在许多应用中,仅有单向性是不够的,还需要称之为”抗碰撞”的条件:
要找出两个随机的消息 M 和 M’,使 H(M) = H(M’) 满足很难。
由于散列函数的这些特性,由于公开密码算法的计算速度往往很慢,所以,在一些密码协议中,它可以作为一个消息 M 的摘要,代替原始消息 M,让发送者为 H(M) 签名而不是对 M 签名。
如 SHA 散列算法用于数字签名协议 DSA中

1.3 数字签名

提到数字签名就离不开公开密码系统和散列技术。
有几种公钥算法能用作数字签名。在一些算法中,例如RSA,公钥或者私钥都可用作加密。用你的私钥加密文件,你就拥有安全的数字签名。在其它情况下,如DSA,算法便区分开来了数字签名算法不能用于加密。这种思想首先由Diffie和Hellman提出。
基本协议是简单的:
(1) A 用她的私钥对文件加密,从而对文件签名。
(2) A 将签名的文件传给B。
(3) B用A的公钥解密文件,从而验证签名。
这个协议中,只需要证明A的公钥的确是她的。如果B不能完成第(3)步,那么他知道签名是无效的。
这个协议也满足以下特征:
(1) 签名是可信的。当B用A的公钥验证信息时,他知道是由A签名的。
(2) 签名是不可伪造的。只有A知道她的私钥。
(3) 签名是不可重用的。签名是文件的函数,并且不可能转换成另外的文件。
(4) 被签名的文件是不可改变的。如果文件有任何改变,文件就不可能用A的公钥验证。
(5) 签名是不可抵赖的。B不用A的帮助就能验证A的签名。
在实际应用中,因为公共密码算法的速度太慢,签名者往往是对消息的散列签名而不是对消息本身签名。这样做并不会降低签名的可信性

目前有许多种技术保证信息的安全不受侵犯,例如加密技术、访问控制技术、认证技术以及安全审计技术等,但这些技术大多数是用来预防用的,信息一旦被攻破,我们不能保证信息的完整性。为此,一种新兴的用来保证信息完整性的安全技术——数字签名技术成为人们非常关心的话题。那么,什么是数字签名技术?它有什么特殊功能呢?

概念

在数字签名技术出现之前,曾经出现过一种“数字化签名”技术,简单地说就是在手写板上签名,然后将图像传输到电子文档中,这种“数字化签名”可以被剪切,然后粘贴到任意文档上,这样非法复制变得非常容易,所以这种签名的方式是不安全的。数字签名技术与数字化签名技术是两种截然不同的安全技术,数字签名与用户的姓名和手写签名形式毫无关系,它实际使用了信息发送者的私有密钥变换所需传输的信息。对于不同的文档信息,发送者的数字签名并不相同。没有私有密钥,任何人都无法完成非法复制。从这个意义上来说,“数字签名”是通过一个单向函数对要传送的报文进行处理得到的,用以认证报文来源并核实报文是否发生变化的一个字母数字串。

原理

该技术在具体工作时,首先发送方对信息施以数学变换,所得的信息与原信息惟一对应;在接收方进行逆变换,得到原始信息。只要数学变换方法优良,变换后的信息在传输中就具有很强的安全性,很难被破译、篡改。这一个过程称为加密,对应的反变换过程称为解密。

现在有两类不同的加密技术,一类是对称加密,双方具有共享的密钥,只有在双方都知道密钥的情况下才能使用,通常应用于孤立的环境之中,比如在使用自动取款机(ATM)时,用户需要输入用户识别号码(PIN),银行确认这个号码后,双方在获得密码的基础上进行交易,如果用户数目过多,超过了可以管理的范围时,这种机制并不可靠。

另一类是非对称加密,也称为公开密钥加密,密钥是由公开密钥和私有密钥组成的密钥对,用私有密钥进行加密,利用公开密钥可以进行解密,但是由于公开密钥无法推算出私有密钥,所以公开的密钥并不会损害私有密钥的安全,公开密钥无须保密,可以公开传播,而私有密钥必须保密,丢失时需要报告鉴定中心及数据库。

算法

数字签名的算法很多, 应用最为广泛的三种是: Hash签名、DSS签名和RSA签名。

二、代码解析

首先我们要选择RSA的一种加解密机制,

RSA的加密机制有两种方案:一个是RSAES-OAEP,另一个RSAES-PKCS1-v1_5。

推荐在新的应用中使用RSAES- OAEP,保留 RSAES-PKCS#1-v1_5跟老的应用兼容。它们两的区别仅仅在于加密前编码的方式不同。而加密前的编码是为了提供了抵抗各种活动的敌对攻击的安全机制。

声明一个 padding 变量,这个与后期的加解密函数中的填充方式有关:

scanf("%d", &inpadding);
switch (inpadding)
{
	case 1:
		printf("请输入明文(16字节以下)\n");
		scanf("%s", &pt);
		padding = LTC_LTC_PKCS_1_V1_5;//LTC_LTC_PKCS_1_V1_5 = 1 
		break;
	case 2:
		printf("请输入明文(16字节以下)\n");
		scanf("%s", &pt);
		padding = LTC_LTC_PKCS_1_OAEP;//LTC_LTC_PKCS_1_OAEP = 2
		break;
	default:
		return 0;//表示退出
}

然后我们需要注册一个伪随机数发生器:

        /*register prng*/
/*
函数定义:int register_prng(const struct ltc_prng_descriptor *prng);
功能说明:注册一个伪随机数生成器
参数说明:const struct ltc_prng_descriptor *prng---一个prng结构体
返回值说明:返回值为-1表示注册一个prng失败,否则注册成功
*/
if (register_prng(&sprng_desc) == -1){
	printf("Error registering sprng");
	return EXIT_FAILURE;
}

然后注册一个数学库:

        /*
函数定义:int register_hash(const struct ltc_hash_descriptor *hash);
功能说明:注册一个数学库
参数说明:const struct ltc_hash_descriptor *hash ---一个hash结构体
返回值说明:返回值为-1表示注册一个数学库失败,否则注册成功
*/
if (register_hash(&sha1_desc) == -1){
	printf("注册 数学库失败~");
	return EXIT_FAILURE;
}

另外在我们还需要找到 hash 函数伪随机数发生器函数 的位置:

       /*
函数定义:int find_hash(const char *name);
功能说明:在hash表里查找一个hash
参数说明:const char *name ---要查找的hash的name
返回值说明:返回值为-1表示查找失败;否则返回该hash在hash表里的位置
*/
hash_idx = find_hash("sha1");
/*
函数定义:int find_prng(const char *name);
功能说明:在prng表里查找一个prng
参数说明:const char *name ---要查找的prng的name
返回值说明:返回值为-1表示查找失败;否则返回该prng在prng表里的位置
*/
prng_idx = find_prng("sprng");

很重要~~~

RSA算法加密要用到公钥,所以我们要生一个公钥,要用到 rsa_make_key() 这个方法。

但是在调用方法之前我们需要初始化一个数学库的一个宏定义,这个很重要!这个bug调了很长时间,我们首先要在main函数的上面,进行预定义:

#define LTM_DESC

然后我们还要在 方法里面初始化一个 ltc_mp 变量,

ltc_mp = ltm_desc;

但是这样编译项目会出现变量未定义的错误,所以我们还要在项目的属性的预编译器里面添加一个 LTM_DESC 变量:

然后,我们就可以调用 rsa_make_key() 生成一个 rsa_key :

	/*
函数定义:int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key);
功能说明:生成一个1024bit的RSA密钥
参数说明:prng_state *prng---prng状态
int wprng---prng标志
int size---密钥长度
long e---加密时e的值
rsa_key *key---RSA密钥
返回值说明:返回值为0表示生成密钥成功,否则生成密钥失败
*/
ltc_mp = ltm_desc;

error = rsa_make_key(NULL, /* PRNG state */
	prng_idx, /* PRNG idx */
	1024 / 8, /* 1024-bit key */
	65537, /* we like e=65537 */
	&key); /* where to store the key */
if (error != CRYPT_OK) {
	printf("rsa_make_key %s", error_to_string(error));
	return EXIT_FAILURE;
}

(1)加密流程

准备工作做完之后,我们就可以使用 rsa_encrypt_key_ex() 进行对明文加解密,我们可以看到这个函数的参数,包括了需要加密的明文pt密文ct随机数生成器的位置参数 prng ,数学库函数的位置参数hash_idx 填充方式padding,以及密钥 key

	/*
函数定义:int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out,
unsigned long *outlen, const unsigned char *lparam, unsigned long l paramlen, prng_state *prng, 
int prng_idx, int hash_idx, int padding, rsa_key *key);
功能说明:RSA加密过程,对输入的明文进行加密
参数说明:const unsigned char *in ---要加密的明文
unsigned long inlen ---明文长度
unsigned char *out ---存放加密后的密文
unsigned long *outlen ---密文长度
const unsigned char *lparam --- lparam参数
unsigned long l paramlen---lparam长度
prng_state *prng--- prng状态
int prng_idx---prng标志
int hash_idx---hash标志
int padding---填充方式
rsa_key *key---密钥
返回值说明:返回值为0表示加密成功,否则加密失败
*/
l1 = sizeof(out);	
error = rsa_encrypt_key_ex(
	pt,
	16,
	out,
	&l1,
	(unsigned char *)"zhaoshuai-960229",
	16,
	NULL,
	prng_idx,
	hash_idx,
	padding,
	&key);
if (error != CRYPT_OK){
	printf("加密失败。");
	return EXIT_FAILURE;
}

(2)解密流程

那么同样我们对密文进行解密的时候,用到了 rsa_decrypt_key_ex() 方法,参数同样包括了需要解密的密文ct,解密后的明文pt

数学库的位置参数 hash_idx ,填充方式padding,以及密钥 key,还有返回的状态参数 state ,表示解密数据是否成功。

	/*
函数定义:int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out,
unsigned long *outlen, const unsigned char *lparam , unsigned long
lparamlen, int hash_idx , int padding, int *stat, rsa_key*key);
功能说明:RSA解密过程,对密文进行解密
参数说明:const unsigned char *in ---要解密的密文
unsigned long inlen ---密文长度
unsigned char *out ---存放解密后的明文
unsigned long *outlen ---明文长度
const unsigned char *lparam --- lparam参数
unsigned long l paramlen---lparam长度
int hash_idx---hash标志
int padding---填充方式
int *stat---解密后的数据正确与否
rsa_key *key---密钥
返回值说明:返回值为0表示解密成功,否则解密失败
*/
//memset(pt, sizeof(pt), 0);

l2 = sizeof(pt2);
error = rsa_decrypt_key_ex(
	out,
	l1,
	pt2,
	&l2,
	(unsigned char *)"zhaoshuai-960229",
	16,
	hash_idx,
	padding,
	&res,
	&key);

三、测试用例

我们需要选择一种加密机制,并且需要输入想要的加密的明文,在这里并没有做文件的读入,核心内容已经包括进来了:

啊哈,加解密成功了。

那么以上就是整个RSA算法加解密的流程,有需要的朋友麻烦点个赞吧~~感谢!

参考资料

程序源码:http://pan.baidu.com/s/1milWOfm

实验报告:http://pan.baidu.com/s/1eSoQPTg

转载请注明:碎念 » 【原创】山东大学 信息安全 LibTomCrypt RSA 公钥加密 实验二

喜欢 (7)or分享 (0)