一尘不染

Golang令牌验证错误

go

我需要验证一个Google id_token,其中一个步骤涉及检查令牌签名。

首先,我从以下网址获取证书:https
:
//www.googleapis.com/oauth2/v2/certs,并从证书中提取模数(n)和指数(e)并生成公钥,然后分解令牌(标题,有效负载和摘要),然后将解码后的内容header.payload与Google
pKey +摘要一起发送到rsa函数rsa.VerifyPKCS1v15

我陷入了这个验证错误: crypto/rsa: verification error

这是代码(我用注释掉了部分代码// validation here fails):

func ValidateIDToken(auth_token string) (err error){    
    res, err := http.Get("https://www.googleapis.com/oauth2/v2/certs")
    if err != nil {
        log.Fatal(err)
        return err
    }

    certs, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        log.Fatal(err)
        return err
    }

    //get modulus and exponent from the cert
    var goCertificate interface{}

    err = json.Unmarshal(certs, &goCertificate)    
    k := goCertificate.(map[string]interface{})["keys"]
    j := k.([]interface{})
    x := j[1]
    h := x.(map[string]interface{})["n"]
    g := x.(map[string]interface{})["e"]
    e64 := base64.StdEncoding
    //build the google pub key
    nStr := h.(string)
    decN, err := base64.StdEncoding.DecodeString(nStr)
    if err != nil {
        log.Println(err)
        return
    }

    n := big.NewInt(0)
    n.SetBytes(decN)
    eStr := g.(string)
    decE, err := base64.StdEncoding.DecodeString(eStr)
    if err != nil {
        log.Println(err)
        return
    }

    var eBytes []byte
    if len(decE) < 8 {
        eBytes = make([]byte, 8-len(decE), 8)
        eBytes = append(eBytes, decE...)
    } else {
        eBytes = decE
    }

    eReader := bytes.NewReader(eBytes)
    var e uint64
    err = binary.Read(eReader, binary.BigEndian, &e)
    if err != nil {
        log.Println(err)
        return
    }

    pKey := rsa.PublicKey{N: n, E: int(e)}
    w := strings.SplitAfter(auth_token, ".")    
    for i, val := range w {
        w[i] = strings.Trim(val, ".")
    }
    y := w[0:2]

    //Join just the first two parts, the header and the payload without the signature
    o := strings.Join(y, ".")   
    headerOauth := DecodeB64(nil,[]byte(w[0]),e64)
    inblockOauth := DecodeB64(nil,[]byte(w[1]),e64)
    toHash := string(headerOauth) + "}." + string(inblockOauth)
    digestOauth := DecodeB64(nil, []byte(w[2]),e64)

    hasherOauth := sha256.New()
    hasherOauth.Write([]byte(toHash))

    // validation here fails
    err = rsa.VerifyPKCS1v15(&pKey,crypto.SHA256,hasherOauth.Sum(nil),digestOauth)

    if err != nil {
        log.Printf("Error verifying key %s",err.Error())
        return err
    }
    return err
}

更新1: 这是toHash var,其中包含标头和有效负载:

{"alg":"RS256","kid":"d91c503452d0f8849200a321ffbf7dea76f9371d"}.{"iss":"accounts.google.com","sub":"104869993929250743503","azp":"client_email_till_@.apps.googleusercontent.com","email":"test@test.hr","at_hash":"KAm1M0g-ssMkdjds7jkbVQ","email_verified":true,"aud":client_email_till_@.apps.googleusercontent.com","hd":"test.hr","iat":1412246551,"exp":1412250451}

更新2: 感谢@Florent
Morselli的回复,我再次尝试了一次,但失败了,这次我仅对第三个部分(签名)进行了B64解码,但错误仍然存​​在,有人可以用auth_token对其进行测试,只需放入ID令牌在下面代码的auth_token变量中,让我知道它是否有效,谢谢。

package main
import(
    "strings"
    "encoding/binary"
    "errors"
    "fmt"
    "log"
    "encoding/base64"
    "io/ioutil"
    "crypto"    
    "crypto/sha256"
    "crypto/rsa"
    "bytes"
    "encoding/json"
    "net/http"
    "math/big"

)

func main() {
    auth_token := ""
    w := strings.SplitAfter(auth_token, ".")    
    for i, val := range w {
        w[i] = strings.Trim(val, ".")
    }
    headerOauth, err := base64.URLEncoding.DecodeString(w[0])

    res, err := http.Get("https://www.googleapis.com/oauth2/v2/certs")
    if err != nil {
        fmt.Println(err)
    }

    certs, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        fmt.Println(err)
    }

    //extract kid from token header
    var header interface{}

    err = json.Unmarshal([]byte(string(headerOauth)+"}"), &header)

    token_kid := header.(map[string]interface{})["kid"]
    fmt.Println("By 1")
    //get modulus and exponent from the cert

    var goCertificate interface{}

    err = json.Unmarshal(certs, &goCertificate)

    //k := goCertificate.(map[string]interface{})[token_kid.(string)]
    k := goCertificate.(map[string]interface{})["keys"]

    ///*mod & exp part
    j := k.([]interface{})
    x := j[0]

    if j[0].(map[string]interface{})["kid"] == token_kid {
        x = j[0]
    }else{
        if j[1].(map[string]interface{})["kid"] == token_kid {
            x = j[1]
        }else{
            errors.New("Token is not valid, kid from token and certificate don't match")

        }
    }
    h := x.(map[string]interface{})["n"]
    g := x.(map[string]interface{})["e"]

    //build the google pub key
    nStr := h.(string)
    decN, err := base64.URLEncoding.DecodeString(nStr)
    if err != nil {
        fmt.Println(err)
        return
    }

    n := big.NewInt(0)
    n.SetBytes(decN)
    eStr := g.(string)
    decE, err := base64.URLEncoding.DecodeString(eStr)
    if err != nil {
        fmt.Println(err)
        return
    }

    var eBytes []byte
    if len(decE) < 8 {
        eBytes = make([]byte, 8-len(decE), 8)
        eBytes = append(eBytes, decE...)
    } else {
        eBytes = decE
    }

    eReader := bytes.NewReader(eBytes)
    var e uint64
    err = binary.Read(eReader, binary.BigEndian, &e)
    if err != nil {
        log.Println(err)
        return
    }

    pKey := rsa.PublicKey{N: n, E: int(e)}
        //inblockOauth := base64.URLEncoding.DecodeString(w[1])
    toHash := w[0] + "." + w[1]
    digestOauth, err := base64.URLEncoding.DecodeString(w[2])

    hasherOauth := sha256.New()
    hasherOauth.Write([]byte(toHash))

    // verification here fails
    err = rsa.VerifyPKCS1v15(&pKey,crypto.SHA256,hasherOauth.Sum(nil),digestOauth)

    if err != nil {
        fmt.Printf("Error verifying key %s",err.Error())

    }

}

