Mar*_*arc 12 c# ssl-certificate xamarin asp.net-core maui
我是移动开发新手,我正在尝试将我的 .NET Maui 应用程序连接到本地 ASP.NET Core 网站 (API)。
我目前被此异常阻止:
System.Net.WebException: 'java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.'
运行dotnet dev-certs https --trust回报A valid HTTPS certificate is already present.
我当前的代码是:
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
if (cert.Issuer.Equals("CN=localhost"))
return true;
return errors == System.Net.Security.SslPolicyErrors.None;
};
var httpclient = new HttpClient(handler);
var test = await httpclient.PostAsync($"https://10.0.2.2:44393/" + uri, new StringContent(serializedItem, Encoding.UTF8, "application/json"));
Run Code Online (Sandbox Code Playgroud)
但问题是我从未输入 ServerCertificateCustomValidationCallback。
我也尝试过
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
return true;
};
Run Code Online (Sandbox Code Playgroud)
但也没有运气。
.NET MAUI 中有什么变化吗?
小智 9
当我尝试让 SignalR 客户端连接我的本地测试服务器时,我遇到了完全相同的问题。在深入研究源代码后,我发现HttpClientHandler实际上使用AndroidMessageHandler它作为其底层处理程序。
虽然AndroidMessageHandler实现了一个属性,但在发送请求时永远不会使用ServerCertificateCustomValidationCallback它的值。此拉取请求中解决了此问题。
目前,要在 Android 上禁用服务器证书验证,您可以实现一个自定义TrustProvider,它将绕过任何证书验证:
using Java.Net;
using Java.Security;
using Java.Security.Cert;
using Javax.Net.Ssl;
namespace MyApp.Platforms.Android
{
internal class DangerousTrustProvider : Provider
{
private const string TRUST_PROVIDER_ALG = "DangerousTrustAlgorithm";
private const string TRUST_PROVIDER_ID = "DangerousTrustProvider";
public DangerousTrustProvider() : base(TRUST_PROVIDER_ID, 1, string.Empty)
{
var key = "TrustManagerFactory." + DangerousTrustManagerFactory.GetAlgorithm();
var val = Java.Lang.Class.FromType(typeof(DangerousTrustManagerFactory)).Name;
Put(key, val);
}
public static void Register()
{
Provider registered = Security.GetProvider(TRUST_PROVIDER_ID);
if (null == registered)
{
Security.InsertProviderAt(new DangerousTrustProvider(), 1);
Security.SetProperty("ssl.TrustManagerFactory.algorithm", TRUST_PROVIDER_ALG);
}
}
public class DangerousTrustManager : X509ExtendedTrustManager
{
public override void CheckClientTrusted(X509Certificate[] chain, string authType, Socket socket) { }
public override void CheckClientTrusted(X509Certificate[] chain, string authType, SSLEngine engine) { }
public override void CheckClientTrusted(X509Certificate[] chain, string authType) { }
public override void CheckServerTrusted(X509Certificate[] chain, string authType, Socket socket) { }
public override void CheckServerTrusted(X509Certificate[] chain, string authType, SSLEngine engine) { }
public override void CheckServerTrusted(X509Certificate[] chain, string authType) { }
public override X509Certificate[] GetAcceptedIssuers() => Array.Empty<X509Certificate>();
}
public class DangerousTrustManagerFactory : TrustManagerFactorySpi
{
protected override void EngineInit(IManagerFactoryParameters mgrparams) { }
protected override void EngineInit(KeyStore keystore) { }
protected override ITrustManager[] EngineGetTrustManagers() => new ITrustManager[] { new DangerousTrustManager() };
public static string GetAlgorithm() => TRUST_PROVIDER_ALG;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您还想禁用主机名验证,则需要动态继承AndroidMessageHandler并覆盖其内部GetSSLHostnameVerifier方法,以返回一个虚拟值IHostNameVerifier:
using Javax.Net.Ssl;
using System.Reflection;
using System.Reflection.Emit;
using Xamarin.Android.Net;
namespace MyApp.Platforms.Android
{
static class DangerousAndroidMessageHandlerEmitter
{
private static Assembly _emittedAssembly = null;
public static void Register(string handlerTypeName = "DangerousAndroidMessageHandler", string assemblyName = "DangerousAndroidMessageHandler")
{
AppDomain.CurrentDomain.AssemblyResolve += (s, e) =>
{
if (e.Name == assemblyName)
{
if (_emittedAssembly == null)
{
_emittedAssembly = Emit(handlerTypeName, assemblyName);
}
return _emittedAssembly;
}
return null;
};
}
private static AssemblyBuilder Emit(string handlerTypeName, string assemblyName)
{
var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule(assemblyName);
DefineDangerousAndroidMessageHandler(module, handlerTypeName);
return assembly;
}
private static void DefineDangerousAndroidMessageHandler(ModuleBuilder module, string handlerTypeName)
{
var typeBuilder = module.DefineType(handlerTypeName, TypeAttributes.Public);
typeBuilder.SetParent(typeof(AndroidMessageHandler));
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod(
"GetSSLHostnameVerifier",
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(IHostnameVerifier),
new[] { typeof(HttpsURLConnection) }
);
var generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Call, typeof(DangerousHostNameVerifier).GetMethod("Create"));
generator.Emit(OpCodes.Ret);
typeBuilder.CreateType();
}
}
public class DangerousHostNameVerifier : Java.Lang.Object, IHostnameVerifier
{
public bool Verify(string hostname, ISSLSession session)
{
return true;
}
public static IHostnameVerifier Create() => new DangerousHostNameVerifier();
}
}
Run Code Online (Sandbox Code Playgroud)
致电DangerousAndroidMessageHandlerEmitter.Register并DangerousTrustProvider在您的MauiProgram:
#if ANDROID && DEBUG
Platforms.Android.DangerousAndroidMessageHandlerEmitter.Register();
Platforms.Android.DangerousTrustProvider.Register();
#endif
Run Code Online (Sandbox Code Playgroud)
最后一步,您需要告诉 Xamarin 使用动态生成的DangerousAndroidMessageHandler. 您应该能够通过AndroidHttpClientHandlerType在文件中设置处理程序类型的完全限定名称来执行此操作csproj:
<PropertyGroup>
<AndroidHttpClientHandlerType>DangerousAndroidMessageHandler, DangerousAndroidMessageHandler</AndroidHttpClientHandlerType>
</PropertyGroup>
Run Code Online (Sandbox Code Playgroud)
或者将 Android 运行时环境变量设置XA_HTTP_CLIENT_HANDLER_TYPE为处理程序的名称:
XA_HTTP_CLIENT_HANDLER_TYPE=DangerousAndroidMessageHandler, DangerousAndroidMessageHandler
Run Code Online (Sandbox Code Playgroud)
上述解决方法也适用于ClientWebSocket任何其他使用SslStream. 这意味着您可以使用 WebSocket 传输连接到测试 SignalR 服务器(这就是我试图实现的目标)。
请记住,仅在调试版本中执行此操作。
| 归档时间: |
|
| 查看次数: |
10648 次 |
| 最近记录: |