2014-03-05 36 views
21

我想使用客户端证书来保护我的RESTful WebApi服务使用ssl和客户端身份验证。WebApi HttpClient不发送客户端证书

测试;我生成了一个自签名证书并放置在本地计算机,受信任的根证书颁发机构文件夹中,并生成了“服务器”和“客户端”证书。 服务器的标准https正常运行。

不过,我有一些代码在服务器验证证书,这当我连接使用我的测试客户端,它提供我的客户证书和测试客户端返回403个禁止状态不会被调用。

这imples服务器失败我的证书达到我的验证代码之前。 但是,如果我激起了提琴手它知道客户证书是必需的,并要求我提供一个我的文档\ Fiddler2。我给了我在我的测试客户端使用的相同客户端证书,现在我的服务器正常工作,并收到了我期望的客户端证书! 这意味着WebApi客户端没有正确发送证书,下面我的客户端代码与我发现的其他示例几乎相同。

static async Task RunAsync() 
    { 
     try 
     { 
      var handler = new WebRequestHandler(); 
      handler.ClientCertificateOptions = ClientCertificateOption.Manual; 
      handler.ClientCertificates.Add(GetClientCert()); 
      handler.ServerCertificateValidationCallback += Validate; 
      handler.UseProxy = false; 

      using (var client = new HttpClient(handler)) 
      { 
       client.BaseAddress = new Uri("https://hostname:10001/"); 

       client.DefaultRequestHeaders.Accept.Clear(); 
       client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); 

       var response = await client.GetAsync("api/system/"); 
       var str = await response.Content.ReadAsStringAsync(); 

       Console.WriteLine(str); 
      } 
     } catch(Exception ex) 
     { 
      Console.Write(ex.Message); 
     } 
    } 

任何想法,为什么它可以在小提琴手但不是我的测试客户端?

编辑:这里是代码GetClientCert()

private static X509Certificate GetClientCert() 
    {    
     X509Store store = null; 
     try 
     { 
      store = new X509Store(StoreName.My, StoreLocation.LocalMachine); 
      store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly); 

      var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "Integration Client Certificate", true); 

      if (certs.Count == 1) 
      { 
       var cert = certs[0]; 
       return cert; 
      } 
     } 
     finally 
     { 
      if (store != null) 
       store.Close(); 
     } 

     return null; 
    } 

授予测试代码不处理空证书,但我调试到enssure了正确的证书所在。

+0

你检查这GetClientCert返回你所期望的证书? – csgero

+1

是:) GetClientCert所做的是从localmachine个人存储中查找客户端证书,这与我给小提琴手的证书相同。 但是,即使它是一个不同的证书我会期望服务器验证代码被调用? – Tronneh

+0

取决于服务的托管方式。例如,IIS会执行证书验证,并会返回403而不会调用您的代码。 – csgero

回答

1

是您的代码与上述相同的用户帐户提琴手在运行运行?如果没有,它可能有权访问证书文件,但没有正确的私钥。同样,你的GetClientCert()函数返回什么?具体来说,它是否有PrivateKey属性集?

+0

Fiddler以我的用户帐户运行,测试在VS2013调试下运行,VS2013以我的用户帐户运行。 'GetClintCert()'方法返回我期望的客户端证书,'HasPrivateKey'属性为真 – Tronneh

+0

我已更新原始问题以包含来自'GetClientCert()' – Tronneh

+0

的代码有趣。考虑为System.Net启用日志记录并查看日志中是否有值得注意的内容? – EricLaw

1

有两种类型的证书。第一个是从服务器所有者发送给您的公共.cer文件。这个文件只是一串长字符。第二个是密钥库证书,这是您创建的自签名证书,并将cer文件发送到您要调用的服务器并安装它。根据您拥有多少安全性,您可能需要将其中一项或两项都添加到客户端(您的案例中的处理程序)。我只看到在安全性非常高的服务器上使用的密钥库证书。此代码从斌/部署的文件夹,这两个证书:

#region certificate Add 
       // KeyStore is our self signed cert 
       // TrustStore is cer file sent to you. 

       // Get the path where the cert files are stored (this should handle running in debug mode in Visual Studio and deployed code) -- Not tested with deployed code 
       string executableLocation = Path.GetDirectoryName(AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory); 

       #region Add the TrustStore certificate 

       // Get the cer file location 
       string pfxLocation = executableLocation + "\\Certificates\\TheirCertificate.cer"; 

       // Add the certificate 
       X509Certificate2 theirCert = new X509Certificate2(); 
       theirCert.Import(pfxLocation, "Password", X509KeyStorageFlags.DefaultKeySet); 
       handler.ClientCertificates.Add(theirCert); 
       #endregion 

       #region Add the KeyStore 
       // Get the location 
       pfxLocation = executableLocation + "\\Certificates\\YourCert.pfx"; 

       // Add the Certificate 
       X509Certificate2 YourCert = new X509Certificate2(); 
       YourCert.Import(pfxLocation, "PASSWORD", X509KeyStorageFlags.DefaultKeySet); 
       handler.ClientCertificates.Add(YourCert); 
       #endregion 

       #endregion 

而且 - 你需要处理证书错误(注:这是不好的 - 它说,所有的证书问题也还行),你应该改变这种代码来处理特定的证书问题,如名称不匹配。它在我的名单上。有很多关于如何做到这一点的例子。

这段代码在你的方法

// Ignore Certificate errors need to fix to only handle 
ServicePointManager.ServerCertificateValidationCallback = MyCertHandler; 

方法某处顶部的类

private bool MyCertHandler(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors error) 
    { 
     // Ignore errors 
     return true; 
    } 
0

在您使用store = new X509Store(StoreName.My, StoreLocation.LocalMachine);的代码。

客户端证书没有从LocalMachine回升,则应改用StoreLocation.CurrentUser

检查MMC -> File -> Add or Remove Snap-ins -> Certificates -> My user account您将看到小提琴手使用的证书。如果您从My user account中删除它,并且只将其从Computer account中导入,您将看到提琴手无法拾取它。

附注是在找到您还必须针对文化进行处理的证书时。

例子:

var certificateSerialNumber= "‎83 c6 62 0a 73 c7 b1 aa 41 06 a3 ce 62 83 ae 25".ToUpper().Replace(" ", string.Empty); 

//0 certs 
var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, certificateSerialNumber, true); 

//null 
var cert = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => x.GetSerialNumberString() == certificateSerialNumber); 

//1 cert 
var cert1 = store.Certificates.Cast<X509Certificate>().FirstOrDefault(x => 
       x.GetSerialNumberString().Equals(certificateSerialNumber, StringComparison.InvariantCultureIgnoreCase));