我需要验证一个Google id_token,其中一个步骤涉及检查令牌签名。
首先,我从以下网址获取证书:https : //www.googleapis.com/oauth2/v2/certs,并从证书中提取模数(n)和指数(e)并生成公钥,然后分解令牌(标题,有效负载和摘要),然后将解码后的内容header.payload与Google pKey +摘要一起发送到rsa函数rsa.VerifyPKCS1v15。
header.payload
rsa.VerifyPKCS1v15
我陷入了这个验证错误: crypto/rsa: verification error
crypto/rsa: verification error
这是代码(我用注释掉了部分代码// validation here fails):
// 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()) } }
正如聊天中所解释的那样,问题在于,如果缺少标头和签名,则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!") }