Fetch cert allow insecure

This commit is contained in:
DHR60 2026-03-27 18:55:19 +08:00
parent 7329dbae11
commit 2918cc92ac
14 changed files with 130 additions and 10 deletions

View file

@ -203,7 +203,7 @@ public class CertPemManager
/// <summary>
/// Get certificate in PEM format from a server with CA pinning validation
/// </summary>
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
/// <summary>
/// Get certificate chain in PEM format from a server with CA pinning validation
/// </summary>
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4)
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4, bool allowInsecure = false)
{
var pemList = new List<string>();
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
/// </summary>
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))
{

View file

@ -2571,6 +2571,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Allow insecure cert fetch (self-signed) 的本地化字符串。
/// </summary>
public static string TbAllowInsecureCertFetch {
get {
return ResourceManager.GetString("TbAllowInsecureCertFetch", resourceCulture);
}
}
/// <summary>
/// 查找类似 Only for fetching self-signed certificates. This may expose you to MITM risks. 的本地化字符串。
/// </summary>
public static string TbAllowInsecureCertFetchTips {
get {
return ResourceManager.GetString("TbAllowInsecureCertFetchTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 ALPN 的本地化字符串。
/// </summary>

View file

@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>Allow insecure cert fetch (self-signed)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
</data>
</root>

View file

@ -1695,4 +1695,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>Allow insecure cert fetch (self-signed)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
</data>
</root>

View file

@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>Allow insecure cert fetch (self-signed)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
</data>
</root>

View file

@ -1698,4 +1698,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>Allow insecure cert fetch (self-signed)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
</data>
</root>

View file

@ -1698,4 +1698,10 @@
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>Allow insecure cert fetch (self-signed)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>Only for fetching self-signed certificates. This may expose you to MITM risks.</value>
</data>
</root>

View file

@ -1695,4 +1695,10 @@
<data name="TbLegacyProtect" xml:space="preserve">
<value>旧版 TUN 保护</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>允许不安全获取证书(自签名)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>仅用于抓取自签证书,存在中间人风险。</value>
</data>
</root>

View file

@ -1695,4 +1695,10 @@
<data name="TbLegacyProtect" xml:space="preserve">
<value>Legacy TUN Protect</value>
</data>
<data name="TbAllowInsecureCertFetch" xml:space="preserve">
<value>允許不安全獲取證書(自簽名)</value>
</data>
<data name="TbAllowInsecureCertFetchTips" xml:space="preserve">
<value>僅用於抓取自簽證書,存在中間人風險。</value>
</data>
</root>

View file

@ -73,6 +73,9 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public bool NaiveQuic { get; set; }
[Reactive]
public bool AllowInsecureCertFetch { get; set; }
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> 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);
}

View file

@ -1008,6 +1008,25 @@
Margin="{StaticResource Margin4}"
Content="{x:Static resx:ResUI.TbFetchCertChain}" />
</StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbAllowInsecureCertFetch}" />
<ToggleSwitch
x:Name="togAllowInsecureCertFetch"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</StackPanel>
<TextBlock
x:Name="txtAllowInsecureCertFetchTips"
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Foreground="#FFD32F2F"
IsVisible="False"
Text="{x:Static resx:ResUI.TbAllowInsecureCertFetchTips}"
TextWrapping="Wrap" />
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"

View file

@ -214,6 +214,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
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);

View file

@ -1269,6 +1269,27 @@
Content="{x:Static resx:ResUI.TbFetchCertChain}"
Style="{StaticResource DefButton}" />
</StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbAllowInsecureCertFetch}" />
<ToggleButton
x:Name="togAllowInsecureCertFetch"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</StackPanel>
<TextBlock
x:Name="txtAllowInsecureCertFetchTips"
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Foreground="#FFD32F2F"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbAllowInsecureCertFetchTips}"
TextWrapping="Wrap"
Visibility="Collapsed" />
<TextBlock
Width="400"
Margin="{StaticResource Margin4}"

View file

@ -208,6 +208,10 @@ public partial class AddServerWindow
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.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);