Last active
January 14, 2023 18:29
-
-
Save ulexxander/6c5553edcc38fb8feb999074534f51bd to your computer and use it in GitHub Desktop.
Go JWT library `golang-jwt/jwt` comparison of different signing algorithms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//nolint:forbidigo,dupl | |
package jwt_test | |
import ( | |
"crypto/ecdsa" | |
"crypto/ed25519" | |
"crypto/elliptic" | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/x509" | |
"encoding/pem" | |
"fmt" | |
"testing" | |
"time" | |
"github.com/golang-jwt/jwt/v4" | |
"github.com/stretchr/testify/require" | |
) | |
func TestGenerateKeyPair(t *testing.T) { | |
t.Run("RSA", func(t *testing.T) { | |
privateKey, err := rsa.GenerateKey(rand.Reader, 2048) | |
require.NoError(t, err) | |
publicKeyPKCS1 := x509.MarshalPKCS1PublicKey(&privateKey.PublicKey) | |
publicKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
Type: "RSA PUBLIC KEY", | |
Bytes: publicKeyPKCS1, | |
}) | |
require.NotNil(t, publicKeyPEM) | |
fmt.Println(string(publicKeyPEM)) | |
privateKeyPKCS1 := x509.MarshalPKCS1PrivateKey(privateKey) | |
privateKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
Type: "RSA PRIVATE KEY", | |
Bytes: privateKeyPKCS1, | |
}) | |
require.NotNil(t, privateKeyPEM) | |
fmt.Println(string(privateKeyPEM)) | |
}) | |
t.Run("ECDSA", func(t *testing.T) { | |
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | |
require.NoError(t, err) | |
publicKeyPKIX, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) | |
require.NoError(t, err) | |
publicKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
Type: "PUBLIC KEY", | |
Bytes: publicKeyPKIX, | |
}) | |
require.NotNil(t, publicKeyPEM) | |
fmt.Println(string(publicKeyPEM)) | |
// Alternatively PKCS #8 format can be used: | |
// privateKeyPKCS8, err := x509.MarshalPKCS8PrivateKey(privateKey) | |
privateKeyEC, err := x509.MarshalECPrivateKey(privateKey) | |
require.NoError(t, err) | |
privateKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
// For PKCS #8 format: | |
// Type: "PRIVATE KEY", | |
Type: "EC PRIVATE KEY", | |
Bytes: privateKeyEC, | |
}) | |
require.NotNil(t, privateKeyPEM) | |
fmt.Println(string(privateKeyPEM)) | |
}) | |
t.Run("Ed25519", func(t *testing.T) { | |
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) | |
require.NoError(t, err) | |
publicKeyPKIX, err := x509.MarshalPKIXPublicKey(publicKey) | |
require.NoError(t, err) | |
publicKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
Type: "PUBLIC KEY", | |
Bytes: publicKeyPKIX, | |
}) | |
require.NotNil(t, publicKeyPEM) | |
fmt.Println(string(publicKeyPEM)) | |
privateKeyPKCS8, err := x509.MarshalPKCS8PrivateKey(privateKey) | |
require.NoError(t, err) | |
privateKeyPEM := pem.EncodeToMemory(&pem.Block{ | |
Type: "PRIVATE KEY", | |
Bytes: privateKeyPKCS8, | |
}) | |
require.NotNil(t, privateKeyPEM) | |
fmt.Println(string(privateKeyPEM)) | |
}) | |
} | |
func TestJWT(t *testing.T) { | |
claims := jwt.RegisteredClaims{ | |
Issuer: "some-backend", | |
Subject: "user-123", | |
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), | |
IssuedAt: jwt.NewNumericDate(time.Now()), | |
} | |
t.Run("PS256", func(t *testing.T) { | |
privateKeyBlock, _ := pem.Decode([]byte(rsaPrivateKey)) | |
require.NotNil(t, privateKeyBlock) | |
privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes) | |
require.NoError(t, err) | |
publicKeyBlock, _ := pem.Decode([]byte(rsaPublicKey)) | |
require.NotNil(t, publicKeyBlock) | |
publicKey, err := x509.ParsePKCS1PublicKey(publicKeyBlock.Bytes) | |
require.NoError(t, err) | |
token := jwt.NewWithClaims(jwt.SigningMethodPS256, claims) | |
tokenSigned, err := token.SignedString(privateKey) | |
require.NoError(t, err) | |
fmt.Printf("tokenSigned (len=%d):\n%v\n", len(tokenSigned), tokenSigned) | |
tokenParsed, err := jwt.ParseWithClaims( | |
tokenSigned, | |
&jwt.RegisteredClaims{}, | |
func(t *jwt.Token) (interface{}, error) { | |
return publicKey, nil | |
}, | |
jwt.WithValidMethods([]string{"PS256"}), | |
) | |
require.NoError(t, err) | |
fmt.Printf("tokenParsed.Claims:\n%+v\n", tokenParsed.Claims) | |
}) | |
t.Run("ES256", func(t *testing.T) { | |
privateKeyBlock, _ := pem.Decode([]byte(ecdsaPrivateKey)) | |
require.NotNil(t, privateKeyBlock) | |
// Alternatively PKCS #8 format can be used: | |
// privateKey, err := x509.ParsePKCS8PrivateKey(privateKeyBlock.Bytes) | |
privateKey, err := x509.ParseECPrivateKey(privateKeyBlock.Bytes) | |
require.NoError(t, err) | |
publicKeyBlock, _ := pem.Decode([]byte(ecdsaPublicKey)) | |
require.NotNil(t, publicKeyBlock) | |
publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes) | |
require.NoError(t, err) | |
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) | |
tokenSigned, err := token.SignedString(privateKey) | |
require.NoError(t, err) | |
fmt.Printf("tokenSigned (len=%d):\n%v\n", len(tokenSigned), tokenSigned) | |
tokenParsed, err := jwt.ParseWithClaims( | |
tokenSigned, | |
&jwt.RegisteredClaims{}, | |
func(t *jwt.Token) (interface{}, error) { | |
return publicKey, nil | |
}, | |
jwt.WithValidMethods([]string{"ES256"}), | |
) | |
require.NoError(t, err) | |
fmt.Printf("tokenParsed.Claims:\n%+v\n", tokenParsed.Claims) | |
}) | |
t.Run("EdDSA", func(t *testing.T) { | |
privateKeyBlock, _ := pem.Decode([]byte(ed25519PrivateKey)) | |
require.NotNil(t, privateKeyBlock) | |
privateKey, err := x509.ParsePKCS8PrivateKey(privateKeyBlock.Bytes) | |
require.NoError(t, err) | |
publicKeyBlock, _ := pem.Decode([]byte(ed25519PublicKey)) | |
require.NotNil(t, publicKeyBlock) | |
publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes) | |
require.NoError(t, err) | |
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims) | |
tokenSigned, err := token.SignedString(privateKey) | |
require.NoError(t, err) | |
fmt.Printf("tokenSigned (len=%d):\n%v\n", len(tokenSigned), tokenSigned) | |
tokenParsed, err := jwt.ParseWithClaims( | |
tokenSigned, | |
&jwt.RegisteredClaims{}, | |
func(t *jwt.Token) (interface{}, error) { | |
return publicKey, nil | |
}, | |
jwt.WithValidMethods([]string{"EdDSA"}), | |
) | |
require.NoError(t, err) | |
fmt.Printf("tokenParsed.Claims:\n%+v\n", tokenParsed.Claims) | |
}) | |
} | |
const rsaPublicKey = `-----BEGIN RSA PUBLIC KEY----- | |
MIIBCgKCAQEAlJB8n4cg0+sa6kfLVeELJ93yEEVJVYxKW7P/RGF8wO/Yq+fv/Uhf | |
GaH3sbimo7UZ+7NFEdEVZ9NUW9JtfFTXQhiaCf594mq3zGt2OrRQU4JmVOeaU4NU | |
5G1nYmyB8Jo3es3yipr1QxJPcBJ97x7uc8gnKAEO3TzTwGrbNfIlVqZKE75VCIJ8 | |
aXV6uT0emgaur2danj5Ib6XnlOjX2Wk9Bb+9IH/TcY/DrNZfk/gXeE4Qf95Wtx+r | |
YCbFN7mXCVLQQqKdmHh4FF91BGZp5oSv1YFKtl0C15CN/yKXVSMPr4kCt5CUnHs/ | |
1jtNY21Ga9MmgrsSbJ79MRlv0BFBuLBpoQIDAQAB | |
-----END RSA PUBLIC KEY-----` | |
const rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY----- | |
MIIEpQIBAAKCAQEAlJB8n4cg0+sa6kfLVeELJ93yEEVJVYxKW7P/RGF8wO/Yq+fv | |
/UhfGaH3sbimo7UZ+7NFEdEVZ9NUW9JtfFTXQhiaCf594mq3zGt2OrRQU4JmVOea | |
U4NU5G1nYmyB8Jo3es3yipr1QxJPcBJ97x7uc8gnKAEO3TzTwGrbNfIlVqZKE75V | |
CIJ8aXV6uT0emgaur2danj5Ib6XnlOjX2Wk9Bb+9IH/TcY/DrNZfk/gXeE4Qf95W | |
tx+rYCbFN7mXCVLQQqKdmHh4FF91BGZp5oSv1YFKtl0C15CN/yKXVSMPr4kCt5CU | |
nHs/1jtNY21Ga9MmgrsSbJ79MRlv0BFBuLBpoQIDAQABAoIBABX6ynVPqKuxvOms | |
dYjfIqdjTszZc2AUwWuP1dRoF+zzjzrftcHjUy6Xp+3DQ+Q0hwOWIkHgjSovOevb | |
1hbu9mnw+dlArZ8x6r9zpqpIwxmX0Uv+d/ajF4w0I3eMVi0io9KnZZrcEi06KEaM | |
ddVK3jyDAEgK4AgbV7jAFm6ExMFs3xPhd/GqSDsqHcq/KmmwTPDux2BatBHRcDgc | |
dr5tf2Zxtib4u0zFBzbWXADmiZ6VstgTAI+f26B4tiDeBDBK2bqdnJcrwtz0ZQ8n | |
/y4kxHl8ee/3zvxbc2YI6ur49sL6Z78TV8yTHrjxT55DM6//69KWwkwDOdY4M6Le | |
u+xC6gECgYEAxBLew8dKpb8/+wqKMbMsv/HY9bT3M/uPpV+3TFfSwf7OIWPpWOTi | |
8c/8OMMPCCEC8CSU2vil/NGwJheocXbMGk3AcroSlPctvAoM2mB+rXkdSHit2WBi | |
B7x/yHoCKjPQz29fNs1wfT7K91i6bJrRqC6HAwampBws9EjUhba7oK0CgYEAwfho | |
+KudYsWbEiQ328UyDEdiqcrOkSpvwzkVRYQCruIpdej5RS0xyK2aSa/kjsiGpyyp | |
/qG8+b4VtUMFL1VMJi6QciKc9lD2hwk2irbsK85syiZnX9nB9pc4v/fAiKczRTJb | |
AbfxHYVF3agx9FiDt0rXapKaaU6+mTq2bfzq50UCgYEAoYrWhECu7M4cPY3ae8Ye | |
aWotkYpuXSK2n86zay+0UebIF9SETZwOqqHsMSvsN4tTK11IUvynbRup4Eh/nDP/ | |
RvCmTR+m7ZqU0KcLHeSAmhX+HVZoDX+OZMdWFGsJW5HjSPR+Yt1x/457lLxCo/YO | |
JF1pGQIiy3T3SLC0gBIkleUCgYEAu9sIVuVmLNWyRJSdIJRPz0KHlGedPUw7J3gu | |
WL/sFVFl1BMI721AzNkNFxZppbshZeDE1p3vtnPY9E7vQ5M5h7ULIe7R02BAEjHj | |
B972xcpK5FDjRxpPVrHIPWsFpZNA/WAycWyi8PM04eJWVw4uqqKqFEXdk+W2hBIv | |
wj/zhH0CgYEAudg9lqap5TFy3pgLDVmOw5WNuQyTMVszWHrTjMaP7kIsJUbKoo79 | |
7lsCEYo6OFgSe8YBR8eYNMmUZlPaI6npXEPaYobuqPQ4dxDUybPlL8h90jLfQDfb | |
wmh9s2vwPcg065DZlKDE4L5rHTKOM81ULf9xU5hfHREoR4nIhxPn13c= | |
-----END RSA PRIVATE KEY-----` | |
const ecdsaPublicKey = `-----BEGIN PUBLIC KEY----- | |
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3WV9i5ZurWc77cEC6ldk48nz5hCO | |
5IJeFXqyjUD1tXiQCGw6EP3m4KtubQBs2dLHcuEmhsvjA33b1yaLuplURw== | |
-----END PUBLIC KEY-----` | |
const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY----- | |
MHcCAQEEIJEsjM2i5OPvjhZoZopvJFr0CEYYbMOR9K/5hbUCODhYoAoGCCqGSM49 | |
AwEHoUQDQgAE3WV9i5ZurWc77cEC6ldk48nz5hCO5IJeFXqyjUD1tXiQCGw6EP3m | |
4KtubQBs2dLHcuEmhsvjA33b1yaLuplURw== | |
-----END EC PRIVATE KEY-----` | |
const ed25519PublicKey = `-----BEGIN PUBLIC KEY----- | |
MCowBQYDK2VwAyEAaIGRj+ctz5peBB+5udWOXobWuG34uCHY8+MnaW0CzDU= | |
-----END PUBLIC KEY-----` | |
const ed25519PrivateKey = `-----BEGIN PRIVATE KEY----- | |
MC4CAQAwBQYDK2VwBCIEIB0RkyGh8E4ge0AkkovLyg0VeTsAB8zmDAwpL2M2CFFk | |
-----END PRIVATE KEY-----` |
Built-in golang-jwt/jwt
functions for parsing public and private keys can be also used:
privateKey, err := jwt.ParseECPrivateKeyFromPEM([]byte(ecdsaPrivateKey))
require.NoError(t, err)
publicKey, err := jwt.ParseECPublicKeyFromPEM([]byte(ecdsaPublicKey))
require.NoError(t, err)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output: