diff --git a/v2rayN/ServiceLib/Manager/CertPemManager.cs b/v2rayN/ServiceLib/Manager/CertPemManager.cs
index b1c8d82a..0a58e2de 100644
--- a/v2rayN/ServiceLib/Manager/CertPemManager.cs
+++ b/v2rayN/ServiceLib/Manager/CertPemManager.cs
@@ -203,7 +203,7 @@ public class CertPemManager
///
/// Get certificate in PEM format from a server with CA pinning validation
///
- public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 4)
+ public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 4, bool allowInsecure = false)
{
try
{
@@ -215,12 +215,14 @@ public class CertPemManager
using var client = new TcpClient();
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
- await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
+ var callback = new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
+ ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, allowInsecure));
+ await using var ssl = new SslStream(client.GetStream(), false, callback);
var sslOptions = new SslClientAuthenticationOptions
{
TargetHost = serverName,
- RemoteCertificateValidationCallback = ValidateServerCertificate
+ RemoteCertificateValidationCallback = callback
};
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
@@ -249,7 +251,7 @@ public class CertPemManager
///
/// Get certificate chain in PEM format from a server with CA pinning validation
///
- public async Task<(List, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4)
+ public async Task<(List, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4, bool allowInsecure = false)
{
var pemList = new List();
try
@@ -262,12 +264,14 @@ public class CertPemManager
using var client = new TcpClient();
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
- await using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
+ var callback = new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
+ ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, allowInsecure));
+ await using var ssl = new SslStream(client.GetStream(), false, callback);
var sslOptions = new SslClientAuthenticationOptions
{
TargetHost = serverName,
- RemoteCertificateValidationCallback = ValidateServerCertificate
+ RemoteCertificateValidationCallback = callback
};
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
@@ -300,16 +304,23 @@ public class CertPemManager
/// Validate server certificate with CA pinning
///
private bool ValidateServerCertificate(
- object sender,
+ object _,
X509Certificate? certificate,
X509Chain? chain,
- SslPolicyErrors sslPolicyErrors)
+ SslPolicyErrors sslPolicyErrors,
+ bool allowInsecure)
{
if (certificate == null)
{
return false;
}
+ // In insecure mode, accept any certificate so self-signed certs can be fetched.
+ if (allowInsecure)
+ {
+ return true;
+ }
+
// Check certificate name mismatch
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
{
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 4498d999..7f50a8b5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -2571,6 +2571,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Allow insecure cert fetch (self-signed) 的本地化字符串。
+ ///
+ public static string TbAllowInsecureCertFetch {
+ get {
+ return ResourceManager.GetString("TbAllowInsecureCertFetch", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Only for fetching self-signed certificates. This may expose you to MITM risks. 的本地化字符串。
+ ///
+ public static string TbAllowInsecureCertFetchTips {
+ get {
+ return ResourceManager.GetString("TbAllowInsecureCertFetchTips", resourceCulture);
+ }
+ }
+
///
/// 查找类似 ALPN 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 61ccbf7b..96630b69 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Legacy TUN Protect
+
+ Allow insecure cert fetch (self-signed)
+
+
+ Only for fetching self-signed certificates. This may expose you to MITM risks.
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
index d466eedd..5bf822ea 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
@@ -1695,4 +1695,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Legacy TUN Protect
+
+ Allow insecure cert fetch (self-signed)
+
+
+ Only for fetching self-signed certificates. This may expose you to MITM risks.
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index 89456f99..23b85499 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Legacy TUN Protect
+
+ Allow insecure cert fetch (self-signed)
+
+
+ Only for fetching self-signed certificates. This may expose you to MITM risks.
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 569877d7..e3358b39 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
Legacy TUN Protect
+
+ Allow insecure cert fetch (self-signed)
+
+
+ Only for fetching self-signed certificates. This may expose you to MITM risks.
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 401c237e..d767ccfb 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1698,4 +1698,10 @@
Legacy TUN Protect
+
+ Allow insecure cert fetch (self-signed)
+
+
+ Only for fetching self-signed certificates. This may expose you to MITM risks.
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index 8c628644..b5d5db1b 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1695,4 +1695,10 @@
旧版 TUN 保护
+
+ 允许不安全获取证书(自签名)
+
+
+ 仅用于抓取自签证书,存在中间人风险。
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index 59a9fc9f..57881c46 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1695,4 +1695,10 @@
Legacy TUN Protect
+
+ 允許不安全獲取證書(自簽名)
+
+
+ 僅用於抓取自簽證書,存在中間人風險。
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
index e5c307c8..424d464f 100644
--- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
@@ -73,6 +73,9 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public bool NaiveQuic { get; set; }
+ [Reactive]
+ public bool AllowInsecureCertFetch { get; set; }
+
public ReactiveCommand FetchCertCmd { get; }
public ReactiveCommand FetchCertChainCmd { get; }
public ReactiveCommand SaveCmd { get; }
@@ -272,7 +275,7 @@ public class AddServerViewModel : MyReactiveObject
domain += $":{SelectedSource.Port}";
}
- (Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
+ (Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName, allowInsecure: AllowInsecureCertFetch);
UpdateCertTip(certError);
}
@@ -297,7 +300,7 @@ public class AddServerViewModel : MyReactiveObject
domain += $":{SelectedSource.Port}";
}
- var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
+ var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName, allowInsecure: AllowInsecureCertFetch);
Cert = CertPemManager.ConcatenatePemChain(certs);
UpdateCertTip(certError);
}
diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
index 4dc378b4..d975cab9 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
@@ -1008,6 +1008,25 @@
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbFetchCertChain}" />
+
+
+
+
+
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);
+ this.Bind(ViewModel, vm => vm.AllowInsecureCertFetch, v => v.togAllowInsecureCertFetch.IsChecked).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.AllowInsecureCertFetch, v => v.txtAllowInsecureCertFetchTips.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.SelectedValue).DisposeWith(disposables);
diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml
index 1eb28d9d..68d5301d 100644
--- a/v2rayN/v2rayN/Views/AddServerWindow.xaml
+++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml
@@ -1269,6 +1269,27 @@
Content="{x:Static resx:ResUI.TbFetchCertChain}"
Style="{StaticResource DefButton}" />
+
+
+
+
+
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);
+ this.Bind(ViewModel, vm => vm.AllowInsecureCertFetch, v => v.togAllowInsecureCertFetch.IsChecked).DisposeWith(disposables);
+ this.WhenAnyValue(x => x.ViewModel.AllowInsecureCertFetch)
+ .Select(b => b ? Visibility.Visible : Visibility.Collapsed)
+ .BindTo(this, v => v.txtAllowInsecureCertFetchTips.Visibility);
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.Text).DisposeWith(disposables);