Fix bug: Yggdrasil client can't send message upper 22w17a

When try to send message in yggdrasil-auth server with `enforce-secure-profile=true`
and `online-mode=true` enabled, will fail with message in red:
Chat disabled due to missing profile public key. Please try reconnecting.
So yggdrasil-auth-client also has to encrypt chat messages like Microsoft-auth-client
to send out messages.
This commit is contained in:
oldkingOK 2024-01-13 03:00:02 +08:00
parent 22cf7a046b
commit 2c8b15b02e
2 changed files with 46 additions and 13 deletions

View file

@ -557,7 +557,7 @@ namespace MinecraftClient
} }
} }
if (Config.Main.General.AccountType == LoginType.microsoft if ((Config.Main.General.AccountType == LoginType.microsoft || Config.Main.General.AccountType == LoginType.yggdrasil)
&& (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser) && (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser)
&& Config.Signature.LoginWithSecureProfile && Config.Signature.LoginWithSecureProfile
&& protocolversion >= 759 /* 1.19 and above */) && protocolversion >= 759 /* 1.19 and above */)
@ -582,7 +582,7 @@ namespace MinecraftClient
if (playerKeyPair == null || playerKeyPair.NeedRefresh()) if (playerKeyPair == null || playerKeyPair.NeedRefresh())
{ {
ConsoleIO.WriteLineFormatted(Translations.mcc_fetching_key, acceptnewlines: true); ConsoleIO.WriteLineFormatted(Translations.mcc_fetching_key, acceptnewlines: true);
playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID); playerKeyPair = KeyUtils.GetNewProfileKeys(session.ID, Config.Main.General.AccountType == LoginType.yggdrasil);
if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null) if (Config.Main.Advanced.ProfileKeyCache != CacheType.none && playerKeyPair != null)
{ {
KeysCache.Store(loginLower, playerKeyPair); KeysCache.Store(loginLower, playerKeyPair);

View file

@ -5,6 +5,7 @@ using System.Text;
using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Handlers;
using MinecraftClient.Protocol.Message; using MinecraftClient.Protocol.Message;
using static MinecraftClient.Protocol.Message.LastSeenMessageList; using static MinecraftClient.Protocol.Message.LastSeenMessageList;
using Newtonsoft.Json.Linq;
namespace MinecraftClient.Protocol.ProfileKey namespace MinecraftClient.Protocol.ProfileKey
{ {
@ -14,11 +15,12 @@ namespace MinecraftClient.Protocol.ProfileKey
private static readonly string certificates = "https://api.minecraftservices.com/player/certificates"; private static readonly string certificates = "https://api.minecraftservices.com/player/certificates";
public static PlayerKeyPair? GetNewProfileKeys(string accessToken) public static PlayerKeyPair? GetNewProfileKeys(string accessToken, bool isYggdrasil)
{ {
ProxiedWebRequest.Response? response = null; ProxiedWebRequest.Response? response = null;
try try
{ {
if (!isYggdrasil) {
var request = new ProxiedWebRequest(certificates) var request = new ProxiedWebRequest(certificates)
{ {
Accept = "application/json" Accept = "application/json"
@ -31,10 +33,16 @@ namespace MinecraftClient.Protocol.ProfileKey
{ {
ConsoleIO.WriteLine(response.Body.ToString()); ConsoleIO.WriteLine(response.Body.ToString());
} }
}
string jsonString = response.Body; // see https://github.com/yushijinhun/authlib-injector/blob/da910956eaa30d2f6c2c457222d188aeb53b0d1f/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java#L49
// POST to "https://api.minecraftservices.com/player/certificates" with authlib-injector will get a dummy response
string jsonString = isYggdrasil ? MakeDummyResponse() : response!.Body;
Json.JSONData json = Json.ParseJson(jsonString); Json.JSONData json = Json.ParseJson(jsonString);
Console.WriteLine("Got public key:" + json.Properties["keyPair"].Properties["publicKey"].StringValue);
Console.WriteLine("Got private key:" + json.Properties["keyPair"].Properties["privateKey"].StringValue);
// Error here
PublicKey publicKey = new(pemKey: json.Properties["keyPair"].Properties["publicKey"].StringValue, PublicKey publicKey = new(pemKey: json.Properties["keyPair"].Properties["publicKey"].StringValue,
sig: json.Properties["publicKeySignature"].StringValue, sig: json.Properties["publicKeySignature"].StringValue,
sigV2: json.Properties["publicKeySignatureV2"].StringValue); sigV2: json.Properties["publicKeySignatureV2"].StringValue);
@ -230,5 +238,30 @@ namespace MinecraftClient.Protocol.ProfileKey
sb.Append(src, start, src.Length - start); sb.Append(src, start, src.Length - start);
return sb.ToString(); return sb.ToString();
} }
public static string MakeDummyResponse()
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
var mimePublicKey = Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo());
var mimePrivateKey = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey());
string publicKeyPEM = $"-----BEGIN RSA PUBLIC KEY-----\n{mimePublicKey}\n-----END RSA PUBLIC KEY-----\n";
string privateKeyPEM = $"-----BEGIN RSA PRIVATE KEY-----\n{mimePrivateKey}\n-----END RSA PRIVATE KEY-----\n";
DateTime now = DateTime.UtcNow;
DateTime expiresAt = now.AddHours(48);
DateTime refreshedAfter = now.AddHours(36);
JObject response = new JObject();
JObject keyPairObj = new JObject
{
{ "privateKey", privateKeyPEM },
{ "publicKey", publicKeyPEM }
};
response.Add("keyPair", keyPairObj);
response.Add("publicKeySignature", "AA==");
response.Add("publicKeySignatureV2", "AA==");
string format = "yyyy-MM-ddTHH:mm:ss.ffffffZ";
response.Add("expiresAt", expiresAt.ToString(format));
response.Add("refreshedAfter", refreshedAfter.ToString(format));
return response.ToString();
}
} }
} }