golang-jwt/jwt key is of invalid type 错误的解决方法
起因:
无意中在github上看到dgrijalva/jwt-go
已经不在维护了,而是替换成了golang-jwt/jwt
,所以考虑更新一下新的jwt包,期间各种复杂,因为只是扫了一眼jwt的源码,以为没什么太大改动,所以以后还是得多看源码包,特此更新一篇笔记
传送门:新版本go-jwt
直接上源代码
package jwt
func GetToken(Id string) (string, error) {
c := MyClaims{
jwt.StandardClaims{
Id : Id,
ExpiresAt: time.Now().Add(TokenExpiration).Unix(),
},
}
// 其实问题出在这里 这里会有很多选项
// 例如 ParseRSAPrivateKeyFromPEM rsa pkcs8的私钥解析
// ParseEdPrivateKeyFromPEM ed25519 pkcs8的私钥解析
// 还有我用的 ParseECPrivateKeyFromPEM ecdsa pkcs8的私钥解析
// 这个方法的目的是还原private 对应算法的[]byte 数据
priKey, err := jwt.ParseECPrivateKeyFromPEM(privateKey)
if err != nil {
return "xxxx", err
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, c)
return token.SignedString(priKey)
}
我们来看一下 token.SignedString(priKey)
的源码
// Get the complete, signed token 获取一个已签名的token
func (t *Token) SignedString(key interface{}) (string, error) {
var sig, sstr string
var err error
// 生成签名这里不用管
if sstr, err = t.SigningString(); err != nil {
return "", err
}
// 主要看这里t.Method.Sign 这里是一个接口
if sig, err = t.Method.Sign(sstr, key); err != nil {
return "", err
}
return strings.Join([]string{sstr, sig}, "."), nil
}
// t.Method.Sign --> 接口
type SigningMethod interface {
Verify(signingString, signature string, key interface{}) error // 验签
Sign(signingString string, key interface{}) (string, error) // 签名
Alg() string // 签名的hash方式 例如HS256
}
// 那么 SignedString 的这段代码就是加签的流程
// 在最上面的代码块 GetToken 中 我使用的是 jwt.SigningMethodES256
// 这个是看一个网上的demo 写的 所以这里也是跟 ParseECPrivateKeyFromPEM 保持一致的
// 也就是说可以有很多加密方式的选择 比如 以下相匹配的
// 也就是说jwt/xxx_utils.go ParseXXXPublicKeyFromPEM
// 对应 jwt/xxx.go 下的 SigningMethodXXXX SigningMethodXXX
// 例如 rsa 方式加签 就是 jwt/rsa_utils.go ParseRSAPrivateKeyFromPEMWithPassword / ParseRSAPublicKeyFromPEM 方法
// 对应 jwt/rsa.go 下的集中加签方式
// SigningMethodRS256 *SigningMethodRSA
// SigningMethodRS384 *SigningMethodRSA
// SigningMethodRS512 *SigningMethodRSA
if sig, err = t.Method.Sign(sstr, key); err != nil {
return "", err
}
以jwt.SigningMethodES256为例
// Implements the Sign method from SigningMethod
// For this signing method, key must be an ecdsa.PrivateKey struct
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
// Get the key
var ecdsaKey *ecdsa.PrivateKey // SigningMethodES256 需要的是 ecdsa 加签算法的私钥
// 所以 再遇到 key is of invalid type 这个问题 直接考虑加密方式是否不匹配
switch k := key.(type) {
case *ecdsa.PrivateKey:
ecdsaKey = k
default:
return "", ErrInvalidKeyType
}
// Create the hasher
if !m.Hash.Available() {
return "", ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))
// Sign the string and return r, s
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
curveBits := ecdsaKey.Curve.Params().BitSize
if m.CurveBits != curveBits {
return "", ErrInvalidKey
}
keyBytes := curveBits / 8
if curveBits%8 > 0 {
keyBytes += 1
}
// We serialize the outputs (r and s) into big-endian byte arrays
// padded with zeros on the left to make sure the sizes work out.
// Output must be 2*keyBytes long.
out := make([]byte, 2*keyBytes)
r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output.
s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output.
return EncodeSegment(out), nil
} else {
return "", err
}
}
以下附上生成ecdsa的私钥生成方法,其他算法同理自行处理吧
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"fmt"
)
func generateEcdsaPrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}
func generatePkcs8PrivateKey(privateKey *ecdsa.PrivateKey) ([]byte, error) {
return x509.MarshalPKCS8PrivateKey(privateKey)
}
func main() {
key, err := generateEcdsaPrivateKey()
if err != nil {
panic(err)
}
pkcs8PrivateKey, err := generatePkcs8PrivateKey(key)
if err != nil {
panic(err)
}
privateKeyString := base64.StdEncoding.EncodeToString(pkcs8PrivateKey)
base64PriKey := FormatPrivateKeyFromString(privateKeyString)
fmt.Println(base64PriKey)
}
func FormatPrivateKeyFromString(privateKey string) string{
return fmt.Sprintf("-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----", privateKey)
}