Golang加密系列之RSA

君子藏锋 · · 13759 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang加密系列的最后一篇,嗯,RSA涉及的概念太多,弄了好久才搞清楚。。。

代码的结构如下图

PS:StarUML这玩意在Mac上所有连到Interface的线都变成直线了...我很惆怅...


定义一个对外开放的接口

package rsa

import "crypto"

type Cipher interface {
   Encrypt(plaintext []byte) ([]byte, error)
   Decrypt(ciphertext []byte) ([]byte, error)
   Sign(src []byte, hash crypto.Hash) ([]byte, error)
   Verify(src []byte, sign []byte, hash crypto.Hash) error
}

定义私有的pkcsClient ,实现Cipher接口, PKCS格式的私钥都使用这个client

package rsa

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

type pkcsClient struct {
   privateKey *rsa.PrivateKey
   publicKey  *rsa.PublicKey
}

func (this *pkcsClient) Encrypt(plaintext []byte) ([]byte, error) {
   return rsa.EncryptPKCS1v15(rand.Reader, this.publicKey, plaintext)
}
func (this *pkcsClient) Decrypt(ciphertext []byte) ([]byte, error) {
   return rsa.DecryptPKCS1v15(rand.Reader, this.privateKey, ciphertext)
}

func (this *pkcsClient) Sign(src []byte, hash crypto.Hash) ([]byte, error) {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.SignPKCS1v15(rand.Reader, this.privateKey, hash, hashed)
}

func (this *pkcsClient) Verify(src []byte, sign []byte, hash crypto.Hash) error {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.VerifyPKCS1v15(this.publicKey, hash, hashed, sign)
}

将私钥类型定义成枚举类型

package privatekey

type Type int64

const (
   PKCS1 Type = iota
   PKCS8
)

定义一个New/NewDefault函数,用于创建client

package rsa

import (
   "crypto/rsa"
   "crypto/x509"
   "encoding/pem"
   "errors"
   "toast/rsa/privatekey"
)

//默认客户端,pkcs8私钥格式,pem编码
func NewDefault(privateKey, publicKey string) (Cipher, error) {
   blockPri, _ := pem.Decode([]byte(privateKey))
   if blockPri == nil {
      return nil, errors.New("private key error")
   }

   blockPub, _ := pem.Decode([]byte(publicKey))
   if blockPub == nil {
      return nil, errors.New("public key error")
   }

   return New(blockPri.Bytes, blockPub.Bytes, privatekey.PKCS8)
}

func New(privateKey, publicKey []byte, privateKeyType privatekey.Type) (Cipher, error) {

   priKey, err := genPriKey(privateKey, privateKeyType)
   if err != nil {
      return nil, err
   }
   pubKey, err := genPubKey(publicKey)
   if err != nil {
      return nil, err
   }
   return &pkcsClient{privateKey: priKey, publicKey: pubKey}, nil
}

func genPubKey(publicKey []byte) (*rsa.PublicKey, error) {
   pub, err := x509.ParsePKIXPublicKey(publicKey)
   if err != nil {
      return nil, err
   }
   return pub.(*rsa.PublicKey), nil
}

func genPriKey(privateKey []byte, privateKeyType privatekey.Type) (*rsa.PrivateKey, error) {
   var priKey *rsa.PrivateKey
   var err error
   switch privateKeyType {
   case privatekey.PKCS1:
      {
         priKey, err = x509.ParsePKCS1PrivateKey([]byte(privateKey))
         if err != nil {
            return nil, err
         }
      }
   case privatekey.PKCS8:
      {
         prkI, err := x509.ParsePKCS8PrivateKey([]byte(privateKey))
         if err != nil {
            return nil, err
         }
         priKey = prkI.(*rsa.PrivateKey)
      }
   default:
      {
         return nil, errors.New("unsupport private key type")
      }
   }
   return priKey, nil
}

最后,看看如何使用上面的代码来进行加密/解密,签名验签

package rsa_test

import (
   "crypto"
   "encoding/base64"
   "encoding/hex"
   "fmt"
   "testing"
   "toast/rsa"
)

var cipher rsa.Cipher

func init() {
   client, err := rsa.NewDefault(`-----BEGIN PRIVATE KEY-----
私钥信息
-----END PRIVATE KEY-----`, `-----BEGIN PUBLIC KEY-----
公钥信息
-----END PUBLIC KEY-----`)

   if err != nil {
      fmt.Println(err)
   }

   cipher = client
}

func Test_DefaultClient(t *testing.T) {

   cp, err := cipher.Encrypt([]byte("测试加密解密"))
   if err != nil {
      t.Error(err)
   }
   cpStr := base64.URLEncoding.EncodeToString(cp)

   fmt.Println(cpStr)

   ppBy, err := base64.URLEncoding.DecodeString(cpStr)
   if err != nil {
      t.Error(err)
   }
   pp, err := cipher.Decrypt(ppBy)

   fmt.Println(string(pp))
}

func Test_Sign_DefaultClient(t *testing.T) {

   src := "测试签名验签"

   signBytes, err := cipher.Sign([]byte(src), crypto.SHA256)
   if err != nil {
      t.Error(err)
   }
   sign := hex.EncodeToString(signBytes)
   fmt.Println(sign)

   signB, err := hex.DecodeString(sign)

   errV := cipher.Verify([]byte(src), signB, crypto.SHA256)
   if errV != nil {
      t.Error(errV)
   }
   fmt.Println("verify success")
}

关于RSA相关的一些概念,参见我的另一篇博客.pem引发的血案

这里还有一个已经编写好的AES/RSA加解密的包,可以直接引用,github地址:https://github.com/89hmdys/toast

本文来自:开源中国博客

感谢作者:君子藏锋

查看原文:Golang加密系列之RSA

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

13759 次点击  ∙  2 赞  
加入收藏 微博
被以下专栏收入,发现更多相似内容
2 回复  |  直到 2017-11-06 09:22:51
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传