Golang RSA 加密 解密 签名 验签

非对称加密 RSA

  • 优点

    与对称加密相比,安全性更好,加解密需要不同的密钥,公钥和私钥都可进行相互的加解密。

  • 缺点

    加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

  • 应用场景

    适合于对安全性要求很高的场景,适合加密少量数据,比如支付数据、登录数据等。

package helpers
import (
    "bytes"
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/asn1"
    "encoding/pem"
    "strings"
)

type Rsa struct {
    privateKey    string
    publicKey     string
    rsaPrivateKey *rsa.PrivateKey
    rsaPublicKey  *rsa.PublicKey
}

func NewRsa(publicKey, privateKey string) *Rsa {
    rsaObj := &Rsa{
        privateKey: privateKey,
        publicKey:  publicKey,
    }
    rsaObj.init() //初始化,如果存在公钥私钥,将其解析
    return rsaObj
}

//初始化
func (r *Rsa) init() {
    if r.privateKey != "" {
        //将私钥解码
        block, _ := pem.Decode([]byte(r.privateKey))
        //pkcs1   //判断是否包含 BEGIN RSA 字符串,这个是由下面生成的时候定义的
        if strings.Index(r.privateKey, "BEGIN RSA") > 0 {
            //解析私钥
            r.rsaPrivateKey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
        } else { //pkcs8
            //解析私钥
            privateKey, _ := x509.ParsePKCS8PrivateKey(block.Bytes)
            //转换格式  类型断言
            r.rsaPrivateKey = privateKey.(*rsa.PrivateKey)
        }
    }

    if r.publicKey != "" {
        //将公钥解码 解析 转换格式
        block, _ := pem.Decode([]byte(r.publicKey))
        publicKey, _ := x509.ParsePKIXPublicKey(block.Bytes)
        r.rsaPublicKey = publicKey.(*rsa.PublicKey)
    }
}

//Encrypt 加密
func (r *Rsa) Encrypt(data []byte) ([]byte, error) {
    // blockLength = 密钥长度 = 一次能加密的明文长度
    // "/8" 将bit转为bytes
    // "-11" 为 PKCS#1 建议的 padding 占用了 11 个字节
    blockLength := r.rsaPublicKey.N.BitLen()/8 - 11
    //如果明文长度不大于密钥长度,可以直接加密
    if len(data) <= blockLength {
        //对明文进行加密
        return rsa.EncryptPKCS1v15(rand.Reader, r.rsaPublicKey, []byte(data))
    }
    //否则分段加密
    //创建一个新的缓冲区
    buffer := bytes.NewBufferString("")
    pages := len(data) / blockLength //切分为多少块
    //循环加密
    for i := 0; i <= pages; i++ {
        start := i * blockLength
        end := (i + 1) * blockLength
        if i == pages {//最后一页的判断
            if start == len(data) {
                continue
            }
            end = len(data)
        }
        //分段加密
        chunk, err := rsa.EncryptPKCS1v15(rand.Reader, r.rsaPublicKey, data[start:end])
        if err != nil {
            return nil, err
        }
        //写入缓冲区
        buffer.Write(chunk)
    }
    //读取缓冲区内容并返回,即返回加密结果
    return buffer.Bytes(), nil
}

//Decrypt 解密
func (r *Rsa) Decrypt(data []byte) ([]byte, error) {
    //加密后的密文长度=密钥长度。如果密文长度大于密钥长度,说明密文非一次加密形成
    //1、获取密钥长度
    blockLength := r.rsaPublicKey.N.BitLen() / 8
    if len(data) <= blockLength {//一次形成的密文直接解密
        return rsa.DecryptPKCS1v15(rand.Reader, r.rsaPrivateKey, data)
    }

    buffer := bytes.NewBufferString("")
    pages := len(data) / blockLength
    for i := 0; i <= pages; i++ {//循环解密
        start := i * blockLength
        end := (i + 1) * blockLength
        if i == pages {
            if start == len(data) {
                continue
            }
            end = len(data)
        }
        chunk, err := rsa.DecryptPKCS1v15(rand.Reader, r.rsaPrivateKey, data[start:end])
        if err != nil {
            return nil, err
        }
        buffer.Write(chunk)
    }
    return buffer.Bytes(), nil
}

