Golang RSA 加密 解密 簽名 驗簽

非對稱加密 RSA

  • 優(yōu)點

    與對稱加密相比掺出,安全性更好载慈,加解密需要不同的密鑰,公鑰和私鑰都可進行相互的加解密珍手。

  • 缺點

    加密和解密花費時間長办铡、速度慢,只適合對少量數(shù)據(jù)進行加密琳要。

  • 應(yīng)用場景

    適合于對安全性要求很高的場景寡具,適合加密少量數(shù)據(jù),比如支付數(shù)據(jù)稚补、登錄數(shù)據(jù)等童叠。

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)
            //轉(zhuǎn)換格式  類型斷言
            r.rsaPrivateKey = privateKey.(*rsa.PrivateKey)
        }
    }

    if r.publicKey != "" {
        //將公鑰解碼 解析 轉(zhuǎn)換格式
        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轉(zhuǎn)為bytes
    // "-11" 為 PKCS#1 建議的 padding 占用了 11 個字節(jié)
    blockLength := r.rsaPublicKey.N.BitLen()/8 - 11
    //如果明文長度不大于密鑰長度厦坛,可以直接加密
    if len(data) <= blockLength {
        //對明文進行加密
        return rsa.EncryptPKCS1v15(rand.Reader, r.rsaPublicKey, []byte(data))
    }
    //否則分段加密
    //創(chuàng)建一個新的緩沖區(qū)
    buffer := bytes.NewBufferString("")
    pages := len(data) / blockLength //切分為多少塊
    //循環(huán)加密
    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
        }
        //寫入緩沖區(qū)
        buffer.Write(chunk)
    }
    //讀取緩沖區(qū)內(nèi)容并返回五垮,即返回加密結(jié)果
    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++ {//循環(huán)解密
        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) {
    //根據(jù) 隨機源 與 指定位數(shù),生成密鑰對撬碟。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、將其轉(zhuǎn)化為pkcs8格式的密鑰(使用自定義方法)
    //  objPkcs1 := x509.MarshalPKCS1PrivateKey(rsaPrivateKey)
    //  objPkcs8 := r.Pkcs1ToPkcs8(objPkcs1)
    //二:直接使用 x509 包 MarshalPKCS8PrivateKey 生成pkcs8密鑰
    objPkcs8,_ := x509.MarshalPKCS8PrivateKey(rsaPrivateKey)
    //fmt.Println("對比兩種結(jié)果",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 轉(zhuǎn)到 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 驗簽結(jié)果:%v\n",
        hex.EncodeToString(sData),
        string(pData),
        hex.EncodeToString(sign),
        verify,
    )
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惶傻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子其障,更是在濱河造成了極大的恐慌银室,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件静秆,死亡現(xiàn)場離奇詭異粮揉,居然都是意外死亡,警方通過查閱死者的電腦和手機抚笔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門扶认,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人殊橙,你說我怎么就攤上這事辐宾。” “怎么了膨蛮?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵叠纹,是天一觀的道長。 經(jīng)常有香客問我敞葛,道長誉察,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任惹谐,我火速辦了婚禮持偏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氨肌。我一直安慰自己鸿秆,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布怎囚。 她就那樣靜靜地躺著卿叽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上考婴,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天贩虾,我揣著相機與錄音,去河邊找鬼蕉扮。 笑死整胃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的喳钟。 我是一名探鬼主播屁使,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奔则!你這毒婦竟也來了蛮寂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤易茬,失蹤者是張志新(化名)和其女友劉穎酬蹋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抽莱,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡范抓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了食铐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匕垫。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖虐呻,靈堂內(nèi)的尸體忽然破棺而出象泵,到底是詐尸還是另有隱情,我是刑警寧澤斟叼,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布偶惠,位于F島的核電站,受9級特大地震影響朗涩,放射性物質(zhì)發(fā)生泄漏忽孽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一谢床、第九天 我趴在偏房一處隱蔽的房頂上張望兄一。 院中可真熱鬧,春花似錦萤悴、人聲如沸瘾腰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春硝全,著一層夾襖步出監(jiān)牢的瞬間栖雾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工伟众, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留析藕,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓凳厢,卻偏偏與公主長得像账胧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子先紫,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348