溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)

發(fā)布時間:2020-07-05 08:50:46 來源:網(wǎng)絡 閱讀:12677 作者:莫名2013 欄目:安全技術(shù)

  對稱加密中,加密和解密使用相同的密鑰,因此必須向解密者配送密鑰,即密鑰配送問題。而非對稱加密中,由于加密和解密分別使用公鑰和私鑰,而公鑰是公開的,因此可以規(guī)避密鑰配送問題。非對稱加密算法,也稱公鑰加密算法。
?
  1977年,Ron Rivest、Adi Shamir、Leonard Adleman三人在美國公布了一種公鑰加密算法,即RSA公鑰加密算法。RSA是目前最有影響力和最常用的公鑰加密算法,可以說是公鑰加密算法的事實標準。
?

RSA加密原理

?
  使用M和C分別表示明文和密文,則RSA加密、解密過程如下:
?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?
  其中e、n的組合(e, n)即為公鑰,d、n的組合(d, n)即為私鑰。當然e、d、n并非任意取值,需要符合一定條件,如下即為e、d、n的求解過程。
?

生成密鑰對

?
  e、d、n的求解過程,也即生成密鑰對的過程。涉及如下步驟:
  1、取兩個大質(zhì)數(shù)(也稱素數(shù))p、q,n = pq。
  2、取正整數(shù)e、d,使得ed mod (p-1)(q-1) = 1,也即:ed ≡ 1 mod (p-1)(q-1)。
  e和d是模(p-1)(q-1)的乘法逆元,僅當e與(p-1)(q-1)互質(zhì)時,存在d。
?
  舉例驗證:
  1、取p、q分別為13、17,n = pq = 221。
  2、而(p-1)(q-1) = 12x16 = 192,取e、d分別為13、133,有13x133 mod 192 = 1
  取明文M = 60,公鑰加密、私鑰解密,加密和解密過程分別如下:
?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?

RSA加密原理證明過程

?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?

手動求解密鑰對中的d

?
  ed mod (p-1)(q-1) = 1,已知e和(p-1)(q-1)求d,即求e對模(p-1)(q-1)的乘法逆元。
  如上面例子中,p、q為13、17,(p-1)(q-1)=192,取e=13,求13d mod 192 = 1中的d。
?
  13d ≡ 1 (mod 192),在右側(cè)添加192的倍數(shù),使計算結(jié)果可以被13整除。
  13d ≡ 1 + 192x9 ≡ 13x133 (mod 192),因此d = 133
?
  其他計算方法有:費馬小定律、擴展歐幾里得算法、歐拉定理。
?

RSA安全性

?
  由于公鑰公開,即e、n公開。
  因此破解RSA私鑰,即為已知e、n情況下求d。
  因ed mod (p-1)(q-1) = 1,且n=pq,因此該問題演變?yōu)椋簩質(zhì)因數(shù)分解求p、q。
?
  目前已被證明,已知e、n求d和對n質(zhì)因數(shù)分解求p、q兩者是等價的。實際中n長度為2048位以上,而當n>200位時分解n是非常困難的,因此RSA算法目前仍被認為是安全實用的。
?

RSA計時***和防范

?
  RSA解密的本質(zhì)是模冪運算,即:
?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?
  其中C為密文,(d,n)為私鑰,均為超過1024位的大數(shù)運算,直接計算并不可行,因此最經(jīng)典的算法為蒙哥馬利算法。而這種計算是比較是耗時的,因此***者可以觀察不同的輸入對應的解密時間,通過分析推斷私鑰,稱為計時***。而防范RSA計時***的辦法,即在解密時加入隨機因素,使得***者無法準確獲取解密時間。
?
  具體實現(xiàn)步驟如下:
?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?

go標準庫中的RSA加解密實現(xiàn)

?
  go標準庫中解密即實現(xiàn)了對計時***的防范,代碼如下:
?

//加密
//m為明文
//(pub.E, pub.N)為公鑰
//c為密文
func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
    e := big.NewInt(int64(pub.E))
    c.Exp(m, e, pub.N)
    return c
}

