Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 89 additions & 30 deletions GameFrameX.Localization/Keys.StartUp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,85 @@ public static partial class Keys
/// </summary>
public static class StartUp
{
/// <summary>
/// 启动期 TLS 证书错误消息。
/// </summary>
/// <remarks>TLS certificate error messages used during startup.</remarks>
public static class Certificates
{
/// <summary>
/// 已启用 {0},但证书路径配置 {1} 为空。
/// </summary>
/// <remarks>
/// {0} is enabled but certificate path setting {1} is empty.
/// 键名: StartUp.Certificates.PathMissing
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string PathMissing = "StartUp.Certificates.PathMissing";

/// <summary>
/// {0} 证书文件不存在:{1}。
/// </summary>
/// <remarks>
/// {0} certificate file does not exist: {1}.
/// 键名: StartUp.Certificates.FileNotFound
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string FileNotFound = "StartUp.Certificates.FileNotFound";

/// <summary>
/// {0} PEM 证书必须配置独立私钥路径 {1}。
/// </summary>
/// <remarks>
/// {0} PEM certificate requires a separate private key path in {1}.
/// 键名: StartUp.Certificates.KeyPathMissing
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string KeyPathMissing = "StartUp.Certificates.KeyPathMissing";

/// <summary>
/// {0} PEM 私钥文件不存在:{1}。
/// </summary>
/// <remarks>
/// {0} PEM private key file does not exist: {1}.
/// 键名: StartUp.Certificates.KeyFileNotFound
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string KeyFileNotFound = "StartUp.Certificates.KeyFileNotFound";

/// <summary>
/// {0} 证书不包含私钥:{1}。
/// </summary>
/// <remarks>
/// {0} certificate has no private key: {1}.
/// 键名: StartUp.Certificates.PrivateKeyMissing
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string PrivateKeyMissing = "StartUp.Certificates.PrivateKeyMissing";

/// <summary>
/// {0} 证书加载失败(证书或私钥损坏、密码错误或私钥不匹配):{1}。
/// </summary>
/// <remarks>
/// {0} certificate failed to load (invalid certificate or key, incorrect password, or mismatched private key): {1}.
/// 键名: StartUp.Certificates.LoadFailed
/// 参数: {0} - 协议名称,{1} - 配置项名称或文件路径
/// </remarks>
public const string LoadFailed = "StartUp.Certificates.LoadFailed";

/// <summary>
/// {0} 私钥使用了传统 OpenSSL 加密格式(Proc-Type/DEK-Info 头),不受支持,请先用 openssl pkcs8 -topk8 转换为加密 PKCS#8 格式:{1}。
/// </summary>
/// <remarks>
/// {0} private key uses the legacy OpenSSL encrypted format (Proc-Type/DEK-Info headers), which is not supported;
/// convert it to encrypted PKCS#8 with openssl pkcs8 -topk8 first: {1}.
/// 键名: StartUp.Certificates.TraditionalEncryptedKeyUnsupported
/// 参数: {0} - 协议名称,{1} - 私钥文件路径
/// </remarks>
public const string TraditionalEncryptedKeyUnsupported = "StartUp.Certificates.TraditionalEncryptedKeyUnsupported";

}

/// <summary>
/// 服务器类型:{0} 停止! 终止原因:{1} 配置信息: {2}
/// </summary>
Expand Down Expand Up @@ -430,6 +509,16 @@ public static class TcpServer
/// </summary>
public static class WebSocketServer
{
/// <summary>
/// 已配置 WSS TLS 监听,端口:{0}。
/// </summary>
/// <remarks>
/// WSS TLS listener configured on port {0}.
/// 键名: StartUp.WebSocketServer.SecureListenerConfigured
/// 参数: {0} - WSS 端口
/// </remarks>
public const string SecureListenerConfigured = "StartUp.WebSocketServer.SecureListenerConfigured";

/// <summary>
/// 启动WebSocket服务器 类型: {0}, 端口: {1}
/// </summary>
Expand Down Expand Up @@ -588,36 +677,6 @@ public static class HttpExceptions
/// 用途: 当HTTP地址不以/结束时抛出异常
/// </remarks>
public const string AddressMustEndWithSlash = "StartUp.HttpExceptions.AddressMustEndWithSlash";

/// <summary>
/// HTTPS port [{0}] is configured but HttpsCertPath (PFX file path) is missing
/// </summary>
/// <remarks>
/// 键名: StartUp.HttpExceptions.HttpsCertPathMissing
/// 用途: 配置了 HTTPS 端口但未配置证书路径时抛出异常
/// 参数: {0} - HTTPS 端口
/// </remarks>
public const string HttpsCertPathMissing = "StartUp.HttpExceptions.HttpsCertPathMissing";

/// <summary>
/// HTTPS certificate file not found: {0}
/// </summary>
/// <remarks>
/// 键名: StartUp.HttpExceptions.HttpsCertFileNotFound
/// 用途: HTTPS 证书文件不存在时抛出异常
/// 参数: {0} - 证书文件路径
/// </remarks>
public const string HttpsCertFileNotFound = "StartUp.HttpExceptions.HttpsCertFileNotFound";

/// <summary>
/// HTTPS certificate failed to load (wrong password or corrupted file): {0}
/// </summary>
/// <remarks>
/// 键名: StartUp.HttpExceptions.HttpsCertLoadFailed
/// 用途: HTTPS 证书加载失败时抛出异常
/// 参数: {0} - 证书文件路径
/// </remarks>
public const string HttpsCertLoadFailed = "StartUp.HttpExceptions.HttpsCertLoadFailed";
}

/// <summary>
Expand Down
33 changes: 24 additions & 9 deletions GameFrameX.Localization/Localization/Messages/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -762,15 +762,6 @@
<value>Address must start with slash. Address: {0}.</value>
</data>

<data name="StartUp.HttpExceptions.HttpsCertPathMissing" xml:space="preserve">
<value>HTTPS port [{0}] is configured but HttpsCertPath (PFX file path) is missing; cannot enable HTTPS listening.</value>
</data>
<data name="StartUp.HttpExceptions.HttpsCertFileNotFound" xml:space="preserve">
<value>HTTPS certificate file not found: {0}</value>
</data>
<data name="StartUp.HttpExceptions.HttpsCertLoadFailed" xml:space="preserve">
<value>HTTPS certificate failed to load (wrong password or corrupted file): {0}</value>
</data>
<data name="Network.Http.SignSecretMissing" xml:space="preserve">
<value>HTTP signature validation is enabled but HttpSignSecret is not configured; every request is rejected (fail-closed).</value>
</data>
Expand Down Expand Up @@ -2403,4 +2394,28 @@
<data name="Client.Bot.MatchResumeEnterResponse" xml:space="preserve">
<value>Bot-{0} post-resume re-enter response MatchId={1} Started={2} ErrorCode={3}</value>
</data>
<data name="StartUp.Certificates.PathMissing" xml:space="preserve">
<value>{0} is enabled but certificate path setting {1} is empty.</value>
</data>
<data name="StartUp.Certificates.FileNotFound" xml:space="preserve">
<value>{0} certificate file does not exist: {1}.</value>
</data>
<data name="StartUp.Certificates.KeyPathMissing" xml:space="preserve">
<value>{0} PEM certificate requires a separate private key path in {1}.</value>
</data>
<data name="StartUp.Certificates.KeyFileNotFound" xml:space="preserve">
<value>{0} PEM private key file does not exist: {1}.</value>
</data>
<data name="StartUp.Certificates.PrivateKeyMissing" xml:space="preserve">
<value>{0} certificate has no private key: {1}.</value>
</data>
<data name="StartUp.Certificates.LoadFailed" xml:space="preserve">
<value>{0} certificate failed to load (invalid certificate or key, incorrect password, or mismatched private key): {1}.</value>
</data>
<data name="StartUp.Certificates.TraditionalEncryptedKeyUnsupported" xml:space="preserve">
<value>{0} private key uses the legacy OpenSSL encrypted format (Proc-Type/DEK-Info headers), which is not supported; convert it to encrypted PKCS#8 with openssl pkcs8 -topk8 first: {1}.</value>
</data>
<data name="StartUp.WebSocketServer.SecureListenerConfigured" xml:space="preserve">
<value>WSS TLS listener configured on port {0}.</value>
</data>
</root>
33 changes: 24 additions & 9 deletions GameFrameX.Localization/Localization/Messages/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -815,15 +815,6 @@
<value>地址必须以斜杠开头。地址:{0}。</value>
</data>

<data name="StartUp.HttpExceptions.HttpsCertPathMissing" xml:space="preserve">
<value>已配置 HTTPS 端口 [{0}],但 HttpsCertPath(PFX 文件路径)未配置,无法启用 HTTPS 监听。</value>
</data>
<data name="StartUp.HttpExceptions.HttpsCertFileNotFound" xml:space="preserve">
<value>HTTPS 证书文件不存在:{0}</value>
</data>
<data name="StartUp.HttpExceptions.HttpsCertLoadFailed" xml:space="preserve">
<value>HTTPS 证书加载失败(密码错误或文件损坏):{0}</value>
</data>
<data name="Network.Http.SignSecretMissing" xml:space="preserve">
<value>HTTP 签名校验已开启,但 HttpSignSecret 未配置,请求一律拒绝(fail-closed)。</value>
</data>
Expand Down Expand Up @@ -2399,4 +2390,28 @@
<data name="Client.Bot.MatchResumeEnterResponse" xml:space="preserve">
<value>机器人-{0}恢复后重入场响应 MatchId={1} Started={2} ErrorCode={3}</value>
</data>
<data name="StartUp.Certificates.PathMissing" xml:space="preserve">
<value>已启用 {0},但证书路径配置 {1} 为空。</value>
</data>
<data name="StartUp.Certificates.FileNotFound" xml:space="preserve">
<value>{0} 证书文件不存在:{1}。</value>
</data>
<data name="StartUp.Certificates.KeyPathMissing" xml:space="preserve">
<value>{0} PEM 证书必须配置独立私钥路径 {1}。</value>
</data>
<data name="StartUp.Certificates.KeyFileNotFound" xml:space="preserve">
<value>{0} PEM 私钥文件不存在:{1}。</value>
</data>
<data name="StartUp.Certificates.PrivateKeyMissing" xml:space="preserve">
<value>{0} 证书不包含私钥:{1}。</value>
</data>
<data name="StartUp.Certificates.LoadFailed" xml:space="preserve">
<value>{0} 证书加载失败(证书或私钥损坏、密码错误或私钥不匹配):{1}。</value>
</data>
<data name="StartUp.Certificates.TraditionalEncryptedKeyUnsupported" xml:space="preserve">
<value>{0} 私钥使用了传统 OpenSSL 加密格式(Proc-Type/DEK-Info 头),不受支持;请先使用 openssl pkcs8 -topk8 转换为加密 PKCS#8 格式:{1}。</value>
</data>
<data name="StartUp.WebSocketServer.SecureListenerConfigured" xml:space="preserve">
<value>已配置 WSS TLS 监听,端口:{0}。</value>
</data>
</root>
37 changes: 6 additions & 31 deletions GameFrameX.StartUp/AppStartUpBase.ByHTTPServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,41 +333,16 @@ private void ConfigureMetricsPortListen(KestrelServerOptions options)
}

