<golang>RSA数字签名及MAC消息认证码

版权声明:本文为作者原创,如需转载,请注明出处
https://blog.csdn.net/weixin_42940826

消息认证码

概念

消息认证码(message authentication code)取三个单词的首字母,简称为MAC。
是一种使用单向散列函数确认完整性,并进行认证,确保文件传输过程中不被篡改的技术。但只能用来认证消息的完整可靠,并不能保证机密性,一般用来传输公开消息。


图解原理

为啥需要消息认证码?
即使使用非对称加密,中间人拦截攻击也是不可避免的,比如黑客将Alice的消息拦截后,用Bob的公钥加密后发送给Bob,Bob会误以为是Alice发的消息。
为什么需要消息认证码
MAC过程实现
MAC过程实现
MAC就是应这个问题而生的,但有一个前提就是两人有一把相同的共享秘钥。


案例分析

比如小明想要向小红发送“我爱你”,首先将“我爱你”拼接上共享密钥,将拼接后的字段计算单向散列值,即计算sha256(“我爱你”+“共享秘钥”)的值,假设值为OJBKO。
此时小明的发送准备工作已经做好,接下来发送明文:“我爱你”+OJBKO即可。
小红收到这个消息后,首先将"我爱你"加上两人的共享秘钥,计算其单向散列值,对比散列值是否等于小明发来的OJBKO,如果等于,证明消息是正确的。


其实了解如上原理之后,我们使用简单的单向散列函数已经可以实现消息认证码了,但是为了方便,go语言标准库中的crypto/hamc包已经为我们封装好了,很方便,下面不多比比,直接看代码。

代码实现

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
)

func main() {
	//先给到一个两人共有的共享秘钥
	key:=[]byte("csdn")
	//所需两个参数:1、单向散列函数名,比如sha1.new,md5.new,sha256.new
	//2、字符切片类型的共享密钥 最终返回一个hash接口
	hash:=hmac.New(sha256.New,key)
	//接下来的操作和单向散列函数的使用是一样的
	//写入数据
	hash.Write([]byte("iloveyou"))
	//计算结果,默认为nil
	mac:=hash.Sum(nil)
	fmt.Printf("mac:%x",mac)
}
//输出结果
//mac:0b9b108a542a357936fc299f7697551c5b5db271680886767256ca47509fb348

MAC弊端

以上就是消息认证码极其实现了,但是消息认证码存在诸多弊端:
1:需要有一个前提,两人得有一把共享秘钥,那么共享秘钥的传输也是一个问题。
2、无法被第三方机构认证且可以被否认 。
因此,更强大的数字签名就应运而生了


数字签名

概念

数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名,就是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。


图解过程

数字签名的过程

案例分析

继续使用小明向小红求婚的例子,只是这里使用数字签名的方法。
首先小明将"我爱你"求出单向散列值,并用自己的私钥进行加密,假设加密后的字符串为ojbkojbko,接着小明就可以将我爱你和ojbkojbko发送给小红了,这一段字符我们就成为是小明的数字签名。
小红收到这一串字符后,使用小明的公钥进行解密,(因为公钥是公开的,任何都可以知道),接着小红求出“我爱你”的单向散列值,对比解密后的结果是否与我爱你的单向散列值相同。
由于小明的私钥只有小明才持有,因此小明不能否认这不是他发的消息,并且使用了单向散列函数,消息也不会被篡改,得到了认证。


代码实现

数字签名主要以非对称加密解密为底层技术,因此,非对称加密有几种,数字签名就有几种,比如RSA、ECC等,这里我们以RSA为例。
go语言已经为我们很完美的封装成两个大函数,一个是签名函数,另一个是验证签名函数。先来看看关键的两个函数。

func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (sign []byte, err error)
参数1: rand.Reader(随机数生成器)
参数2: 非对称加密的私钥
参数3: 使用的哈希算法
	crypto.sha1
	crypto.md5
参数4: 数据计算之后得到的散列值
返回值: 
- sign: 得到的签名数据
- err: 返回错误信息


func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error)
参数1: 公钥
参数2: 哈希算法 -> 与签名使用的哈希算法一致
参数3: 将原始数据进行哈希运算得到的散列值
参数4: 签名的字符串
返回值: 
	- nil -> 验证成功
	-!=nil -> 失败

了解这两个关键函数再来实现就容易的多。

具体实现

package main

import (
	"crypto/rsa"
	"crypto/rand"
	"crypto"
	"crypto/sha256"
	"fmt"
)


func main()  {
	//定义一个要签名的明文
	plainText:=[]byte("iloveyou")


	//开始签名前需要得到密钥对,为了方便我们将密钥对一次性使用,就不保存到文件中去了
	//bits建议1024的倍数
	//生成密钥对,公钥是存储在私钥接口里的,点一下即可获得
	privateKey,_:=rsa.GenerateKey(rand.Reader,1024)

	//签名函数中需要的数据散列值
	hash:=sha256.Sum256(plainText)

	//调用签名函数,填入所需四个参数,得到签名
	sign,_:=rsa.SignPKCS1v15(rand.Reader,privateKey,crypto.SHA256,hash[:])

	fmt.Printf("sign:%x\n",sign)

	//验证签名
	err:=rsa.VerifyPKCS1v15(&privateKey.PublicKey,crypto.SHA256,hash[:],sign)
	if err!=nil {
		fmt.Println("认证失败")
	}else {
		fmt.Println("认证成功")
	}
}

//得到结果
//sign:019dbd0348614db3ccedca0dba494c3d8d7e696dd4d6eb9b068acee1cd1e3312ce74ab0628fac6d0c82d417be1647b3f1d04654813d78942051b8da38302b953cf009b42a9080c104a79dab2b5091cb11663ba87b65fd5bcba465b5e7b788f532ae93fe243d3cf95313239d2001e8729027a811d04731549cb6eeb3df58d924f
//认证成功

数字签名解决了消息认证码的秘钥配送问题,并且不能够被否认。
配合上第三方认证,目前被广泛使用。


以上就是消息认证码和RSA数字签名了,博客中还有很多相关密码学知识的帖子,都是自学中的一些心得体会,有问题欢迎大家一起交流: )

猜你喜欢

转载自blog.csdn.net/weixin_42940826/article/details/83869324
今日推荐