Skip to content

Instantly share code, notes, and snippets.

@ulexxander
Last active January 14, 2023 18:29
Show Gist options
  • Save ulexxander/6c5553edcc38fb8feb999074534f51bd to your computer and use it in GitHub Desktop.
Save ulexxander/6c5553edcc38fb8feb999074534f51bd to your computer and use it in GitHub Desktop.
Go JWT library `golang-jwt/jwt` comparison of different signing algorithms
//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-----`
@ulexxander
Copy link
Author

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