/// <summary>
/// 加载 HTTPS 证书(PFX),供 <see cref="AppSetting.HttpsPort"/> 监听使用;任一配置错误在 Kestrel 监听前 fail-fast。
/// 加载 HTTPS 的 PFX 或 PEM 证书,在 Kestrel 监听前校验证书配置。
/// </summary>
/// <remarks>
/// Loads the HTTPS certificate (PFX) for the <see cref="AppSetting.HttpsPort"/> listener; any configuration error
/// fails fast before Kestrel starts listening.
/// 调用前提:<paramref name="setting"/> 的 <c>HttpsPort</c> 大于 0。
/// </remarks>
/// <remarks>Loads the HTTPS PFX or PEM certificate and validates configuration before Kestrel listens.</remarks>
/// <param name="setting">应用配置 / Application setting</param>
/// <returns>已加载的证书 / The loaded certificate</returns>
/// <exception cref="InvalidOperationException">证书路径为空 / The certificate path is empty</exception>
/// <exception cref="InvalidOperationException">证书文件不存在 / The certificate file does not exist</exception>
/// <exception cref="InvalidOperationException">证书加载失败(密码错误或文件损坏)/ The certificate failed to load (wrong password or corrupted file)</exception>
/// <returns>包含私钥的证书 / Certificate with its private key</returns>
/// <exception cref="InvalidOperationException">证书配置或加载失败 / Certificate configuration or loading failed</exception>
internal static X509Certificate2 LoadHttpsCertificate(AppSetting setting)
{
if (setting.HttpsCertPath.IsNullOrWhiteSpace())
{
// Localization: StartUp.HttpExceptions.HttpsCertPathMissing - 已配置 HTTPS 端口 [{0}],但 HttpsCertPath 未配置(PFX 文件路径),无法启用 HTTPS 监听
throw new InvalidOperationException(LocalizationService.GetString(Localization.Keys.StartUp.HttpExceptions.HttpsCertPathMissing, setting.HttpsPort));
}

if (!File.Exists(setting.HttpsCertPath))
{
// Localization: StartUp.HttpExceptions.HttpsCertFileNotFound - HTTPS 证书文件不存在: {0}
throw new InvalidOperationException(LocalizationService.GetString(Localization.Keys.StartUp.HttpExceptions.HttpsCertFileNotFound, setting.HttpsCertPath));
}

try
{
return X509CertificateLoader.LoadPkcs12FromFile(setting.HttpsCertPath, setting.HttpsCertPassword, X509KeyStorageFlags.DefaultKeySet, Pkcs12LoaderLimits.Defaults);
}
catch (Exception e)
{
// Localization: StartUp.HttpExceptions.HttpsCertLoadFailed - HTTPS 证书加载失败(密码错误或文件损坏): {0}
throw new InvalidOperationException(LocalizationService.GetString(Localization.Keys.StartUp.HttpExceptions.HttpsCertLoadFailed, setting.HttpsCertPath), e);
}
return ServerCertificateLoader.Load("HTTPS", setting.HttpsCertPath, setting.HttpsCertificateKeyPath,
setting.HttpsCertPassword, nameof(setting.HttpsCertPath), nameof(setting.HttpsCertificateKeyPath));
}

/// <summary>
Expand Down
Loading
Loading