Dynamics CRM插件沙箱模式下用证书调用Web API遇权限问题
Dynamics 365插件加载客户端证书时的权限错误解决方案
问题详情
在Dynamics 365在线环境的插件中调用某Web API时,加载客户端证书阶段触发权限错误:
Permission request type 'System.Security.Permissions.KeyContainerPermission, mscorlib, Version=4.0.0.0,
错误发生在代码行:X509Certificate2 certificate = new X509Certificate2(certificateBytes);。该API需要同时使用客户端证书认证和基础用户认证,且相同逻辑在控制台应用中可正常调用成功。
原代码
public OutputWS callapi() { try { byte[] certificateBytes = Convert.FromBase64String(secretvalue); // Load the certificate from the byte array X509Certificate2 certificate = new X509Certificate2(certificateBytes); // Create an instance of HttpClientHandler HttpClientHandler handler = new HttpClientHandler(); handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.ClientCertificates.Add(certificate); using (HttpClient httpClient = new HttpClient(handler)) { // Set the Basic Authentication header var username = "user"; var password = "password"; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"))); // Convert the request body to JSON var jsonRequest = JsonConvert.SerializeObject(inputWS); var httpContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json"); // Make the API POST request HttpResponseMessage response1 = httpClient.PostAsync("webapilink", httpContent).Result; tracingService.Trace("response1", response1.ToString()); if (response1.IsSuccessStatusCode) { tracingService.Trace("successs", response1.StatusCode + " " + response1.ReasonPhrase); } else { tracingService.Trace("failed", response1.StatusCode + " " + response1.ReasonPhrase); } string responseContent = response1.Content.ReadAsStringAsync().Result; OutputWS responseRequest = JsonConvert.DeserializeObject<OutputWS>(responseContent); tracingService.Trace("responseRequest", JsonConvert.SerializeObject(responseRequest)); tracingService.Trace("output function call", JsonConvert.SerializeObject(responseRequest)); // Return the response content return responseRequest; } } catch (Exception ex) { tracingService.Trace("Error", ex.ToString()); throw; } }
解决方案
1. 修改证书加载方式,指定密钥存储标志
Dynamics 365沙箱环境限制对密钥容器的访问,默认构造函数加载证书时会尝试写入密钥容器,触发权限错误。需使用带X509KeyStorageFlags参数的重载构造函数,避免持久化密钥到本地容器:
// 使用EphemeralKeySet,密钥仅在内存中存在,不写入容器 X509Certificate2 certificate = new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.EphemeralKeySet);
若证书包含密码,可将第二个参数替换为证书密码:
X509Certificate2 certificate = new X509Certificate2(certificateBytes, "certPassword", X509KeyStorageFlags.EphemeralKeySet);
2. 改用异步调用避免死锁
插件中使用.Result同步调用HttpClient方法可能导致死锁,建议改为异步模式(需插件实现IAsyncPlugin接口):
public async Task ExecuteAsync(IServiceProvider serviceProvider) { // 初始化tracingService等逻辑... HttpResponseMessage response1 = await httpClient.PostAsync("webapilink", httpContent); string responseContent = await response1.Content.ReadAsStringAsync(); // 后续处理... }
3. 验证证书完整性
确保Base64字符串对应的证书包含完整的私钥(控制台能正常调用已验证这一点,但沙箱环境对私钥访问更严格,EphemeralKeySet可解决此问题)。
修正后的核心代码片段
byte[] certificateBytes = Convert.FromBase64String(secretvalue); // 关键修改:指定EphemeralKeyStorageFlags X509Certificate2 certificate = new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.EphemeralKeySet); HttpClientHandler handler = new HttpClientHandler(); handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.ClientCertificates.Add(certificate); using (HttpClient httpClient = new HttpClient(handler)) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"))); var jsonRequest = JsonConvert.SerializeObject(inputWS); var httpContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json"); // 异步调用示例(需插件为异步) HttpResponseMessage response1 = await httpClient.PostAsync("webapilink", httpContent); string responseContent = await response1.Content.ReadAsStringAsync(); OutputWS responseRequest = JsonConvert.DeserializeObject<OutputWS>(responseContent); // ...后续逻辑 }
内容的提问来源于stack exchange,提问作者taniiit
相关产品推荐
相关产品推荐

