Go 实现RSA加密解密
什么是RSA
RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA可以被用于公钥密码和数字签名。
RSA加密解密
公钥加密->私钥解密 (接收者密钥对)
公钥与密钥的产生
假设Alice想要通过一个不可靠的媒体接收Bob的一条私人讯息。她可以用以下的方式来产生一个公钥和一个私钥:
- 随意选择两个大的质数p和q,p不等于q,计算N=pq。
- 根据欧拉函数,求得r = (p-1)(q-1)
- 选择一个小于 r 的整数* e*,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)
- 将* p 和 q *的记录销毁。
(N,e)是公钥,(N,d)是私钥。Alice将她的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。
加密消息
假设Bob想给Alice送一个消息m,他知道Alice产生的N和e。他使用起先与Alice约好的格式将m转换为一个小于N的整数n,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。假如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为n。用下面这个公式他可以将n加密为c:
ne ≡ c (mod N)
计算c并不复杂。Bob算出c后就可以将它传递给Alice。
解密消息
Alice得到Bob的消息c后就可以利用她的密钥d来解码。她可以用以下这个公式来将c转换为n:
cd ≡ n (mod N)
得到n后,她可以将原来的信息m重新复原。
解码的原理是:
cd ≡ n e·d(mod N)
以及ed ≡ 1 (mod p-1)和ed ≡ 1 (mod q-1)。由费马小定理可证明(因为p和q是质数)
n e·d ≡ n (mod p) 和 n e·d ≡ n (mod q)
这说明(因为p和q是不同的质数,所以p和q互质)
n e·d ≡ n (mod pq)
签名消息
私钥加密->公钥解密过程 (发送者密钥对)
作用
1、数据完整性2、数据来源的安全性
RSA也可以用来为一个消息署名。假如甲想给乙传递一个署名的消息的话,那么她可以为她的消息计算一个散列值(Message digest),然后用她的密钥(private key)加密这个散列值并将这个“署名”加在消息的后面。这个消息只有用她的公钥才能被解密。乙获得这个消息后可以用甲的公钥解密这个散列值,然后将这个数据与他自己为这个消息计算的散列值相比较。假如两者相符的话,那么他就可以知道发信人持有甲的密钥,以及这个消息在传播路径上没有被篡改过。
图解:
方式一:使用RSA完成签名验签过程
代码实现 :
package main
import (
"crypto"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"fmt"
)
//rsa加密解密 签名验签
func main() {
//生成私钥
priv, e := rsa.GenerateKey(rand.Reader, 1024)
if e != nil {
fmt.Println(e)
}
//根据私钥产生公钥
pub := &priv.PublicKey
//明文
plaintext := []byte("Hello world")
//加密生成密文
fmt.Printf("%q\n加密:\n", plaintext)
ciphertext, e := rsa.EncryptOAEP(md5.New(), rand.Reader, pub, plaintext, nil)
if e != nil {
fmt.Println(e)
}
fmt.Printf("\t%x\n", ciphertext)
//解密得到明文
fmt.Printf("解密:\n")
plaintext, e = rsa.DecryptOAEP(md5.New(), rand.Reader, priv, ciphertext, nil)
if e != nil {
fmt.Println(e)
}
fmt.Printf("\t%q\n", plaintext)
//消息先进行Hash处理
h := md5.New()
h.Write(plaintext)
hashed := h.Sum(nil)
fmt.Printf("%q MD5 Hashed:\n\t%x\n", plaintext, hashed)
//签名
opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.MD5}
sig, e := rsa.SignPSS(rand.Reader, priv, crypto.MD5, hashed, opts)
if e != nil {
fmt.Println(e)
}
fmt.Printf("签名:\n\t%x\n", sig)
//认证
fmt.Printf("验证结果:")
if e := rsa.VerifyPSS(pub, crypto.MD5, hashed, sig, opts); e != nil {
fmt.Println("失败:", e)
} else {
fmt.Println("成功.")
}
}
签名方式二:使用DSA完成签名验签过程
DSA是基于整数有限域离散对数难题的,其安全性与RSA相比差不多。DSA的一个重要特点是两个素数公开,这样,当使用别人的p和q时,即使不知道私钥,你也能确认它们是否是随机产生的,还是作了手脚。RSA算法却做不到。
DSA是Schnorr算法与ElGammal方式的变体,只能被用于数字签名。
代码实现:
package main
import (
"crypto/dsa"
"crypto/rand"
"fmt"
)
/*
DSA只能做签名 验签 无法做加密
*/
func main() {
var params dsa.Parameters
//生成参数
dsa.GenerateParameters(¶ms,rand.Reader,dsa.L1024N160)
//生成私钥
var priv dsa.PrivateKey
priv.Parameters = params
dsa.GenerateKey(&priv,rand.Reader)
//根据私钥生成公钥
pub := priv.PublicKey
//利用私钥签名数据
msg := []byte("hello world")
r,s,_ :=dsa.Sign(rand.Reader,&priv,msg)
//公钥验签
b := dsa.Verify(&pub,msg,r,s)
if b == true {
fmt.Println("验证成功")
}
}