我正在尝试通过他们的 WSDL 创建到 Web 服务的连接。有人告诉我,该服务的身份验证被描述为通过交换证书进行身份验证的 TLS。我通过 Visual Studio 中的“添加服务引用”生成了客户端。当我发送命令时,我希望看到 wireshark 中的“握手”,但我什至没有看到发送的“Client Hello”启动。
此处描述了服务的身份验证:
http://en.wikipedia.org/wiki/Transport_Layer_Security#Description
我的客户端是用 c# 编写的
这是我正在运行以测试连接的完整程序(它以 Run() 开头):
public class ClientExample
{
private const string Url =
"https://xxxxxxxxx";
public static void Run()
{
ServicePointManager.ServerCertificateValidationCallback += ValidateCertificate;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
PerformTest("Clear Cache", GetBinding());
}
private static Binding GetBinding()
{
var bec = new BindingElementCollection
{
new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8),
new HttpsTransportBindingElement{ RequireClientCertificate = true }
};
return new CustomBinding(bec);
}
private static void PerformTest(string test, Binding binding)
{
try
{
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(test);
Console.ResetColor();
var client = GetClient(binding);
SendMessage(client);
}
catch (Exception e)
{
DisplayError(e);
}
}
private static MyClient GetClient(Binding binding)
{
var endpointAddress = new EndpointAddress(Url);
var client = new MyClient(binding, endpointAddress);
if (client.ClientCredentials != null)
{
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My,
X509FindType.FindBySubjectName,
"xxxxxxxxxxxxx");
}
return client;
}
private static void SendMessage(ChargePointServiceClient client)
{
var response = client.clearCache("xxxxxxxxxxxxx", new ClearCacheRequest());
Console.WriteLine(ClearCacheDescription(response));
}
private static string ClearCacheDescription(ClearCacheStatus response)
{
switch (response)
{
case ClearCacheStatus.Accepted:
return "Accepted";
case ClearCacheStatus.Rejected:
return "Rejected";
}
return "Unkown";
}
private static bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
{
switch (sslpolicyerrors)
{
case SslPolicyErrors.None:
return true;
case SslPolicyErrors.RemoteCertificateChainErrors:
DisplayWarningMessage("RemoteCertificateChainErrors");
return false;
case SslPolicyErrors.RemoteCertificateNameMismatch:
DisplayWarningMessage("RemoteCertificateNameMismatch");
return false;
case SslPolicyErrors.RemoteCertificateNotAvailable:
DisplayWarningMessage("RemoteCertificateNotAvailable");
return false;
default:
DisplayWarningMessage("Unkown Certificate Validation Error");
return false;
}
}
private static void DisplayError(Exception exception)
{
if (exception == null)
return;
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(@"Exception");
Console.ResetColor();
Console.WriteLine(exception.Message);
if (exception.InnerException != null)
Console.WriteLine();
DisplayError(exception.InnerException);
}
private static void DisplayWarningMessage(string message)
{
Console.BackgroundColor = ConsoleColor.DarkYellow;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(message);
Console.ResetColor();
}
}
我通过 app.config 中的 system.diagnostic 记录了所有网络流量:
<system.diagnostics>
<sources>
<source name="System.Net">
<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>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
<add name="System.Net.Cache" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
traceOutputOptions="None"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
以下是一些 感兴趣的日志行:
这确认创建了 tls 流:
System.Net Information: 0 : [9040] TlsStream#50727427::.ctor(host=xxxxx, #certs=1)
创建安全 channel :
System.Net Information: 0 : [9040] SecureChannel#11159819::.ctor(hostname=xxxxxxx, #clientCertificates=1, encryptionPolicy=RequireEncryption)
System.Net Information: 0 : [9040] Enumerating security packages:
System.Net Information: 0 : [9040] Negotiate
System.Net Information: 0 : [9040] NegoExtender
System.Net Information: 0 : [9040] Kerberos
System.Net Information: 0 : [9040] NTLM
System.Net Information: 0 : [9040] TSSSP
System.Net Information: 0 : [9040] pku2u
System.Net Information: 0 : [9040] Schannel
System.Net Information: 0 : [9040] Microsoft Unified Security Protocol Provider
System.Net Information: 0 : [9040] LiveSSP
System.Net Information: 0 : [9040] WDigest
System.Net Information: 0 : [9040] CREDSSP
System.Net Information: 0 : [9040] SecureChannel#11159819 - Attempting to restart the session using the user-provided certificate: [Version]
不知道为什么要查找私钥:
System.Net Information: 0 : [9040] SecureChannel#11159819 - Left with 1 client certificates to choose from.
System.Net Information: 0 : [9040] SecureChannel#11159819 - Trying to find a matching certificate in the certificate store.
System.Net Information: 0 : [9040] SecureChannel#11159819 - Locating the private key for the certificate: [Version]
此时在日志中,我看到证书已交换。不幸的是,线鲨并没有证实这一点......
现在我的程序验证服务证书并开始处理
System.Net Information: 0 : [9040] SecureChannel#11159819 - Remote certificate was verified as valid by the user.
System.Net Information: 0 : [9040] ProcessAuthentication(Protocol=Ssl3, Cipher=Rc4 128 bit strength, Hash=Sha1 160 bit strength, Key Exchange=RsaKeyX 2048 bit strength).
然后我看到一些加密的数据交换并发送了我的清除缓存命令
我收到来自服务的加密响应,但该消息表明出现故障。我认为这是因为服务不接受身份验证
System.Net Error: 0 : [9040] Exception in HttpWebRequest#46890055::GetResponse - The remote server returned an error: (500) Internal Server Error..
这是我在wireshark中使用的过滤器(两个IP都是不属于我的服务)
(ip.src == xxx.xxx.xxx.xx or ip.dst == xxx.xxx.xxx.xx) and ssl.handshake
另外,我使用的证书没有私钥。我认为我不需要 TLS 文档中的一个。
所以我的问题是为什么我在运行程序时在wireshark中看不到Client Hello/Server Hello,或者我应该如何配置客户端绑定(bind)来启动客户端hello?
(我标记 wcf 是因为我认为 wcf 专业人员可能知道我的问题的答案。我的解决方案将独立于 wcf,因为我无法控制服务绑定(bind))
最佳答案
听起来您所描述的是在 SSL/TLS 中使用客户端证书身份验证。在这种情况下,您使用的客户端证书肯定需要有一个私钥,这是完成 SSL/TLS 握手所必需的。
请注意,在配置客户端时,您需要告诉绑定(bind)使用客户端证书进行传输身份验证:
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
然后告诉它如何使用 ClientCertificate 行为定位您要使用的证书:
<behaviors>
<endpointBehaviors>
<behavior name="ClientCertificateBehavior">
<clientCredentials>
<clientCertificate findValue="CN=clienttempcert" storeLocation="CurrentUser"
storeName="My" x509FindType="FindBySubjectDistinguishedName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
请注意,您指定的证书 key 必须具有客户端可以加载的私钥(但是,该服务根本不需要私钥)。
关于c# - 具有相互身份验证的服务客户端(2 路客户端证书身份验证),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21394239/