mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-17 00:23:52 +00:00
JWK
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package selfhosted
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
jose "github.com/go-jose/go-jose/v4"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
)
|
||||
|
||||
// keyIDFromPublicKey derives a key ID non-reversibly from a public key.
|
||||
//
|
||||
// The Key ID is field on a given on JWTs and JWKs that help relying parties
|
||||
// pick the correct key for verification when the identity party advertises
|
||||
// multiple keys.
|
||||
//
|
||||
// Making the derivation non-reversible makes it impossible for someone to
|
||||
// accidentally obtain the real key from the key ID and use it for token
|
||||
// validation.
|
||||
// This method is copied from
|
||||
// https://github.com/kubernetes/kubernetes/blob/v1.29.3/pkg/serviceaccount/jwt.go#L99
|
||||
func keyIDFromPublicKey(publicKey interface{}) (string, error) {
|
||||
publicKeyDERBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to serialize public key to DER format: %v", err)
|
||||
}
|
||||
|
||||
hasher := crypto.SHA256.New()
|
||||
hasher.Write(publicKeyDERBytes)
|
||||
publicKeyDERHash := hasher.Sum(nil)
|
||||
|
||||
keyID := base64.RawURLEncoding.EncodeToString(publicKeyDERHash)
|
||||
|
||||
return keyID, nil
|
||||
}
|
||||
|
||||
type JWK struct {
|
||||
Keys []jose.JSONWebKey `json:"keys"`
|
||||
}
|
||||
|
||||
func NewJWK(pub []byte) (*JWK, error){
|
||||
pubKeys, err := keyutil.ParsePublicKeysPEM(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey := pubKeys[0]
|
||||
var alg jose.SignatureAlgorithm
|
||||
switch pubKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
alg = jose.RS256
|
||||
default:
|
||||
return nil, errors.New("public key is not RSA")
|
||||
}
|
||||
|
||||
kid, err := keyIDFromPublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var keys []jose.JSONWebKey
|
||||
keys = append(keys, jose.JSONWebKey{
|
||||
Key: pubKey,
|
||||
KeyID: kid,
|
||||
Algorithm: string(alg),
|
||||
Use: "sig",
|
||||
})
|
||||
return &JWK{Keys: keys}, nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package selfhosted
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
const rsaKeyID = "JHJehTTTZlsspKHT-GaJxK7Kd1NQgZJu3fyK6K_QDYU"
|
||||
|
||||
|
||||
func TestReadKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filename string
|
||||
expected string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "rsa",
|
||||
filename: "testdata/rsa.pub",
|
||||
expected: rsaKeyID,
|
||||
},
|
||||
{
|
||||
name: "rsa",
|
||||
filename: "testdata/ecdsa.pub",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
content, err := os.ReadFile(tt.filename)
|
||||
assert.NoError(t, err)
|
||||
actual, err := NewJWK(content)
|
||||
if tt.expectErr {
|
||||
assert.Error(t, err, "")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, actual.Keys[0].KeyID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPL
|
||||
X2i8uIp/C/ASqiIGUeeKQtX0/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
|
||||
-----END PUBLIC KEY-----
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA249XwEo9k4tM8fMxV7zx
|
||||
OhcrP+WvXn917koM5Qr2ZXs4vo26e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecI
|
||||
zshKuv1gKIxbbLQMOuK1eA/4HALyEkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG
|
||||
51RoiMgbQxaCyYxGfNLpLAZK9L0Tctv9a0mJmGIYnIOQM4kC1A1I1n3EsXMWmeJU
|
||||
j7OTh/AjjCnMnkgvKT2tpKxYQ59PgDgU8Ssc7RDSmSkLxnrv+OrN80j6xrw0OjEi
|
||||
B4Ycr0PqfzZcvy8efTtFQ/Jnc4Bp1zUtFXt7+QeevePtQ2EcyELXE0i63T1CujRM
|
||||
WwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
Reference in New Issue
Block a user