//Sign 签名
func (r *Rsa) Sign(data []byte, sHash crypto.Hash) ([]byte, error) {
    hash := sHash.New()
    hash.Write(data)
    sign, err := rsa.SignPKCS1v15(rand.Reader, r.rsaPrivateKey, sHash, hash.Sum(nil))
    if err != nil {
        return nil, err
    }
    return sign, nil
}

//Verify 验签
func (r *Rsa) Verify(data []byte, sign []byte, sHash crypto.Hash) bool {
    h := sHash.New()
    h.Write(data)
    return rsa.VerifyPKCS1v15(r.rsaPublicKey, sHash, h.Sum(nil), sign) == nil
}

//CreateKeys 生成pkcs1 格式的公钥私钥
func (r *Rsa) CreateKeys(keyLength int) (privateKey, publicKey string) {
    //根据 随机源 与 指定位数,生成密钥对。rand.Reader = 密码强大的伪随机生成器的全球共享实例
    rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, keyLength)
    if err != nil {
        return
    }
    //编码私钥
    privateKey = string(pem.EncodeToMemory(&pem.Block{
        Type:  "RSA PRIVATE KEY",//自定义类型
        Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivateKey),
    }))
    //编码公钥
    objPkix, err := x509.MarshalPKIXPublicKey(&rsaPrivateKey.PublicKey)
    if err != nil {
        return
    }
    publicKey = string(pem.EncodeToMemory(&pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: objPkix,
    }))
    return
}

//CreatePkcs8Keys 生成pkcs8 格式公钥私钥
func (r *Rsa) CreatePkcs8Keys(keyLength int) (privateKey, publicKey string) {
    rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, keyLength)
    if err != nil {
        return
    }
    //两种方式
    //一:1、生成pkcs1格式的密钥 2、将其转化为pkcs8格式的密钥(使用自定义方法)
    //  objPkcs1 := x509.MarshalPKCS1PrivateKey(rsaPrivateKey)
    //  objPkcs8 := r.Pkcs1ToPkcs8(objPkcs1)
    //二:直接使用 x509 包 MarshalPKCS8PrivateKey 生成pkcs8密钥
    objPkcs8,_ := x509.MarshalPKCS8PrivateKey(rsaPrivateKey)
    //fmt.Println("对比两种结果",strings.Compare(string(objPkcs8),string(rr)))

    privateKey = string(pem.EncodeToMemory(&pem.Block{
        Type:  "PRIVATE KEY",
        Bytes: objPkcs8,
    }))

    objPkix, err := x509.MarshalPKIXPublicKey(&rsaPrivateKey.PublicKey)
    if err != nil {
        return
    }

    publicKey = string(pem.EncodeToMemory(&pem.Block{
        Type:  "PUBLIC KEY",
        Bytes: objPkix,
    }))
    return
}

//Pkcs1ToPkcs8 将pkcs1 转到 pkcs8 自定义
func (r *Rsa) Pkcs1ToPkcs8(key []byte) []byte {
    info := struct {
        Version             int
        PrivateKeyAlgorithm []asn1.ObjectIdentifier
        PrivateKey          []byte
    }{}
    info.Version = 0
    info.PrivateKeyAlgorithm = make([]asn1.ObjectIdentifier, 1)
    info.PrivateKeyAlgorithm[0] = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
    info.PrivateKey = key
    k, _ := asn1.Marshal(info)
    return k
}

使用

func testRsa()  {
       // 生成一段string
    data := strings.Repeat("H", 245)+"q"

    //生成 公钥 私钥
    privateKey, publicKey := helpers.NewRsa("", "").CreatePkcs8Keys(2048)
    //privateKey, publicKey := helpers.NewRsa("", "").CreateKeys(1024)
    fmt.Printf("公钥:%v \n 私钥: %v \n", publicKey, privateKey)

    rsaObj := helpers.NewRsa(publicKey, privateKey)
    //加密
    sData, err := rsaObj.Encrypt([]byte(data))
    if err != nil {
        fmt.Println("加密失败:", err)
    }
    //解密
    pData, err := rsaObj.Decrypt(sData)
    if err != nil {
        fmt.Println("解密失败:", err)
    }
        //签名
    sign, _ := rsaObj.Sign([]byte(data), crypto.SHA256)
        //验签
    verify := rsaObj.Verify([]byte(data), sign, crypto.SHA256)
    fmt.Printf(" 加密:%v\n 解密:%v\n 签名:%v\n 验签结果:%v\n",
        hex.EncodeToString(sData),
        string(pData),
        hex.EncodeToString(sign),
        verify,
    )
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356