Go-RSA加密解密详解与代码

本文涉及的产品
云解析 DNS,旗舰版 1个月
密钥管理服务KMS,1000个密钥,100个凭据,1个月
全局流量管理 GTM,标准版 1个月
简介: Go-RSA加密解密详解与代码

对称密码的缺点

保密通信进入计算机网络时代,传统密码体制逐渐暴露其固有的弱点,体现在密钥分发。通过前面的对称密码文章,可以看到需要双方知道密钥。问题在于我要给你发送秘密信息,没有安全的通道,发送信息前需要找到安全的通道告诉你密钥,然后再在不安全通道进行通信。问题是我既然找到了安全的通道,直接发送消息不就行了吗? 当然,古代大部分是在线下约定好的,类似《悬崖之上》的密码本。

对称密码解决了密钥分发问题,可以在不安全通道发送密钥。当然,对称密码还有数字签名问题,后面数字签名相关的文章再说。

RSA概述

W.Diffie和Hellman发表了著名的文章《密码学的新方向》首次提出了公钥密码算法的思想。


公钥密码体制为密码学的发展提供了新的理论和技术思想,一方面公钥密码算法是建立在数学函数基础上的,而不是建立在字符或位方式的操作上的;另一方面公钥密码算法是以非对称的形式使用加密密钥和解密密钥,这两个密钥的使用对密钥管理、认证等都有着深刻的实际意义。可以说,公钥密码体制的出现在密码学发展史上是一次质的飞跃。


1978年,Rivest,Shamir和Adleman提出的RSA算法体现了公钥算法的思想,被认为是第一个安全的、实用的公钥密码算法。


RSA的理论基础是数论的欧拉定理,它的安全性依赖于大整数的素因子分解的困难性。

RSA密钥对生成算法

  1. 选取两个大素数p和q,两个数长度接近且相差较大。
  2. 计算n=p*q,9(n)=(p-1)(q-1)。
  3. 随机选取整数e,满足gcd(ee(n))=1。
  4. 计算d,满足d*e=1(mode(n))。

注:p和q保密。e和n为公钥,d为私钥。

RSA加解密过程

加密算法:c=E(m)=20201204182323419.gif(mod n)

解密算法:m=D(c)=20201204182323419.gif(mod n)

RSA小结

  • 第一个实用的公开密钥算法。
  • 目前使用最多的一种公钥密码算法。
  • RSA的理论基础是数论的欧拉定理
  • RSA的安全性依赖于大数的素因子分解的困难性。
  • 密码分析者既不能证明也不能否定RSA的安全性。
  • 既能用于加密也能用于数字签名
  • 目前密钥长度1024位是安全的。

RSA的Go实现

encoding/pem包

func Decode(data []byte) (p *Block, rest []byte)

Decode函数会从输入里查找到下一个PEM格式的块(证书、私钥等)。它返回解码得到的Block和剩余未解码的数据。如果未发现PEM数据,返回(nil, data)。

crypto/x509包

func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte

MarshalPKCS1PrivateKey将rsa私钥序列化为ASN.1 PKCS#1 DER编码。

func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)

MarshalPKIXPublicKey将公钥序列化为PKIX格式DER编码。

func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)

ParsePKIXPublicKey解析一个DER编码的公钥。这些公钥一般在以"BEGIN PUBLIC KEY"出现的PEM块中。

func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)

ParsePKCS1PrivateKey解析ASN.1 PKCS#1 DER编码的rsa私钥。

crypto/rsa包

func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error)

EncryptPKCS1v15使用PKCS#1 v1.5规定的填充方案和RSA算法加密msg。信息不能超过((公共模数的长度)-11)字节。注意:使用本函数加密明文(而不是会话密钥)是危险的,请尽量在新协议中使用RSA OAEP。

func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error)

DecryptPKCS1v15使用PKCS#1 v1.5规定的填充方案和RSA算法解密密文。如果random不是nil,函数会注意规避时间侧信道攻击。

生成RSA密钥对

使用rsa.GenerateKey生成私钥

使用x509.MarshalPKCS1PrivateKey序列化私钥为derText

使用pem.Block转为Block

使用pem.Encode写入文件

从私钥中获取公钥

使用x509.MarshalPKIXPublicKey序列化公钥为derStream

使用pem.Block转为Block

使用pem.Encode写入文件

func GenerateRsaKey(keySize int, dirPath string) error {
  privateKey,err := rsa.GenerateKey(rand.Reader,keySize)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  // x509
  derText :=x509.MarshalPKCS1PrivateKey(privateKey)
  // pem Block
  block := &pem.Block{
    Type:"rsa private key",
    Bytes:derText,
  }
  // just joint, caller must let dirPath right
  file,err := os.Create(dirPath+"private.pem")
  defer file.Close()
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  err = pem.Encode(file,block)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  // get PublicKey from privateKey
  publicKey := privateKey.PublicKey
  derStream,err := x509.MarshalPKIXPublicKey(&publicKey)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  block = &pem.Block{
    Type:"rsa public key",
    Bytes:derStream,
  }
  file,err = os.Create(dirPath+"public.pem")
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  err = pem.Encode(file, block)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return util.Error(file,line+1,err.Error())
  }
  return nil
}

加解密

加密

  1. 获取公钥
  2. 使用x509.ParsePKIXPublicKey解析公钥
  3. 使用rsa.EncryptPKCS1v15加密
