go/Java 國密sm2簽名驗籤

近期go項目對接第三方Java服務,第三方要求使用國密sm3/sm2算法進行數據簽名驗籤,特記錄go端開發注意事項git

1 關於密鑰對

密鑰生成可使用openssl庫,openssl版本至少是1.1.1,終端運行openssl version檢查版本,以前版本不支持sm2/sm3, openssl官網https://www.openssl.org/source/github

檢查橢圓曲線是否包含sm2,以下所示是支持sm2的。算法

$ openssl ecparam -list_curves | grep SM2

  SM2       : SM2 curve over a 256 bit prime field

密鑰生成流程,存在第四步的緣由是go使用的庫須要讀取pkcs#8格式私鑰pem文件:函數

1 生成sm2私鑰: openssl ecparam -genkey -name SM2 -out sm2PriKey.pem
2 sm2私鑰導出公鑰: openssl ec -in sm2PriKey.pem -pubout -out sm2PubKey.pem
3 查看私鑰: openssl ec -in sm2PriKey.pem -text
4 私鑰pkcs#1轉pkcs#8: openssl pkcs8 -topk8 -inform PEM -in sm2PriKey.pem -outform pem -nocrypt -out sm2PriKeyPkcs8.pem

2 go簽名驗籤代碼

1 讀取公鑰pem文件, 將公鑰X/Y的bytes拼接base64編碼,函數示例:
func DealPubKey() {
    // 公鑰X/Y的bytes拼接後base64編碼
    PubKeyPath := "/home/xx/sm2PubKey.pem"
    pubKey, e := sm2.ReadPublicKeyFromPem(PubKeyPath, nil)

    if e != nil {
        log.Println("pubKeyPem read failed, error: ", e)
    }

    var buf bytes.Buffer
    buf.Write(pubKey.X.Bytes())
    buf.Write(pubKey.Y.Bytes())

    XY := base64.StdEncoding.EncodeToString(buf.Bytes())
    log.Println("pubKey XY base64--->", XY)
}
2 sm2簽名驗籤使用的包"github.com/tjfoc/gmsm/sm2", go mod自動安裝不會安裝最新版本, go.mod不要添加此包,
github最新版本是是V1.2, 切換到項目目錄, 終端運行 go get github.com/tjfoc/gmsm@master安裝最新版本後會自動添加到go.mod
3 "github.com/tjfoc/gmsm/sm2"使用簽名函數爲 sm2.Sm2Sign, 此函數會將輸入的[]bytes類型數據sm3摘要運算後進行sm2簽名, 對應驗籤函數sm2.Sm2Verify
4 函數sm2.Sign默認是使用sha-512進行摘要運算,與服務端Java簽名驗籤失敗.

代碼示例:ui

package main

import (
    "bytes"
    "encoding/base64"
    "fmt"
    "github.com/tjfoc/gmsm/sm2"
    "log"
    "math/big"
    "os"
)
var (
    default_uid = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
)

func Sign(body string) (string, error) {
    cwd, _ := os.Getwd()
    PriKeyPath := cwd + string(os.PathSeparator) + "sm2PriKeyPkcs8.pem"

    priKey, e := sm2.ReadPrivateKeyFromPem(PriKeyPath, nil)
    if e != nil {
        log.Println("priKeyPem read failed, error: ", e)
        return "", e
    }

    r, s, err := sm2.Sm2Sign(priKey, []byte(body), default_uid)
    if err != nil {
        log.Println("priKey sign error: ", err)
        return "", err
    }

    //Buffer是一個實現了讀寫方法的可變大小的字節緩衝
    var buffer bytes.Buffer
    buffer.Write(r.Bytes())
    buffer.Write(s.Bytes())

    signature := base64.StdEncoding.EncodeToString(buffer.Bytes())
    log.Println("priKey signature base64: ", signature)
    return signature, nil
}

func Verify(body, signature string) {
    cwd, _ := os.Getwd()
    PubKeyPath := cwd + string(os.PathSeparator) + "sm2PubKey.pem"

    pubKey, e := sm2.ReadPublicKeyFromPem(PubKeyPath, nil)

    if e != nil {
        log.Println("pubKeyPem read failed, error: ", e)
    }

    d64, err := base64.StdEncoding.DecodeString(signature)
    if err != nil {
        log.Println("base64 decode error: ", err)
    }

    l := len(d64)
    br := d64[:l/2]
    bs := d64[l/2:]

    var ri, si big.Int
    r := ri.SetBytes(br)
    s := si.SetBytes(bs)
    v := sm2.Sm2Verify(pubKey, []byte(body), default_uid, r, s)
    log.Printf("pubKey verified: %v\n", v)
}

func main() {
    body := `{"name":"mike","gender":"male"}`
    signature, _ := Sign(body)
    Verify(body, signature)
}
相關文章
相關標籤/搜索