golang-jwt 使用ECDSA signing method

294 阅读3分钟

golang-jwt 的项目地址:Signing Methods - golang-jwt docs

  1. ECDSA 证书和 RSA 证书有什么区别:

ECDSA(Elliptic Curve Digital Signature Algorithm)证书和RSA(Rivest-Shamir-Adleman)证书是两种不同的公钥加密算法所生成的证书,它们之间有几个主要区别:

  1. 加密算法

    • RSA:RSA是一种基于整数因子分解问题的公钥加密算法,其安全性基于大素数的难以因式分解。
    • ECDSA:ECDSA是一种基于椭圆曲线离散对数问题的公钥加密算法,其安全性基于椭圆曲线上的离散对数难题。
  2. 密钥长度

    • RSA:RSA证书通常使用较长的密钥长度,通常为2048位或更长,以确保安全性。
    • ECDSA:相比之下,ECDSA证书使用较短的密钥长度,因为椭圆曲线密码学相比传统RSA加密需要更短的密钥长度来提供相同的安全性。通常,ECDSA证书的密钥长度可以选择256位或更长的长度。
  3. 性能

    • ECDSA:由于使用较短的密钥长度和椭圆曲线算法的特性,ECDSA在加密和签名操作上通常比RSA更高效。
    • RSA:相比之下,RSA的加密和签名操作通常更耗费计算资源,尤其是在密钥长度较长时。
  4. 支持情况

    • RSA:RSA是较早被广泛使用的公钥加密算法之一,因此得到了更广泛的支持,许多加密库和协议都支持RSA证书。
    • ECDSA:ECDSA在一些新兴的加密应用中得到了较多的关注,但是并不像RSA那样被广泛支持。然而,随着对RSA密钥长度的要求不断增加,ECDSA在一些情况下被视为更加高效和安全的替代方案。

2.生成ECDSA证书:

要生成ECDSA(Elliptic Curve Digital Signature Algorithm)证书,你可以使用OpenSSL工具。以下是生成ECDSA证书的简单步骤:

  1. 生成私钥: 使用以下命令生成ECDSA私钥:

    openssl ecparam -genkey -name prime256v1 -out ecdsa-private-key.pem
    

    这将生成一个名为ecdsa-private-key.pem的ECDSA私钥文件。

  2. 生成CSR(Certificate Signing Request) : 使用以下命令生成CSR文件:

    openssl req -new -key ecdsa-private-key.pem -out ecdsa-csr.pem
    

    在这一步,你需要填写有关证书请求的信息,包括国家、州、城市等信息。

  3. 自签名证书(可选): 如果你想要自签名证书,可以使用以下命令生成自签名证书:

    openssl x509 -req -days 365 -in ecdsa-csr.pem -signkey ecdsa-private-key.pem -out ecdsa-certificate.pem
    

    这将使用私钥对CSR进行签名,生成一个有效期为365天的自签名ECDSA证书文件ecdsa-certificate.pem

3.修改证书内容:

3.1编辑ecdsa-private-key.pem: 删除

-----BEGIN EC PARAMETERS----- 
xxxx
-----END EC PARAMETERS-----

最后样子:

image.png

3.2加载私钥方法:


func loadECDSAPrivateKey(filename string) (*ecdsa.PrivateKey, error) {
    // 读取证书文件内容
    pemData, err := ioutil.ReadFile(filename)
    if err != nil {
       return nil, err
    }

    // 解析 PEM 数据
    block, _ := pem.Decode(pemData)
    if block == nil || block.Type != "EC PRIVATE KEY" {
       return nil, fmt.Errorf("failed to decode PEM block containing ECDSA private key")
    }

    // 解析 ECDSA 私钥
    privateKey, err := x509.ParseECPrivateKey(block.Bytes)
    if err != nil {
       return nil, err
    }

    return privateKey, nil
}

3.3加载公钥方法:


func loadECDSAPublicKey(filename string) (*ecdsa.PublicKey, error) {
    // 读取证书文件内容
    certPEM, err := ioutil.ReadFile(filename)
    if err != nil {
       return nil, err
    }

    // 解码 PEM 格式证书
    block, _ := pem.Decode(certPEM)
    if block == nil {
       return nil, fmt.Errorf("failed to decode PEM block")
    }

    // 解析 X.509 证书
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
       return nil, err
    }

    // 从证书中提取公钥
    publicKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
    if !ok {
       return nil, fmt.Errorf("failed to extract ECDSA public key from certificate")
    }

    return publicKey, nil
}

3.3生成与验证:


func main() {

    key, err := loadECDSAPrivateKey("./ecdsa-private-key.pem")
    if err != nil {
       println("cccc")
       println(err.Error())
    }
    println(key)

    // 创建 JWT 令牌
    token := jwt.New(jwt.SigningMethodES256)

    // 设置 JWT payload
    claims := token.Claims.(jwt.MapClaims)
    claims["sub"] = "subject"
    claims["exp"] = time.Now().Add(time.Hour * 1).Unix()

    // 使用 ECDSA 私钥签署 JWT
    tokenString, err := token.SignedString(key)
    if err != nil {
       fmt.Println("Error signing token:", err)
       return
    }

    fmt.Println("JWT token:", tokenString)

    publicKey, err := loadECDSAPublicKey("./ecdsa-certificate.pem")

    if err != nil {
       println("cccc1111")
       println(err.Error())
    }
    println(publicKey)

    // 解析 JWT token
    tokenS, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
       return publicKey, nil
    })
    if err != nil {
       fmt.Println("Error parsing token:", err)
       return
    }

    // 验证 JWT token
    if tokenS.Valid {
       fmt.Println("JWT token is valid")
       claims := tokenS.Claims.(jwt.MapClaims)
       fmt.Println("Subject:", claims["sub"])
       fmt.Println("Expires At:", time.Unix(int64(claims["exp"].(float64)), 0))
    } else {
       fmt.Println("JWT token is not valid")
    }
}