//解密
//傳入random支持防范計時***
func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) {
    if c.Cmp(priv.N) > 0 {
        err = ErrDecryption
        return
    }
    if priv.N.Sign() == 0 {
        return nil, ErrDecryption
    }

    var ir *big.Int
    if random != nil {
        var r *big.Int

        for {
            //步驟1產(chǎn)生0至n-1之間隨機數(shù)r
            r, err = rand.Int(random, priv.N)
            if err != nil {
                return
            }
            if r.Cmp(bigZero) == 0 {
                r = bigOne
            }
            var ok bool
            //r的模n的乘法逆元ir,步驟4中使用
            ir, ok = modInverse(r, priv.N)
            if ok {
                break
            }
        }
        bigE := big.NewInt(int64(priv.E))
        //計算步驟2中C'
        rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0
        cCopy := new(big.Int).Set(c)
        cCopy.Mul(cCopy, rpowe)
        cCopy.Mod(cCopy, priv.N)
        c = cCopy
    }

    if priv.Precomputed.Dp == nil {
        //步驟3,使用C'計算對應的M'
        m = new(big.Int).Exp(c, priv.D, priv.N)
    } else {
        //略
    }

    if ir != nil {
        //步驟4計算實際的M
        m.Mul(m, ir)
        m.Mod(m, priv.N)
    }

    return
}
//代碼位置src/crypto/rsa/rsa.go

RSA簽名和驗簽的原理

?
  非對稱加密算法,除支持加密外,還可以實現(xiàn)簽名。原理如下:
?
  簽名:
  1、提取消息摘要,使用發(fā)送方私鑰對消息摘要加密,生成消息簽名。
  2、將消息簽名和消息一起,使用接收方公鑰加密,獲得密文。
?
  驗簽:
  1、使用接收方私鑰對密文解密,獲得消息和消息簽名。
  2、使用發(fā)送方公鑰解密消息簽名,獲得消息摘要。
  3、使用相同辦法重新提取消息摘要,與上一步中消息摘要對比,如相同則驗簽成功。
?
  附示意圖如下:
?
RSA加解密及簽名算法的技術(shù)原理及其Go語言實現(xiàn)
?

go標準庫中的RSA簽名實現(xiàn)

?

//簽名
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
    //哈希提取消息摘要
    hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
    if err != nil {
        return nil, err
    }

    tLen := len(prefix) + hashLen
    k := (priv.N.BitLen() + 7) / 8
    if k < tLen+11 {
        return nil, ErrMessageTooLong
    }

    // EM = 0x00 || 0x01 || PS || 0x00 || T
    em := make([]byte, k)
    em[1] = 1
    for i := 2; i < k-tLen-1; i++ {
        em[i] = 0xff
    }
    //整合消息摘要和消息體
    copy(em[k-tLen:k-hashLen], prefix)
    copy(em[k-hashLen:k], hashed)

    m := new(big.Int).SetBytes(em)
    //使用發(fā)送方私鑰加密消息摘要和消息體,即為簽名
    c, err := decryptAndCheck(rand, priv, m)
    if err != nil {
        return nil, err
    }

    copyWithLeftPad(em, c.Bytes())
    return em, nil
}

//驗證簽名
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
    //哈希提取消息摘要
    hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
    if err != nil {
        return err
    }

    tLen := len(prefix) + hashLen
    k := (pub.N.BitLen() + 7) / 8
    if k < tLen+11 {
        return ErrVerification
    }

    c := new(big.Int).SetBytes(sig)
    //使用發(fā)送方公鑰解密,提取消息體和消息簽名
    m := encrypt(new(big.Int), pub, c)
    em := leftPad(m.Bytes(), k)
    // EM = 0x00 || 0x01 || PS || 0x00 || T

    //對比發(fā)送方和接收方消息體、以及消息簽名
    ok := subtle.ConstantTimeByteEq(em[0], 0)
    ok &= subtle.ConstantTimeByteEq(em[1], 1)
    ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed)
    ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix)
    ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0)

    for i := 2; i < k-tLen-1; i++ {
        ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
    }

    if ok != 1 {
        return ErrVerification
    }

    return nil
}
//代碼位置src/crypto/rsa/pkcs1v15.go

?

后記

?
  RSA算法中使用了大量數(shù)論知識,有關(guān)數(shù)論知識還有待學習。待續(xù)。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI