RSA加密解密

截取自我的博客:https://chunlife.top/2018/07/29/RSA%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86/

因为项目需要,最近做一个RSA加密解密的接口,使用Go进行开发,接口使用jsonrpc,go 对RSA加密解密有很好的支持,不过由于受限于底层单片机,所以上层应用需要做一些稍微的调整。

一、概要

RSA是一种非对称加密算法,什么是非对称加密算法呢,那就是公钥、私钥可互相进行加密解密:公钥加密—私钥解密,私钥加密—公钥解密。

了解RSA算法的实现原理,可参考:非对称加密过程详解(基于RSA非对称加密算法实现)

三、RSA一些名词的解释

什么是PKCS#1,PKCS(公钥密码标准),而#1就是RSA的标准。

PEM文件,也就是公私钥的编码格式。

RSA算法的原理:RSA算法详解,从这篇博客主要是提取出RSA算法的公式。

1
C=(P^e)%n

N是公钥私钥共同使用的,其为模数。另外还有公钥的指数E,私钥的指数E。

公钥的指数一般是65537,私钥的指数则是一个极大的数,想想一个极大的数作为指数,计算时间是会耗费很长时间的。故公钥加密解密都很快,私钥则会慢很多

二、Go中的RSA加密解密

RSA标准是通过公钥加密,私钥解密 ,没有私钥加密,公钥解密。为什么会这样呢?

Go设计库一般会严格按照标准来进行设计(在很多地方都见过相似做法),那是RSA标准中没有后一种情况的使用场景吗?其实是有的,只不过这个过程不要加密解密,而是RSA签名与验签。所以按照标准,RSA标准库也就不会有私钥加密,公钥解密的方法了。

这个问题想想应该是很多人的问题了,那么在Google上进行搜索了下,还是发现了解决方法。

https://github.com/wenzhenxi/gorsa

库中实现了公钥加解密的方法。

还有其他解决方法吗?是有的,在Google上进行搜索就可以找到,记得还有人使用CGO调用C库来解决。

四、Go RSA库的使用

标准库的使用者很多,博客也很多,这里不做多的介绍,放上一个博主的链接。

GO加密解密之RSA

五、项目中的问题

此处,由于我的问题比较特殊,所以到此并没完全解决我的问题,还记得上面说的RSA指数与模数的东西吗,因为上层是与stm32进行通信,32RSA的库是需要自己手动将指数与模数填入结构体中的,那么上层就应该将生成的私钥进行分解开来,得到stm32所需要的指数和模数,那么怎么得到这些数据呢。

我使用的方法是借用OpenSSL,理论上来说像Python就可以做到,但是我并不想将事情复杂化,直接借用现有的工具是最省事的。

参考:如何用 openssl 生成RSA双密匙;签名证书;加密文件邮件

1
openssl rsa -in private.pem -text -noout

-noout : 表示不显示密钥

运行结果:

RSA加密解密_第1张图片

modulus、publicExponent、privateExponent,这三个数就是我们所需要的数据(publicExponent一般算法会设为65537)。

那么此处就很简单的进行字符串截取就可以做到拿出这三个数据了。

六、AES加密解密

既然提到了RSA,不对称加密算法了,那么也去了解了解AES对称加密算法吧。

golang实现AES ECB模式的加密和解密

Go的实现可参考贴出来的链接,不过此处给出我遇到的一个问题。

截取自博客中的原文:

1533047635959

标记出来的话,其实是有问题的,AES算法,区块长度是固定的,为128bit。

摘抄自百度百科:严格地说,AES和Rijndael加密法并不完全一样(虽然在实际应用中二者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度固定为128比特,密钥长度则可以是128、192、256比特;而Rijndael使用的密钥和区块长度可以是32位的整数倍,以128位为下限,256比特为上限。加密过程中使用的密钥是由Rijndael密钥生成方案产生。

在golang的源码设计中也可以证明这点,其blocksize设定为const,其值为16(byte),显然,标准库是并不允许使用者去修改这个值的,那么AES-128/192/256,其实是针对的密钥长度来说的。

另外,使用go AES库需要注意的是,go aes输入的密钥不满足16、24、32的要求,会直接返回错误,其并没有设计补全机制,需要自己实现。

数据块长度不足128bit,其同样也需要补全;很遗憾的是go依然没有帮助自动补全。补全方式有多种,一般常见的是zeropadding,pkcs5padding,pkcs7padding。

参考golang AES/ECB/PKCS5 加密解密 url-safe-base64

博客使用的是pkcs5padding,这里补上zeropadding。

1
2
3
4
5
6
7
8
9
10
11
12
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{0}, padding) 
	return append(ciphertext, padtext...)
}

func ZeroUnPadding(origData []byte) []byte {
	return bytes.TrimFunc(origData,
		func(r rune) bool {
			return r == rune(0)
		})
}

附上我写的填充key的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func KeyPadding(key string) (keyByte []byte) {

	keyLen := len(key)
	switch {
	case keyLen < 16:
		keyByte = ZeroPadding([]byte(key), 16)
	case keyLen > 16 && keyLen < 24:
		keyByte = ZeroPadding([]byte(key), 24)
	case keyLen > 24 && keyLen < 32:
		keyByte = ZeroPadding([]byte(key), 32)
	case keyLen > 32:
		keyByte = []byte(key)[:32]
	default:
		keyByte = []byte(key)
	}

	return keyByte
}

七、使用的方法

bytes.Join将byte数组的数组进行组合

1
bytes.Join(pBytes, []byte(""))

第二个参数表示数组间用什么去间隔

将一个大数按大小端转换为byte数组模式

binary.BigEndian.PutUint64

https://blog.csdn.net/coledaddy/article/details/71195528

返回子串在字符串中的索引

例如strings.Index(str, “modules”),返回的是开始出现”modules”的位置,即”m”。

将数组转换为以“,”分割的字符串

1
strings.Replace(strings.Trim(fmt.Sprint(byteArr),"[]"), " ", ",", -1)

将十六进制的字符,转换为整数

1
strconv.ParseUint(data, 16, 8)

data:字符,16:进制,8:转换数据的大小,8则是8bit。

你可能感兴趣的:(Go)