Golang加密解密之RSA(附带php)
RSA加密算法简史
  RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)⼀起提出的。当时他们三⼈都在⿇省理⼯学院⼯作。RSA就是他们三⼈姓⽒开头字母拼在⼀起组成的。
RSA加密算法原理
  学过算法的朋友都知道,计算机中的算法其实就是数学运算。所以,再讲解RSA加密算法之前,有必要了解⼀下⼀些必备的数学知识。我们就从数学知识开始讲解。
必备数学知识
  RSA加密算法中,只⽤到素数、互质数、指数运算、模运算等⼏个简单的数学知识。所以,我们也需要了解这⼏个概念即可。
素数
  素数⼜称质数,指在⼀个⼤于1的⾃然数中,除了1和此整数⾃⾝外,不能被其他⾃然数整除的数。这个概念,我们在上初中,甚⾄⼩学的时候都学过了,这⾥就不再过多解释了。
互质数
  百度百科上的解释是:公因数只有1的两个数,叫做互质数。;上的解释是:互质,⼜称互素。若N个整数的最⼤公因⼦是1,则称这N个整数互质。
  常见的互质数判断⽅法主要有以下⼏种:
1、两个不同的质数⼀定是互质数。例如,2与7、13与19。
2、⼀个质数,另⼀个不为它的倍数,这两个数为互质数。例如,3与10、5与 26。
3、相邻的两个⾃然数是互质数。如 15与 16。
4、相邻的两个奇数是互质数。如 49与 51。
5、较⼤数是质数的两个数是互质数。如97与88。
6、⼩数是质数,⼤数不是⼩数的倍数的两个数是互质数。例如 7和 16。
7、2和任何奇数是互质数。例如2和87。
8、1不是质数也不是合数,它和任何⼀个⾃然数在⼀起都是互质数。如1和9908。
9、辗转相除法。
指数运算
  指数运算⼜称乘⽅计算,计算结果称为幂。nm指将n⾃乘m次。把nm看作乘⽅的结果,叫做”n的m次幂”或”n的m次⽅”。其中,n称为“底数”,m称为“指数”。
模运算
  模运算即求余运算。“模”是“Mod”的⾳译。和模运算紧密相关的⼀个概念是“同余”。数学上,当两个整数除以同⼀个正整数,若得相同余数,则⼆整数同余。
  两个整数a,b,若它们除以正整数m所得的余数相等,则称a,b对于模m同余,记作: a ≡ b (mod m);读作:a同余于b模m,或者,a与b关于模m同余。例如:26 ≡ 14 (mod 12)。
RSA加密算法
公钥与密钥的产⽣
  假设Alice想要通过⼀个不可靠的媒体接收Bob的⼀条私⼈讯息。她可以⽤以下的⽅式来产⽣⼀个公钥和⼀个私钥:
1、随意选择两个⼤的质数p和q,p不等于q,计算N=pq。
2、根据欧拉函数,求得r = (p-1)(q-1)
3、选择⼀个⼩于 r 的整数 e,求得 e 关于模 r 的模反元素,命名为d。(模反元素存在,当且仅当e与r互质)
4、将 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)
签名消息
  RSA也可以⽤来为⼀个消息署名。假如甲想给⼄传递⼀个署名的消息的话,那么她可以为她的消息计算⼀个散列值(Message digest),然后⽤她的密钥(private key)加密这个散列值并将这个“署名”加在消息的后⾯。这个消息只有⽤她的公钥才能被解密。⼄获得这个消息后可以⽤甲的公钥解密这个散列值,然后将这个数据与他⾃⼰为这个消息计算的散列值相⽐较。假如两者相符的话,那么他就可以知道发信⼈持有甲的密钥,以及这个消息在传播路径上没有被篡改过。
Golang加密解密之RSA
在PHP中,很多功能经常是⼀个函数解决;⽽Go中的却不是。本⽂会通过PHP加密,Go解密;Go加密,PHP解密来学习Go 的RSA相关的API。
该⽂讨论Go RSA加密解密。所有操作在linux下完成。
⼀、概要
这是⼀个⾮对称加密算法,⼀般通过公钥加密,私钥解密。
在加解密过程中,使⽤openssl⽣产密钥。执⾏如下操作:
1)创建私钥:
openssl genrsa -out private.pem 1024 //密钥长度,1024觉得不够安全的话可以⽤2048,但是代价也相应增⼤
2)创建公钥:
openssl rsa -in private.pem -pubout -out public.pem
这样便⽣产了密钥。
⼀般地,各个语⾔也会提供API,⽤于⽣成密钥。在Go中,可以查看encoding/pem包和crypto/x509包。
加密解密这块,涉及到很多标准,个⼈建议需要的时候临时学习⼀下。
⼆、Go RSA加密解密
1、rsa加解密,必然会去查crypto/ras这个包
Package rsa implements RSA encryption as specified in PKCS#1.
这是该包的说明:实现RSA加密技术,基于PKCS#1规范。
对于什么是PKCS#1,可以查阅相关资料。PKCS(公钥密码标准),⽽#1就是RSA的标准。可以查看:PKCS系列简介
从该包中函数的名称,可以看到有两对加解密的函数。
EncryptOAEP和DecryptOAEP
EncryptPKCS1v15和DecryptPKCS1v15
这称作加密⽅案,详细可以查看,PKCS #1 v2.1 RSA 算法标准
可见,当与其他语⾔交互时,需要确定好使⽤哪种⽅案。
PublicKey和PrivateKey两个类型分别代表公钥和私钥,关于这两个类型中成员该怎么设置,这涉及到RSA加密算法,本⽂中,这两个类型的实例通过解析⽂章开头⽣成的密钥得到。
2、解析密钥得到PublicKey和PrivateKey的实例
这个过程,我也是花了好些时间(主要对各种加密的各种东东不熟):怎么将openssl⽣成的密钥⽂件解析到公钥和私钥实例呢?
在encoding/pem包中,看到了—–BEGIN Type—–这样的字样,这正好和openssl⽣成的密钥形式差不多,那就试试。
parse error怎么解决在该包中,⼀个block代表的是PEM编码的结构,关于PEM,请查阅相关资料。我们要解析密钥,当然⽤Decode⽅法:
func Decode(data []byte) (p *Block, rest []byte)
这样便得到了⼀个Block的实例(指针)。
解析来看crypto/x509。为什么是x509呢?这⼜涉及到⼀堆概念。先不管这些,我也是看encoding和crypto这两个包的⼦包摸索出来的。
在x509包中,有⼀个函数:
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
从该函数的说明:ParsePKIXPublicKey parses a DER encoded public key. These values are typically found in PEM blocks with “BEGIN PUBLIC KEY”。可见这就是解析PublicKey的。另外,这⾥说到了PEM,可以上⾯的encoding/pem对了。
⽽解析私钥的,有好⼏个⽅法,从上⾯的介绍,我们知道,RSA是PKCS#1,刚好有⼀个⽅法:
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)
返回的就是rsa.PrivateKey。
3、解密解密实现
通过上⾯的介绍,Go中RSA的解密解密实现就不难了。代码如下:
// 加密
func RsaEncrypt(origData []byte) ([]byte, error) {
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("public key error")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
pub := pubInterface.(*rsa.PublicKey)
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
// 解密
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error!")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
其中,publicKey和privateKey是openssl⽣成的密钥,我⽣成的如下:
/
/ 公钥和私钥可以从⽂件中读取
var privateKey = []byte(`
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----
`)
var publicKey = []byte(`
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
AUeJ6PeW+DAkmJWF6QIDAQAB
-----END PUBLIC KEY-----
`)
4、使⽤例⼦
package main
import (
"fmt"
)
func main() {
data, err := RsaEncrypt([]byte("git@github/mrkt"))
if err != nil {
panic(err)
}
origData, err := RsaDecrypt(data)
if err != nil {
panic(err)
}
fmt.Println(string(origData))
}
该例⼦是加密完git@github/mrkt后⽴马解密
三、跨语⾔加解密
语⾔内部正常,还得看看和其他语⾔是否⼀致,即:其他语⾔加密,Go语⾔得正确解密;Go语⾔加密,其他语⾔正确解密1、PHP RSA加解密
这⾥,我选择PHP,使⽤的是openssl扩展。PHP中加解密很简单,如下两个⽅法(这⾥只考虑⽤公钥加密,私钥解密):bool openssl_public_encrypt ( string $data , string &$crypted , mixed
$key [, int $padding = OPENSSL_PKCS1_PADDING ] ) bool
openssl_private_decrypt ( string $data , string &$decrypted , mixed
$key [, int $padding = OPENSSL_PKCS1_PADDING ] )
最后⼀个参数是加密⽅案(补齐⽅式)。由于Go中使⽤的是PKCS1⽽不是OAEP,所以,使⽤默认值即可。
PHP代码如下:
$privateKey = '-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----'; $publicKey = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv
ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd
wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL
AUeJ6PeW+DAkmJWF6QIDAQAB
-----END PUBLIC KEY-----';
function rsaEncrypt($data)
{
global $publicKey;
openssl_public_encrypt($data, $crypted, $publicKey);
return $crypted;
}
function rsaDecrypt($data)
{
global $privateKey;
openssl_private_decrypt($data, $decrypted, $privateKey);
return $decrypted;
}
function main()
{
$crypted = rsaEncrypt("git@github/mrk");
$decrypted = rsaDecrypt($crypted);
echo "encrypt and decrypt:" . $decrypted;
}
main();
这⾥也是⽤PHP加解密git@github/mrkt
2、Go和PHP⼀起⼯作
这⾥要注意的⼀点是,由于加密后是字节流,直接输出查看会乱码,因此,为了便于语⾔直接加解密,这⾥将加密之后的数据进⾏base64编码。
3、使⽤
⽰例中,php和Go版本都⽀持-d参数传⼊加密好的字符串,将其解密;不传时,会输出加密好并base64编码的串,可⽤于其他语⾔解密。
总结
以上就是⽤Go语⾔实现了RSA的加密解密的全部内容,⽂章很深⼊的讲解了RSA的加密解密过程,对学习相关知识的朋友很有帮助。如果有疑问欢迎留⾔讨论。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。