From 9235e92c0a12dbcc746388a031d9ef0998858ac4 Mon Sep 17 00:00:00 2001 From: xmdhs Date: Sun, 15 Oct 2023 23:24:26 +0800 Subject: [PATCH] minecraftservices/publickeys --- handle/yggdrasil/yggdrasil.go | 8 ++++++++ model/yggdrasil/model.go | 9 +++++++++ server/route/route.go | 1 + service/yggdrasil/yggdrasil.go | 27 +++++++++++++++++++++++++++ utils/sign/rsa_test.go | 14 ++++++++++++++ 5 files changed, 59 insertions(+) diff --git a/handle/yggdrasil/yggdrasil.go b/handle/yggdrasil/yggdrasil.go index cd3f963..a42a0d0 100644 --- a/handle/yggdrasil/yggdrasil.go +++ b/handle/yggdrasil/yggdrasil.go @@ -136,3 +136,11 @@ func (r readerClose) Read(p []byte) (n int, err error) { func (r readerClose) Close() error { return r.close.Close() } + +func (y *Yggdrasil) PublicKeys() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + p := y.yggdrasilService.PublicKeys(ctx) + json.NewEncoder(w).Encode(p) + } +} diff --git a/model/yggdrasil/model.go b/model/yggdrasil/model.go index 5f86486..4ccc59b 100644 --- a/model/yggdrasil/model.go +++ b/model/yggdrasil/model.go @@ -92,3 +92,12 @@ type CertificatesKeyPair struct { PrivateKey string `json:"privateKey"` PublicKey string `json:"publicKey"` } + +type PublicKeys struct { + PlayerCertificateKeys []PublicKeyList `json:"playerCertificateKeys"` + ProfilePropertyKeys []PublicKeyList `json:"profilePropertyKeys"` +} + +type PublicKeyList struct { + PublicKey string `json:"publicKey"` +} diff --git a/server/route/route.go b/server/route/route.go index 30d244d..2967f11 100644 --- a/server/route/route.go +++ b/server/route/route.go @@ -68,6 +68,7 @@ func newYggdrasil(handelY *yggdrasil.Yggdrasil) http.Handler { r.Get("/minecraftservices/player/attributes", handelY.PlayerAttributes()) r.Post("/minecraftservices/player/attributes", handelY.PlayerAttributes()) r.Post("/minecraftservices/player/report", handelY.PlayerReport()) + r.Get("/minecraftservices/publickeys", handelY.PublicKeys()) r.Get("/", handelY.YggdrasilRoot()) return r diff --git a/service/yggdrasil/yggdrasil.go b/service/yggdrasil/yggdrasil.go index 6d5f626..f7243bd 100644 --- a/service/yggdrasil/yggdrasil.go +++ b/service/yggdrasil/yggdrasil.go @@ -3,10 +3,15 @@ package yggdrasil import ( "context" "crypto/rsa" + "crypto/x509" + _ "embed" + "encoding/base64" "encoding/binary" "fmt" + "sync" "time" + "github.com/samber/lo" "github.com/xmdhs/authlib-skin/config" "github.com/xmdhs/authlib-skin/db/cache" "github.com/xmdhs/authlib-skin/db/ent" @@ -76,3 +81,25 @@ func (y *Yggdrasil) Auth(ctx context.Context, t yggdrasil.ValidateToken) (*model } return u, nil } + +//go:embed yggdrasil_session_pubkey.der +var mojangPubKey []byte + +var mojangPubKeyStr = sync.OnceValue(func() string { + pub := lo.Must(x509.ParsePKIXPublicKey(mojangPubKey)).(*rsa.PublicKey) + derBytes := lo.Must(x509.MarshalPKIXPublicKey(pub)) + return base64.StdEncoding.EncodeToString(derBytes) +}) + +func (y *Yggdrasil) PublicKeys(ctx context.Context) yggdrasil.PublicKeys { + mojangPub := mojangPubKeyStr() + derBytes := lo.Must(x509.MarshalPKIXPublicKey(y.prikey.PublicKey)) + myPub := base64.StdEncoding.EncodeToString(derBytes) + + pl := []yggdrasil.PublicKeyList{{PublicKey: mojangPub}, {PublicKey: myPub}} + + return yggdrasil.PublicKeys{ + PlayerCertificateKeys: pl, + ProfilePropertyKeys: pl, + } +} diff --git a/utils/sign/rsa_test.go b/utils/sign/rsa_test.go index 31c89a7..972ee08 100644 --- a/utils/sign/rsa_test.go +++ b/utils/sign/rsa_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/samber/lo" + "github.com/stretchr/testify/require" ) func TestAuthlibSign(t *testing.T) { @@ -97,3 +98,16 @@ func TestMojangSign(t *testing.T) { } } + +func TestMojangPubKey(t *testing.T) { + b, err := os.ReadFile("../../service/yggdrasil/yggdrasil_session_pubkey.der") + if err != nil { + t.Fatal(err) + } + pub := lo.Must(x509.ParsePKIXPublicKey(b)).(*rsa.PublicKey) + derBytes, err := x509.MarshalPKIXPublicKey(pub) + require.Nil(t, err) + + const key = `MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAylB4B6m5lz7jwrcFz6Fd/fnfUhcvlxsTSn5kIK/2aGG1C3kMy4VjhwlxF6BFUSnfxhNswPjh3ZitkBxEAFY25uzkJFRwHwVA9mdwjashXILtR6OqdLXXFVyUPIURLOSWqGNBtb08EN5fMnG8iFLgEJIBMxs9BvF3s3/FhuHyPKiVTZmXY0WY4ZyYqvoKR+XjaTRPPvBsDa4WI2u1zxXMeHlodT3lnCzVvyOYBLXL6CJgByuOxccJ8hnXfF9yY4F0aeL080Jz/3+EBNG8RO4ByhtBf4Ny8NQ6stWsjfeUIvH7bU/4zCYcYOq4WrInXHqS8qruDmIl7P5XXGcabuzQstPf/h2CRAUpP/PlHXcMlvewjmGU6MfDK+lifScNYwjPxRo4nKTGFZf/0aqHCh/EAsQyLKrOIYRE0lDG3bzBh8ogIMLAugsAfBb6M3mqCqKaTMAf/VAjh5FFJnjS+7bE+bZEV0qwax1CEoPPJL1fIQjOS8zj086gjpGRCtSy9+bTPTfTR/SJ+VUB5G2IeCItkNHpJX2ygojFZ9n5Fnj7R9ZnOM+L8nyIjPu3aePvtcrXlyLhH/hvOfIOjPxOlqW+O5QwSFP4OEcyLAUgDdUgyW36Z5mB285uKW/ighzZsOTevVUG2QwDItObIV6i8RCxFbN2oDHyPaO5j1tTaBNyVt8CAwEAAQ==` + require.Equal(t, key, base64.StdEncoding.EncodeToString(derBytes)) +}