阅读 235

收藏
2020-07-02

共1个答案

一尘不染

正如聊天中所解释的那样,问题在于,如果缺少标头和签名,则Base64解码器无法解码标头和签名。

您只需添加以下代码即可:

    if m := len(h_) % 4; m != 0 {
        h_ += strings.Repeat("=", 4-m)
    }

这是完整的代码:

package main
import(
    "strings"
    "encoding/binary"
    "errors"
    "fmt"
    "log"
    "encoding/base64"
    "io/ioutil"
    "crypto"    
    "crypto/sha256"
    "crypto/rsa"
    "bytes"
    "encoding/json"
    "net/http"
    "math/big"

)

func main() {
    auth_token := ""
    w := strings.Split(auth_token, ".")    
    h_, s_ := w[0], w[2]

    if m := len(h_) % 4; m != 0 {
        h_ += strings.Repeat("=", 4-m)
    }
    if m := len(s_) % 4; m != 0 {
        s_ += strings.Repeat("=", 4-m)
    }

    headerOauth, err := base64.URLEncoding.DecodeString(h_)

    res, err := http.Get("https://www.googleapis.com/oauth2/v2/certs")
    if err != nil {
        fmt.Println(err)
    }

    certs, err := ioutil.ReadAll(res.Body)
    res.Body.Close()
    if err != nil {
        fmt.Println(err)
    }

    //extract kid from token header
    var header interface{}

    err = json.Unmarshal([]byte(string(headerOauth)), &header)

    token_kid := header.(map[string]interface{})["kid"]
    fmt.Println("By 1")
    //get modulus and exponent from the cert

    var goCertificate interface{}

    err = json.Unmarshal(certs, &goCertificate)

    //k := goCertificate.(map[string]interface{})[token_kid.(string)]
    k := goCertificate.(map[string]interface{})["keys"]

    ///*mod & exp part
    j := k.([]interface{})
    x := j[0]

    if j[0].(map[string]interface{})["kid"] == token_kid {
        x = j[0]
    }else{
        if j[1].(map[string]interface{})["kid"] == token_kid {
            x = j[1]
        }else{
            errors.New("Token is not valid, kid from token and certificate don't match")

        }
    }
    h := x.(map[string]interface{})["n"]
    g := x.(map[string]interface{})["e"]

    //build the google pub key
    nStr := h.(string)
    decN, err := base64.URLEncoding.DecodeString(nStr)
    if err != nil {
        fmt.Println(err)
        return
    }

    n := big.NewInt(0)
    n.SetBytes(decN)
    eStr := g.(string)
    decE, err := base64.URLEncoding.DecodeString(eStr)
    if err != nil {
        fmt.Println(err)
        return
    }

    var eBytes []byte
    if len(decE) < 8 {
        eBytes = make([]byte, 8-len(decE), 8)
        eBytes = append(eBytes, decE...)
    } else {
        eBytes = decE
    }

    eReader := bytes.NewReader(eBytes)
    var e uint64
    err = binary.Read(eReader, binary.BigEndian, &e)
    if err != nil {
        log.Println(err)
        return
    }

    pKey := rsa.PublicKey{N: n, E: int(e)}
        //inblockOauth := base64.URLEncoding.DecodeString(w[1])
    toHash := w[0] + "." + w[1]
    digestOauth, err := base64.URLEncoding.DecodeString(s_)

    hasherOauth := sha256.New()
    hasherOauth.Write([]byte(toHash))

    // verification of the signature
    err = rsa.VerifyPKCS1v15(&pKey,crypto.SHA256,hasherOauth.Sum(nil),digestOauth)


    if err != nil {
        fmt.Printf("Error verifying key %s",err.Error())
    }
    fmt.Printf("OK!")
}
2020-07-02