You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:17:08