From a871e9fbfb7a257167b1a68c3cf942695f05d4d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:15:51 +0000 Subject: [PATCH 1/8] feat: add cipherSuites option and "unsafe" fingerprint to TLS UI settings --- v2rayN/ServiceLib/Global.cs | 1 + .../Models/CoreConfigs/V2rayConfig.cs | 2 ++ .../ServiceLib/Models/Entities/ProfileItem.cs | 1 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 ++++++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ .../CoreConfig/V2ray/V2rayOutboundService.cs | 1 + .../Views/AddServerWindow.axaml | 30 +++++++++++++----- .../Views/AddServerWindow.axaml.cs | 1 + v2rayN/v2rayN/Views/AddServerWindow.xaml | 31 ++++++++++++++----- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 1 + 10 files changed, 65 insertions(+), 15 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index cb651ac6908..82580efc7bb 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -413,6 +413,7 @@ public class Global "qq", "random", "randomized", + "unsafe", "" ]; diff --git a/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs b/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs index d5f189e8a2f..15807af1d00 100644 --- a/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs @@ -356,6 +356,8 @@ public class TlsSettings4Ray public List? alpn { get; set; } + public List? cipherSuites { get; set; } + public string? fingerprint { get; set; } public bool? show { get; set; } diff --git a/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs b/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs index 5daca9888a0..56eded2608f 100644 --- a/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs @@ -183,6 +183,7 @@ public bool GetAllowInsecure() public string AllowInsecure { get; set; } public string Sni { get; set; } public string Alpn { get; set; } = string.Empty; + public string CipherSuites { get; set; } public string Fingerprint { get; set; } public string PublicKey { get; set; } public string ShortId { get; set; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index cd70be7a1c8..8e5bfbfd9cd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2796,6 +2796,15 @@ public static string TbCancel { } } + /// + /// 查找类似 Cipher Suites 的本地化字符串。 + /// + public static string TbCipherSuites { + get { + return ResourceManager.GetString("TbCipherSuites", resourceCulture); + } + } + /// /// 查找类似 Certificate Pinning 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 67339c2b6a9..f44d418f3c6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -585,6 +585,9 @@ Alter ID + + Cipher Suites + Fingerprint diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index c5251f9ca73..2f1103af30e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -382,6 +382,7 @@ private void FillBoundStreamSettings(Outbounds4Ray outbound) { allowInsecure = _node.GetAllowInsecure(), alpn = _node.GetAlpn(), + cipherSuites = _node.CipherSuites.IsNullOrEmpty() ? null : Utils.String2List(_node.CipherSuites), fingerprint = _node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : _node.Fingerprint, echConfigList = _node.EchConfigList.NullIfEmpty(), verifyPeerCertByName = _node.VerifyPeerCertByName.NullIfEmpty(), diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 8d92036f231..86447e7d31f 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -1109,7 +1109,7 @@ Grid.Row="8" ColumnDefinitions="300,Auto" IsVisible="False" - RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> + + + diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index f5042a6aefc..fe500bb3ce1 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -236,6 +236,7 @@ public AddServerWindow(ProfileItem profileItem) this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.CipherSuites, v => v.txtCipherSuites.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index 6d506d8c8dd..077b2d3ad6c 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -1422,6 +1422,7 @@ + @@ -1481,16 +1482,32 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" + Text="{x:Static resx:ResUI.TbCipherSuites}" /> + + + diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 88e5153e9df..059fbd111df 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -234,6 +234,7 @@ public AddServerWindow(ProfileItem profileItem) this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.CipherSuites, v => v.txtCipherSuites.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables); From b2a341eddda5eeb70567e8ed9cbed5abfb92ea61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:18:35 +0000 Subject: [PATCH 2/8] fix: initialize CipherSuites to string.Empty for null safety --- v2rayN/ServiceLib/Models/Entities/ProfileItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs b/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs index 56eded2608f..09eb1d79139 100644 --- a/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs @@ -183,7 +183,7 @@ public bool GetAllowInsecure() public string AllowInsecure { get; set; } public string Sni { get; set; } public string Alpn { get; set; } = string.Empty; - public string CipherSuites { get; set; } + public string CipherSuites { get; set; } = string.Empty; public string Fingerprint { get; set; } public string PublicKey { get; set; } public string ShortId { get; set; } From cde18809d57577a4dd72a1958883757f0669f912 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:28:31 +0000 Subject: [PATCH 3/8] fix: cipherSuites should be string (not list) per xray-core spec; add missing ConfigHandler copy --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 + v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs | 2 +- .../Services/CoreConfig/V2ray/V2rayOutboundService.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index f1fc385fc18..9dd5306099d 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -250,6 +250,7 @@ public static async Task AddServer(Config config, ProfileItem profileItem) item.AllowInsecure = profileItem.AllowInsecure; item.Fingerprint = profileItem.Fingerprint; item.Alpn = profileItem.Alpn; + item.CipherSuites = profileItem.CipherSuites; item.PublicKey = profileItem.PublicKey; item.ShortId = profileItem.ShortId; diff --git a/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs b/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs index 15807af1d00..dbe0e1c7dbc 100644 --- a/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs @@ -356,7 +356,7 @@ public class TlsSettings4Ray public List? alpn { get; set; } - public List? cipherSuites { get; set; } + public string? cipherSuites { get; set; } public string? fingerprint { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 2f1103af30e..fe546335720 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -382,7 +382,7 @@ private void FillBoundStreamSettings(Outbounds4Ray outbound) { allowInsecure = _node.GetAllowInsecure(), alpn = _node.GetAlpn(), - cipherSuites = _node.CipherSuites.IsNullOrEmpty() ? null : Utils.String2List(_node.CipherSuites), + cipherSuites = _node.CipherSuites.IsNullOrEmpty() ? null : _node.CipherSuites, fingerprint = _node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : _node.Fingerprint, echConfigList = _node.EchConfigList.NullIfEmpty(), verifyPeerCertByName = _node.VerifyPeerCertByName.NullIfEmpty(), From 1db861104ca2acd8d129f40d1ed7e5164c70786c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:39:46 +0000 Subject: [PATCH 4/8] feat: add TbCipherSuites to all locale resource files --- v2rayN/ServiceLib/Resx/ResUI.fa.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.id.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 +++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 +++ 7 files changed, 21 insertions(+) diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa.resx b/v2rayN/ServiceLib/Resx/ResUI.fa.resx index 6c55b7deff5..933d28c1b34 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa.resx @@ -585,6 +585,9 @@ AlterId + + Cipher Suites + اثرانگشت diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index cfee0bd9c11..5245bf304ec 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -585,6 +585,9 @@ ID supplémentaire (alterId) + + Cipher Suites + Empreinte diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 756e8e32e22..04cf1e116c3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -585,6 +585,9 @@ Alter ID + + Cipher Suites + Ujjlenyomat diff --git a/v2rayN/ServiceLib/Resx/ResUI.id.resx b/v2rayN/ServiceLib/Resx/ResUI.id.resx index 73c26704305..8d1791ed8e8 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.id.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.id.resx @@ -585,6 +585,9 @@ Alter ID + + Cipher Suites + Sidik jari diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index c0c546c2717..789e34a3ff4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -585,6 +585,9 @@ AlterId + + Cipher Suites + Отпечаток diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index e0a84d3c289..287eb29a02b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -585,6 +585,9 @@ 额外 ID (alterId) + + Cipher Suites + Fingerprint diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 3d3d582b314..c391bf5bd44 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -585,6 +585,9 @@ 額外 ID (alterid) + + Cipher Suites + Fingerprint From d3ebb74a03fff9930ac6d8b9754cadb545c30bab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 00:53:26 +0000 Subject: [PATCH 5/8] fix: disable txtCipherSuites for Hysteria2 and Naive protocols --- v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs | 2 ++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index fe500bb3ce1..b6ae665c1fa 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -76,6 +76,7 @@ public AddServerWindow(ProfileItem profileItem) gridTransport.IsVisible = false; cmbFingerprint.IsEnabled = false; cmbAlpn.IsEnabled = false; + txtCipherSuites.IsEnabled = false; break; case EConfigType.TUIC: @@ -115,6 +116,7 @@ public AddServerWindow(ProfileItem profileItem) gridFinalmask.IsVisible = false; cmbFingerprint.IsEnabled = false; cmbAlpn.IsEnabled = false; + txtCipherSuites.IsEnabled = false; togAllowInsecure.IsEnabled = false; cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls; diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 059fbd111df..b4625b4467c 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -75,6 +75,7 @@ public AddServerWindow(ProfileItem profileItem) gridTransport.Visibility = Visibility.Collapsed; cmbFingerprint.IsEnabled = false; cmbAlpn.IsEnabled = false; + txtCipherSuites.IsEnabled = false; break; case EConfigType.TUIC: @@ -114,6 +115,7 @@ public AddServerWindow(ProfileItem profileItem) gridFinalmask.Visibility = Visibility.Collapsed; cmbFingerprint.IsEnabled = false; cmbAlpn.IsEnabled = false; + txtCipherSuites.IsEnabled = false; togAllowInsecure.IsEnabled = false; cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls; From e12646de228fcf3ae58467f42e99d0057e236780 Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Fri, 10 Jul 2026 05:45:21 +0330 Subject: [PATCH 6/8] fix: sing-box "unsafe" fingerprint guard and CipherSuites resets (follow-up to #1) The "unsafe" fingerprint added in #1 is Xray-only: sing-box passes the node fingerprint into tls.utls.fingerprint and rejects unknown uTLS names at startup, so any node with fingerprint "unsafe" running on the sing-box core failed to connect. This is also reachable indirectly via AddServerCommon, which stamps the global default fingerprint (settable to "unsafe" in options) onto REALITY nodes. Skip uTLS in that case; plain Go TLS matches what Xray's "unsafe" means anyway. Also reset CipherSuites in AddHysteria2Server and AddNaiveServer alongside the existing Fingerprint/Alpn resets, since #1 disabled the textbox for these protocols but left stale values in the database. Co-Authored-By: Claude Fable 5 --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 2 ++ .../Services/CoreConfig/Singbox/SingboxOutboundService.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 9dd5306099d..56403433fb7 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -716,6 +716,7 @@ public static async Task AddHysteria2Server(Config config, ProfileItem prof profileItem.Fingerprint = string.Empty; profileItem.Alpn = string.Empty; //profileItem.Alpn = "h3"; + profileItem.CipherSuites = string.Empty; profileItem.Network = string.Empty; if (profileItem.StreamSecurity.IsNullOrEmpty()) @@ -918,6 +919,7 @@ public static async Task AddNaiveServer(Config config, ProfileItem profileI profileItem.Password = profileItem.Password.TrimEx(); profileItem.Fingerprint = string.Empty; profileItem.Alpn = string.Empty; + profileItem.CipherSuites = string.Empty; profileItem.Network = string.Empty; profileItem.AllowInsecure = string.Empty; if (profileItem.StreamSecurity.IsNullOrEmpty()) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 6e359fc0220..81027a1f2e5 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -422,7 +422,8 @@ private void FillOutboundTls(Outbound4Sbox outbound) insecure = _node.GetAllowInsecure(), alpn = _node.GetAlpn(), }; - if (_node.Fingerprint.IsNotEmpty()) + // "unsafe" is an Xray-only fingerprint; sing-box's uTLS rejects it, so fall back to plain TLS + if (_node.Fingerprint.IsNotEmpty() && _node.Fingerprint != "unsafe") { tls.utls = new Utls4Sbox() { From 13494551543961ef036a04965a35c42dcae52eb4 Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Fri, 10 Jul 2026 06:04:59 +0330 Subject: [PATCH 7/8] feat: round-trip CipherSuites in share links and VMess QR code The fork already round-trips its custom TLS fields (ech, vcn, pcs, pqv) through share-link query parameters and the VMess QR-code JSON, but CipherSuites was missing, so sharing a profile silently dropped it. Add a "cs" query parameter to BaseFmt (exported inside the TLS-only block alongside alpn, since cipherSuites is TLS-only) and a "cs" field to VmessQRCode, mapped in VmessFmt. The key matches the one used by the v2rayNG fork (patterniha/v2rayNG#3) so links carrying cipher suites interop between the two clients. Co-Authored-By: Claude Fable 5 --- v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs | 5 +++++ v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs | 2 ++ v2rayN/ServiceLib/Models/Dto/VmessQRCode.cs | 2 ++ 3 files changed, 9 insertions(+) diff --git a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs index 1427581ec99..2a613032992 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs @@ -67,6 +67,10 @@ protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dicti { dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); } + if (item.CipherSuites.IsNotEmpty()) + { + dicQuery.Add("cs", Utils.UrlEncode(item.CipherSuites)); + } ToUriQueryAllowInsecure(item, ref dicQuery); } if (item.EchConfigList.IsNotEmpty()) @@ -225,6 +229,7 @@ protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item.StreamSecurity = GetQueryValue(query, "security"); item.Sni = GetQueryValue(query, "sni"); item.Alpn = GetQueryDecoded(query, "alpn"); + item.CipherSuites = GetQueryDecoded(query, "cs"); item.Fingerprint = GetQueryDecoded(query, "fp"); item.PublicKey = GetQueryDecoded(query, "pbk"); item.ShortId = GetQueryDecoded(query, "sid"); diff --git a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs index c67725dcd70..c338e3019b7 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs @@ -66,6 +66,7 @@ public class VmessFmt : BaseFmt sni = item.Sni, alpn = item.Alpn, fp = item.Fingerprint, + cs = item.CipherSuites, insecure = item.GetAllowInsecure() ? "1" : "0", vcn = item.VerifyPeerCertByName, pcs = item.CertSha @@ -142,6 +143,7 @@ public class VmessFmt : BaseFmt item.Sni = Utils.ToString(vmessQRCode.sni); item.Alpn = Utils.ToString(vmessQRCode.alpn); item.Fingerprint = Utils.ToString(vmessQRCode.fp); + item.CipherSuites = Utils.ToString(vmessQRCode.cs); item.AllowInsecure = vmessQRCode.insecure == "1" ? Global.StringTrue : string.Empty; item.VerifyPeerCertByName = Utils.ToString(vmessQRCode.vcn); item.CertSha = Utils.ToString(vmessQRCode.pcs); diff --git a/v2rayN/ServiceLib/Models/Dto/VmessQRCode.cs b/v2rayN/ServiceLib/Models/Dto/VmessQRCode.cs index bfb1408b289..473c0d1d3bd 100644 --- a/v2rayN/ServiceLib/Models/Dto/VmessQRCode.cs +++ b/v2rayN/ServiceLib/Models/Dto/VmessQRCode.cs @@ -39,6 +39,8 @@ public class VmessQRCode public string fp { get; set; } = string.Empty; + public string cs { get; set; } = string.Empty; + public string insecure { get; set; } = string.Empty; public string vcn { get; set; } = string.Empty; From 763236d050da68b42713a7dd862320264a8e443b Mon Sep 17 00:00:00 2001 From: patterniha <71074308+patterniha@users.noreply.github.com> Date: Fri, 10 Jul 2026 06:11:33 +0330 Subject: [PATCH 8/8] feat: apply CipherSuites to sing-box TLS outbounds sing-box supports cipher_suites on outbound TLS (a list of the same Go TLS names Xray takes as a colon-separated string), so the setting no longer silently does nothing when a profile runs on the sing-box core. Add cipher_suites to Tls4Sbox and map it in GenOutboundTls for the TLS branch only (REALITY uses uTLS where cipher suites do not apply), splitting the stored string on ":" or ",". Co-Authored-By: Claude Fable 5 --- v2rayN/ServiceLib/Models/CoreConfigs/SingboxConfig.cs | 1 + .../CoreConfig/Singbox/SingboxOutboundService.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/v2rayN/ServiceLib/Models/CoreConfigs/SingboxConfig.cs b/v2rayN/ServiceLib/Models/CoreConfigs/SingboxConfig.cs index a835b15f05e..ccae7472487 100644 --- a/v2rayN/ServiceLib/Models/CoreConfigs/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/CoreConfigs/SingboxConfig.cs @@ -184,6 +184,7 @@ public class Tls4Sbox public string? server_name { get; set; } public bool? insecure { get; set; } public List? alpn { get; set; } + public List? cipher_suites { get; set; } public Utls4Sbox? utls { get; set; } public Reality4Sbox? reality { get; set; } public bool? fragment { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 81027a1f2e5..9be9be49350 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -433,6 +433,15 @@ private void FillOutboundTls(Outbound4Sbox outbound) } if (_node.StreamSecurity == Global.StreamSecurity) { + if (_node.CipherSuites.IsNotEmpty()) + { + // Xray stores cipher suites as a colon-separated string; sing-box wants a list of the same Go TLS names + tls.cipher_suites = _node.CipherSuites + .Split([':', ','], StringSplitOptions.RemoveEmptyEntries) + .Select(c => c.Trim()) + .Where(c => c.IsNotEmpty()) + .ToList(); + } var certs = CertPemManager.ParsePemChain(_node.Cert); if (certs.Count > 0) {