support PROD salt for #207

This commit is contained in:
DismissedLight
2022-11-14 16:37:17 +08:00
parent e29e12c9fe
commit 0be84a2585
12 changed files with 173 additions and 34 deletions

View File

@@ -22,3 +22,8 @@ internal abstract class Md5Convert
return System.Convert.ToHexString(hash); return System.Convert.ToHexString(hash);
} }
} }
internal class RSAConvert
{
}

View File

@@ -16,33 +16,6 @@ namespace Snap.Hutao.Core;
/// </summary> /// </summary>
internal static class CoreEnvironment internal static class CoreEnvironment
{ {
// 计算过程https://github.com/UIGF-org/Hoyolab.Salt
/// <summary>
/// 动态密钥1的K2盐
/// </summary>
public const string DynamicSecretK2Salt = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ";
/// <summary>
/// 动态密钥1的LK2盐
/// </summary>
public const string DynamicSecretLK2Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a";
/// <summary>
/// 动态密钥2的X4盐
/// </summary>
public const string DynamicSecretX4Salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
/// <summary>
/// 动态密钥2的X6盐
/// </summary>
public const string DynamicSecretX6Salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v";
/// <summary>
/// LoginApi的盐
/// </summary>
public const string DynamicSecretPRODSalt = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS";
/// <summary> /// <summary>
/// 米游社请求UA /// 米游社请求UA
/// </summary> /// </summary>
@@ -55,6 +28,7 @@ internal static class CoreEnvironment
/// <summary> /// <summary>
/// 动态密钥 /// 动态密钥
/// https://github.com/UIGF-org/Hoyolab.Salt
/// </summary> /// </summary>
public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>() public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>()
{ {

View File

@@ -129,10 +129,15 @@ internal static class ApiEndpoints
/// </summary> /// </summary>
public const string UserGameRolesByCookie = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn"; public const string UserGameRolesByCookie = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn";
/// <summary>
/// 用户游戏角色
/// </summary>
public const string UserGameRolesByStoken = $"{ApiTaKumiBindingApi}/getUserGameRolesByStoken";
/// <summary> /// <summary>
/// AuthKey /// AuthKey
/// </summary> /// </summary>
public const string GenAuthKey = $"{ApiTaKumiBindingApi}/genAuthKey"; public const string BindingGenAuthKey = $"{ApiTaKumiBindingApi}/genAuthKey";
#endregion #endregion
#region Auth #region Auth
@@ -179,7 +184,7 @@ internal static class ApiEndpoints
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18 // https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
#endregion #endregion
#region ma-cn-session #region LoginApi
/// <summary> /// <summary>
/// 获取 CookieToken /// 获取 CookieToken
@@ -192,8 +197,30 @@ internal static class ApiEndpoints
public const string AccountVerifyLtoken = $"{PassportApiV4}/account/ma-cn-session/web/verifyLtoken"; public const string AccountVerifyLtoken = $"{PassportApiV4}/account/ma-cn-session/web/verifyLtoken";
#endregion #endregion
#region WebAccountApi
/// <summary>
/// 通过通行证Cookie登录
/// </summary>
/// <returns>通行证Cookie登录</returns>
public static string AccountLoginByCookie()
{
return $"{WebApiAccountApi}/login_by_cookie?t={DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}";
}
#endregion
#region App #region App
/// <summary>
/// 另一个AuthKey
/// </summary>
public const string AppAccountGenAuthKey = $"{AppAuthApi}/genAuthKey";
/// <summary>
/// 通过 Stoken 获取 Ltoken
/// </summary>
public const string AppAccountGetLTokenBySToken = $"{AppAuthApi}/getLTokenBySToken";
/// <summary> /// <summary>
/// 小组件数据 /// 小组件数据
/// </summary> /// </summary>
@@ -209,6 +236,7 @@ internal static class ApiEndpoints
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api"; private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
private const string App = "https://app.mihoyo.com"; private const string App = "https://app.mihoyo.com";
private const string AppAuthApi = $"{App}/account/auth/api";
private const string AppCardApi = $"{App}/game_record/app/card/api"; private const string AppCardApi = $"{App}/game_record/app/card/api";
private const string BbsApi = "https://bbs-api.mihoyo.com"; private const string BbsApi = "https://bbs-api.mihoyo.com";
@@ -224,5 +252,8 @@ internal static class ApiEndpoints
private const string SdkStatic = "https://sdk-static.mihoyo.com"; private const string SdkStatic = "https://sdk-static.mihoyo.com";
private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api"; private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api";
private const string WebApiAccount = "https://webapi.account.mihoyo.com";
private const string WebApiAccountApi = $"{WebApiAccount}/Api";
private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc&region=cn_gf01&level=55&uid=100000000"; private const string AnnouncementQuery = "game=hk4e&game_biz=hk4e_cn&lang=zh-cn&bundle_id=hk4e_cn&platform=pc&region=cn_gf01&level=55&uid=100000000";
} }

View File

@@ -0,0 +1,77 @@
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Web.Hoyolab.App.Account;
/// <summary>
/// 账户客户端
/// </summary>
[HttpClient(HttpClientConfigration.XRpc)]
internal class AccountClient
{
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
private readonly ILogger<AccountClient> logger;
/// <summary>
/// 构造一个新的账户客户端
/// </summary>
/// <param name="httpClient">http客户端</param>
/// <param name="options">选项</param>
/// <param name="logger">日志器</param>
public AccountClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<AccountClient> logger)
{
this.httpClient = httpClient;
this.options = options;
this.logger = logger;
}
/// <summary>
/// 异步生成祈愿验证密钥
/// 需要stoken
/// </summary>
/// <param name="user">用户</param>
/// <param name="data">提交数据</param>
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
{
Response<GameAuthKey>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
.SetReferer("https://app.mihoyo.com")
.UsingDynamicSecret1(SaltType.K2)
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.AppAccountGenAuthKey, data, options, logger, token)
.ConfigureAwait(false);
return resp?.Data;
}
/// <summary>
/// 异步获取 Ltoken
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>Ltoken</returns>
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.PROD)]
public async Task<string?> GetLTokenBySTokenAsync(User user, CancellationToken token)
{
Response<LtokenWrapper>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
.UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AppAccountGetLTokenBySToken)
.TryCatchGetFromJsonAsync<Response<LtokenWrapper>>(token)
.ConfigureAwait(false);
return resp?.Data?.Ltoken;
}
}

