1. 程式人生 > >golang中橢圓曲線密碼

golang中橢圓曲線密碼

簡介
橢圓曲線密碼學(英語:Elliptic curve cryptography,縮寫為 ECC),一種建立公開金鑰加密的演算法,基於橢圓曲線數學。橢圓曲線在密碼學中的使用是在1985年由Neal Koblitz和Victor Miller分別獨立提出的。

ECC的主要優勢是在某些情況下它比其他的方法使用更小的金鑰——比如RSA加密演算法——提供相當的或更高等級的安全。

橢圓曲線密碼學的許多形式有稍微的不同,所有的都依賴於被廣泛承認的解決橢圓曲線離散對數問題的困難性上.

數學原理
不管是RSA還是ECC或者其它,公鑰加密演算法都是依賴於某個正向計算很簡單(多項式時間複雜度),而逆向計算很難(指數級時間複雜度)的數學問題。

橢圓曲線依賴的數學難題是:

k為正整數,P是橢圓曲線上的點(稱為基點), k*P=Q , 已知q和P,很難計算出k
金鑰生成
golang 標準包中直接提供了金鑰生成的方法

// 初始化橢圓曲線
pubkeyCurve := elliptic.P256()
// 隨機挑選基點,生成私鑰
p, err := ecdsa.GenerateKey(pubkeyCurve, rand.Reader)
if err != nil {
    fmt.Println("1", err)
    os.Exit(1)
}
PRIVATE = p

加密和解密
golang標準包中沒有提供加密和解密演算法,但是以太坊go-ethereum實現了相關演算法,這裡對其進行二次封裝

// 將標準包生成私鑰轉化為ecies私鑰
prv2 = ecies.ImportECDSA(PRIVATE)
// ecc(ecies)加密
func ECCEncrypt(pt []byte) ([]byte, error) {
    ct, err := ecies.Encrypt(rand.Reader, &prv2.PublicKey, pt, nil, nil)
    return ct, err
}
// ecc(ecies)解密
func ECCDecrypt(ct []byte) ([]byte, error) {
    pt, err := prv2.Decrypt(ct, nil, nil)
    return pt, err
}

簽名和驗籤
golang標準包中提供了相關方法

func EccSign(pt []byte) (sign []byte, err error) {
    // 根據明文plaintext和私鑰,生成兩個big.Ing
    r, s, err := ecdsa.Sign(rand.Reader, PRIVATE, pt)
    if err != nil {
        fmt.Println(err)
        return nil, err
    }
    rs, err := r.MarshalText()
    if err != nil {
        return nil, err
    }
    ss, err := s.MarshalText()
    if err != nil {
        return nil, err
    }
    // 將r,s合併(以“+”分割),作為簽名返回
    var b bytes.Buffer
    b.Write(rs)
    b.Write([]byte(`+`))
    b.Write(ss)
    return b.Bytes(), nil
}

func EccSignVer(pt, sign []byte) bool {
    var rint, sint big.Int
    // 根據sign,解析出r,s
    rs := bytes.Split(sign, []byte("+"))
    rint.UnmarshalText(rs[0])
    sint.UnmarshalText(rs[1])
    // 根據公鑰,明文,r,s驗證簽名
    v := ecdsa.Verify(&PRIVATE.PublicKey, pt, &rint, &sint)
    return v
}

ECC,ECIES,ECDSA,ECDH 是什麼關係,有哪些區別?
ECC,全稱橢圓曲線密碼學(英語:Elliptic curve cryptography,縮寫為 ECC),主要是指相關數學原理
ECIES,在ECC原理的基礎上實現的一種公鑰加密方法,和RSA類似
ECDSA,在ECC原理上實現的簽名方法
ECDH在ECC和DH的基礎上實現的金鑰交換演算法
RSA演算法的金鑰對可以通過.pem檔案來儲存,ECC的金鑰對要如何儲存呢?
go-ethereum提供了兩種對外介面.

金鑰對和[]byte之間的轉換

// 私鑰 -> []byte
// FromECDSA exports a private key into a binary dump.
func FromECDSA(priv *ecdsa.PrivateKey) []byte {
    if priv == nil {
        return nil
    }
    return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}

// []byte -> 私鑰
// ToECDSA creates a private key with the given D value.
func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
    return toECDSA(d, true)
}

// 公鑰 -> []byte
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
    if pub == nil || pub.X == nil || pub.Y == nil {
        return nil
    }
    return elliptic.Marshal(S256(), pub.X, pub.Y)
}

// []byte -> 公鑰
    func ToECDSAPub(pub []byte) *ecdsa.PublicKey {
    if len(pub) == 0 {
        return nil
    }
    x, y := elliptic.Unmarshal(S256(), pub)
    return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}
}

公鑰轉換的時候需要注意,eth中預設使用的橢圓曲線是S256().生成金鑰對時,如果使用的是其他橢圓曲線(如P256),需要對FromECDSAPub,ToECDSAPub稍作修改

金鑰對和檔案之間的轉換,其實就是在中間加一層hex編碼

// 私鑰 -> 檔案
// SaveECDSA saves a secp256k1 private key to the given file with
// restrictive permissions. The key data is saved hex-encoded.
func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
    k := hex.EncodeToString(FromECDSA(key))
    return ioutil.WriteFile(file, []byte(k), 0600)
}

// 檔案 -> 私鑰
func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
    buf := make([]byte, 64)
    fd, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    defer fd.Close()
    if _, err := io.ReadFull(fd, buf); err != nil {
        return nil, err
    }

    key, err := hex.DecodeString(string(buf))
    if err != nil {
        return nil, err
    }
    return ToECDSA(key)
}