func RsaEncrypt(plainText []byte,filePath string) ([]byte, error) {
  // get pem.Block
  block,err := util.GetKey(filePath)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,err.Error())
  }
  // X509
  publicInterface,err := x509.ParsePKIXPublicKey(block.Bytes)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,err.Error())
  }
  publicKey,flag := publicInterface.(*rsa.PublicKey)
  if flag == false{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,errors.RsatransError)
  }
  // encrypt
  cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,err.Error())
  }
  return cipherText,nil
}

解密

  1. 获取私钥
  2. 使用x509.ParsePKCS1PrivateKey解析私钥
  3. 使用rsa.DecryptPKCS1v15解密
func RsaDecrypt(cipherText []byte,filePath string) (plainText []byte,err error) {
  // get pem.Block
  block,err := util.GetKey(filePath)
  if err != nil{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,err.Error())
  }
  // get privateKey
  privateKey, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
  defer func() {
    if err2 := recover();err2 != nil{
      _, file, line, _ := runtime.Caller(0)
      err = util.Error(file,line,errors.RsaNilError)
    }
  }()
  // get plainText use privateKey
  plainText, err3 := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
  if err3 != nil{
    _, file, line, _ := runtime.Caller(0)
    return nil,util.Error(file,line+1,err3.Error())
  }
  return plainText,err
}

测试代码

func TestRsa(t *testing.T) {
  // 生成密钥对
  err := GenerateRsaKey(1024, "./")
  if err!=nil{
    fmt.Println(err)
  }
  // 测试加密
  plainText := []byte("hi, I'm lady_killer9")
  cipherText,err := RsaEncrypt(plainText,"./public.pem")
  if err!=nil{
    fmt.Println(err)
  }
  fmt.Printf("加密后为:%s\n",cipherText)
  // 测试解密
  plainText,err = RsaDecrypt(cipherText,"./private.pem")
  if err!=nil{
    fmt.Println(err)
  }
  fmt.Printf("解密后为:%s\n",plainText)
}

截图

2020062310470442.png

源代码地址:https://gitee.com/frankyu365/gocrypto

目前是v1.0.5-alpha版本,测试中,遇见了个空指针panic,不太确定直接recover的思路是否正确。

参考

《现代密码学教程 谷利泽,杨义先等》


Go标准库-crypto/rsa


Go标准库-crypto/x509


Go标准库-encoding/pem


更多Go相关内容:Go-Golang学习总结笔记


有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。


相关文章
|
3月前
|
Cloud Native Go 开发工具
不改一行代码轻松玩转 Go 应用微服务治理
为了更好的进行 Go 应用微服务治理,提高研发效率和系统稳定性,本文将介绍 MSE 微服务治理方案,无需修改业务代码,实现治理能力。
19848 9
|
14天前
|
安全 Go 开发者
代码之美:Go语言并发编程的优雅实现与案例分析
【10月更文挑战第28天】Go语言自2009年发布以来,凭借简洁的语法、高效的性能和原生的并发支持,赢得了众多开发者的青睐。本文通过两个案例,分别展示了如何使用goroutine和channel实现并发下载网页和构建并发Web服务器,深入探讨了Go语言并发编程的优雅实现。
28 2
|
1月前
|
算法 安全 Go
RSA加密算法详解与Python和Go实现
RSA加密算法详解与Python和Go实现
94 1
|
1月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
126 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
1月前
|
JSON 搜索推荐 Go
ZincSearch搜索引擎中文文档及在Go语言中代码实现
ZincSearch官网及开发文档均为英文,对非英语用户不够友好。GoFly全栈开发社区将官方文档翻译成中文,并增加实战经验和代码,便于新手使用。本文档涵盖ZincSearch在Go语言中的实现,包括封装工具库、操作接口、统一组件调用及业务代码示例。官方文档https://zincsearch-docs.zinc.dev;中文文档https://doc.goflys.cn/docview?id=41。
|
2月前
|
存储 安全 数据安全/隐私保护
安全升级!Python AES加密实战,为你的代码加上一层神秘保护罩
【9月更文挑战第12天】在软件开发中,数据安全至关重要。本文将深入探讨如何使用Python中的AES加密技术保护代码免受非法访问和篡改。AES(高级加密标准)因其高效性和灵活性,已成为全球最广泛使用的对称加密算法之一。通过实战演练,我们将展示如何利用pycryptodome库实现AES加密,包括生成密钥、初始化向量(IV)、加密和解密文本数据等步骤。此外,还将介绍密钥管理和IV随机性等安全注意事项。通过本文的学习,你将掌握使用AES加密保护敏感数据的方法,为代码增添坚实的安全屏障。
132 8
|
1月前
|
安全 测试技术 Go
Python 和 Go 实现 AES 加密算法的技术详解
Python 和 Go 实现 AES 加密算法的技术详解
75 0
|
3月前
|
缓存 NoSQL 数据库
go-zero微服务实战系列(五、缓存代码怎么写)
go-zero微服务实战系列(五、缓存代码怎么写)
|
3月前
|
安全 Java Shell
"SpringBoot防窥秘籍大公开!ProGuard混淆+xjar加密,让你的代码穿上隐形斗篷,黑客也无奈!"
【8月更文挑战第11天】开发SpringBoot应用时,保护代码免遭反编译至关重要。本文介绍如何运用ProGuard和xjar强化安全性。ProGuard能混淆代码,去除未使用的部分,压缩字节码,使反编译困难。需配置ProGuard规则文件并处理jar包。xjar则进一步加密jar包内容,即使被解压也无法直接读取。结合使用这两种工具可显著提高代码安全性,有效保护商业机密及知识产权。
319 3
|
3月前
|
程序员 测试技术 Go
用 Go 编写简洁代码的最佳实践
用 Go 编写简洁代码的最佳实践