View File

@@ -0,0 +1,26 @@
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Web.Hoyolab.App.Account;
/// <summary>
/// Ltoken 包装器
/// </summary>
public class LtokenWrapper
{
/// <summary>
/// Ltoken
/// </summary>
[JsonPropertyName("ltoken")]
public string Ltoken { get; set; } = default!;
}

View File

@@ -39,7 +39,7 @@ internal class PassportClient
/// <param name="user">用户</param> /// <param name="user">用户</param>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>验证信息</returns> /// <returns>验证信息</returns>
[ApiInformation(Cookie = CookieType.Ltoken)] [ApiInformation(Cookie = CookieType.All)]
public async Task<VerifyInformation?> VerifyLtokenAsync(User user, CancellationToken token) public async Task<VerifyInformation?> VerifyLtokenAsync(User user, CancellationToken token)
{ {
Response<VerifyInformation>? response = await httpClient Response<VerifyInformation>? response = await httpClient

View File

@@ -12,7 +12,7 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth;
/// 账户信息 /// 账户信息
/// </summary> /// </summary>
[SuppressMessage("", "SA1600")] [SuppressMessage("", "SA1600")]
public class AccountInfo public class ActionTicketAccountInfo
{ {
[JsonPropertyName("is_realname")] [JsonPropertyName("is_realname")]
public bool IsRealname { get; set; } public bool IsRealname { get; set; }

View File

@@ -29,5 +29,5 @@ public class ActionTicketWrapper
/// 账户信息 /// 账户信息
/// </summary> /// </summary>
[JsonPropertyName("account_info")] [JsonPropertyName("account_info")]
public AccountInfo AccountInfo { get; set; } = default!; public ActionTicketAccountInfo AccountInfo { get; set; } = default!;
} }

View File

@@ -69,4 +69,21 @@ internal class BindingClient
return EnumerableExtension.EmptyIfNull(resp?.Data?.List); return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
} }
/// <summary>
/// 获取用户角色信息
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.Stoken)]
public async Task<List<UserGameRole>> GetUserGameRolesByStokenAsync(User user, CancellationToken token = default)
{
Response<ListWrapper<UserGameRole>>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
.TryCatchGetFromJsonAsync<Response<ListWrapper<UserGameRole>>>(ApiEndpoints.UserGameRolesByStoken, options, logger, token)
.ConfigureAwait(false);
return EnumerableExtension.EmptyIfNull(resp?.Data?.List);
}
} }

View File

@@ -3,6 +3,7 @@
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Model.Entity; using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using Snap.Hutao.Web.Response; using Snap.Hutao.Web.Response;
using System.Net.Http; using System.Net.Http;
@@ -40,13 +41,14 @@ internal class BindingClient2
/// <param name="data">提交数据</param> /// <param name="data">提交数据</param>
/// <param name="token">取消令牌</param> /// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns> /// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default) public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
{ {
Response<GameAuthKey>? resp = await httpClient Response<GameAuthKey>? resp = await httpClient
.SetUser(user, CookieType.Stoken) .SetUser(user, CookieType.Stoken)
.SetReferer("https://app.mihoyo.com") .SetReferer("https://app.mihoyo.com")
.UsingDynamicSecret1(SaltType.LK2) .UsingDynamicSecret1(SaltType.K2)
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.GenAuthKey, data, options, logger, token) .TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token)
.ConfigureAwait(false); .ConfigureAwait(false);
return resp?.Data; return resp?.Data;

View File

@@ -5,6 +5,8 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
/// <summary> /// <summary>
/// 验证密钥提交数据 /// 验证密钥提交数据
/// im_css?
/// {"auth_appid":"im_ccs","game_biz":"bbs_cn","game_uid":0,"region":""}
/// </summary> /// </summary>
public sealed class GenAuthKeyData public sealed class GenAuthKeyData
{ {

View File

@@ -43,6 +43,11 @@ public enum KnownReturnCode : int
/// </summary> /// </summary>
RET_NEED_AIGIS = -3101, RET_NEED_AIGIS = -3101,
/// <summary>
/// 登录信息已失效,请重新登录
/// </summary>
LoginDataOutdated = -262,
/// <summary> /// <summary>
/// 访问过于频繁 /// 访问过于频繁
/// </summary> /// </summary>