c# - OnAuthorization 中没有证书(HttpActionContext actionContext)
问题描述
我从这个示例中创建了 CertificateTestController 和 ValuesController 如何使用客户端证书在 Web API 中进行身份验证和授权。如果您从用户 Ogglas 向下滚动到“更新”。我以他为例,让“CertificateTestController”工作,我可以从我的商店获取证书并将其添加到“处理程序”中。当我调用“ValuesController”时,没有证书被初始化
X509Certificate2 cert = actionContext.Request.GetClientCertificate();
这是我拥有的完整代码
值控制器
{
[RequireSpecificCert]
public class ValuesController : ApiController
{
// GET api/values
public IHttpActionResult Get()
{
return Ok("It works!");
}
public class RequireSpecificCertAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "HTTPS Required"
};
}
else
{
X509Certificate2 cert = actionContext.Request.GetClientCertificate();
X509Certificate2 cert2 = actionContext.RequestContext.ClientCertificate;
if (cert == null)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "Client Certificate Required"
};
}
else
{
X509Chain chain = new X509Chain();
//Needed because the error "The revocation function was unable to check revocation for the certificate" happened to me otherwise
chain.ChainPolicy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.NoCheck,
};
try
{
var chainBuilt = chain.Build(cert);
Debug.WriteLine(string.Format("Chain building status: {0}", chainBuilt));
var validCert = CheckCertificate(chain, cert);
if (chainBuilt == false || validCert == false)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "Client Certificate not valid"
};
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
{
Debug.WriteLine(string.Format("Chain error: {0} {1}", chainStatus.Status, chainStatus.StatusInformation));
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
base.OnAuthorization(actionContext);
}
}
private bool CheckCertificate(X509Chain chain, X509Certificate2 cert)
{
var rootThumbprint = WebConfigurationManager.AppSettings["rootThumbprint"].ToUpper().Replace(" ", string.Empty);
var clientThumbprint = WebConfigurationManager.AppSettings["clientThumbprint"].ToUpper().Replace(" ", string.Empty);
//Check that the certificate have been issued by a specific Root Certificate
var validRoot = chain.ChainElements.Cast<X509ChainElement>().Any(x => x.Certificate.Thumbprint.Equals(rootThumbprint, StringComparison.InvariantCultureIgnoreCase));
//Check that the certificate thumbprint matches our expected thumbprint
var validCert = cert.Thumbprint.Equals(clientThumbprint, StringComparison.InvariantCultureIgnoreCase);
return validRoot && validCert;
}
}
用下面的 CertificateTestController 调用上面的 ValuesController
{
[RoutePrefix("api/certificatetest")]
public class CertificateTestController : ApiController
{
public IHttpActionResult Get()
{
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(GetClientCert());
handler.UseProxy = false;
var client = new HttpClient(handler);
var result = client.GetAsync("https://localhost:44301//values").GetAwaiter().GetResult();
var resultString = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return Ok(resultString);
}
private static X509Certificate GetClientCert()
{
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificateSerialNumber = "2bc034466b6960d2fee84d86e6c2532a".ToUpper().Replace(" ", string.Empty);
var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));
return cert;
}
finally
{
store.Close();
}
}
}
}
请帮忙!
解决方案
以下是我在尝试解决此问题时回答的问题/问题。
Q1。
为什么我的证书没有到达客户端(代码)?
A1。
VS 会在 OnAuthorization(HttpActionContext actionContext) 被命中之前进行初始 SSL 协商。此时,服务器在证书存储中搜索安装了私钥以进行验证的客户端证书。如果没有私钥,则失败。我通过打开详细信息找到了问题。请看下文。
以下是需要更改才能使其正常工作的配置。
1. Web Config 文件更改
• SSL 协商
下面的配置告诉 Visual Studio,“api/values” URL 需要一个 ssl 协商。这有助于我们限制预期证书协商的时间和地点。位置路径属性
<location path="api/values">
<system.webServer>
<security>
<access sslFlags="SslNegotiateCert" />
<authentication>
<iisClientCertificateMappingAuthentication enabled="true">
</iisClientCertificateMappingAuthentication>
</authentication>
</security>
</system.webServer>
</location>
• 证书详细
下面的诊断有助于我们进行故障排除,并找出证书验证可能导致的任何问题。
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.Net">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.HttpListener">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Sockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="System.Net.trace.log"
traceOutputOptions = "ProcessId, DateTime"/>
</sharedListeners>
<switches>
<add name="System.Net" value="Verbose" />
<add name="System.Net.Sockets" value="Verbose" />
<add name="System.Net.Cache" value="Verbose" />
<add name="System.Net.HttpListener" value="Verbose" />
</switches>
</system.diagnostics>
• 应用设置
将 rootThumbprint 值更改为任何服务器证书的指纹,而 clientThumprint 将是任何客户端证书指纹。certificateSerialNumber 应该是传出证书序列号。
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="rootThumbprint" value="change"/>
<add key="clientThumbprint" value="change"/>
<add key="certificateSerialNumber" value="change"/>
</appSettings>
2.本地Visual Studio和IIS express
1) 应用程序主机.config
路径 Visual Studio 早于 2015 年但在 2008 年之后位置 --> c:\Users\e#\Documents\IISExpress\Config Visual Studio 2015 & 2017 {project_name}.vs\config\config.host.config
更改确保以下属性设置为“允许”
2) 启用 SSL
在 Visual Studio 属性窗口中启用 SSL。这很重要,因为 URL 从 http 变为 https。
3) WebApiConfig.cs
创建一个类过滤器文件夹。适当地命名它。在运行任何其他代码之前,证书验证需要成为第一个检查。这就是 WebApiConfig.cs 派上用场的地方。例如,我调用了我的类 RequireHttpsAttribute。为了运行检查,只需将以下行放入 WebApiConfig.cs config.Filters.Add(new RequireHttpsAttribute());
4) 证书测试控制器.cs
这是类充当客户端。此类用于将证书附加到请求中并将其发送出去。这堂课有一个变化。这仅在计算机上进行了本地测试。var result = client.GetAsync(" https://localhost:44300//api//values ").GetAwaiter().GetResult(); 更改网址。 我们根据下面的序列号附上证书。证书也可以基于指纹、主题、过期/撤销、链验证等附加。
var certificateSerialNumber = WebConfigurationManager.AppSettings["certificateSerialNumber"].ToUpper().Replace(" ", string.Empty);
5) 值控制器.cs
此类充当服务器。这是证书验证发生的地方。引用 webApiConfig.cs 的“CheckCertificate”方法/函数有两个变化。如果您按照上面 WebApiConfig.cs 下的应用程序设置更改,那么您就可以开始了。
3. 文件
1) 证书测试控制器.cs
2) 值控制器.cs
推荐阅读
- mysql - 如何拆分wordpress帖子表
- php - 当数组索引不是英文字符时,如何使用 Codeigniter 从多维数组输入中获取 POST 数据?
- angular - ngFor 完整列表和唯一列表
- swift - 协议中的公共默认初始化
- android - getString 和 DialogListener 在类切换后不起作用
- sql - 如果未全选,则在两个日期字段之间进行选择
- ios - 设置 UIView 以根据 Subviews 改变其高度
- python - 将 python 日志记录到标准输出的最简单方法
- php - Docker - docker-compose: php-fpm 从镜像中工作,但不能从 Dockerfile 中构建
- python - Python 中 Twisted TCP 客户端的 self.transport.write() 问题