29

go/Java 国密sm2签名验签

 4 years ago
source link: https://www.tuicool.com/articles/ueqE73a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

近期go项目对接第三方Java服务,第三方要求使用国密sm3/sm2算法进行数据签名验签,特记录go端开发注意事项

1 关于密钥对

密钥生成可以使用openssl库,openssl版本至少是1.1.1,终端运行 openssl version 检查版本,之前版本不支持sm2/sm3, openssl官网 https://www.openssl.org/source/

检查椭圆曲线是否包含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签名验签失败.

代码示例:

package main

import (
    "bytes"
    "encoding/base64"
    "fmt"
    "github.com/tjfoc/gmsm/sm2"
    "github.com/tjfoc/gmsm/sm3"
    "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)
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK