使用System.Net.WebClient与HTTPS证书

12
在我的C# Windows客户端中,我有一个提交到“母舰”的POST请求。当然,我希望提交的数据是安全的,所以我付款让HostGator为我签发了SSL证书。

我保存了.CER文件,并构造了如下的请求:

//wrapper for WebClient object to use certificate file
class SecureWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri address)
    {
        HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
        string certPath = @"e:\mycertificate.cer";
        X509Certificate myCert = X509Certificate.CreateFromCertFile(certPath);
        request.ClientCertificates.Add(myCert);
        return request;
    }
}

//request
private static SecureWebClient client = new SecureWebClient();
private static NameValueCollection = new NameValueCollection();
nvc.Add(POST_ACTION, ACTION_CODE_LOGIN);
nvc.Add(POST_EMAIL, email);
nvc.Add(POST_PASSWORD, password);

sResponse = System.Text.Encoding.ASCII.GetString(client.UploadValues(BASE_URL + ACTION_PAGE, nvc));

它抛出了一个System.Net.WebException异常:

底层连接已关闭:发送时发生了意外错误。

InnerException是一个System.IO.IOException异常:

握手失败,因为出现了意外的数据包格式。

对于我的错误有什么见解吗?


你想在客户端还是服务器上使用SSL证书? - SLaks
1
请澄清一下:您想要通信加密(需要服务器上的证书)吗?还是您想要验证客户端身份(需要客户端证书)?具体而言,您需要在相关端点上拥有一个私钥(可能是 .PFX 文件)。 - Roger Lipscombe
我并不是完全精通安全方面的知识。我可以通过HTTPS协议在浏览器中访问主机站点。我想要以加密方式将数据发布到完全相同的服务器/站点。所以,@SLaks - 从技术上讲,我不确定自己想要什么。我只是以外行人的语言描述了我需要的东西。 - Honus Wagner
@Roger - 请看上面(每个评论只能添加一个@标签) - Honus Wagner
4个回答

12
如果你使用客户端证书,并且可以使用https://访问你的服务器,那么你的代码应该如下所示:

如果您未使用客户端证书并且可以通过https://访问您的服务器,则代码应如下所示:

private static WebClient client = new WebClient();
private static NameValueCollection nvc= new NameValueCollection();

nvc.Add(POST_ACTION, ACTION_CODE_LOGIN);
nvc.Add(POST_EMAIL, email);
nvc.Add(POST_PASSWORD, password);

sResponse = System.Text.Encoding.ASCII.GetString(client.UploadValues(BASE_URL + ACTION_PAGE, nvc));
只要你的BASE_URL使用https://,那么所有数据(从服务器到客户端和从客户端到服务器)都将被加密。换句话说,从客户端到服务器使用SSL/TLS不需要客户端做任何特殊的事情(比如证书),只需使用https://作为方案即可,因为操作系统会提供一切必要的东西(例如信任的根证书)来保障数据传输的安全。

我按照你说的做了——我的代码就像你发布的代码一样,我的BASE_URL是https://www.myserver.com/,但我收到了与原帖中描述的完全相同的错误消息。现在,我做错了什么? - Honus Wagner
1
将您的问题拆分为几个部分。首先,您能否使用WebClient下载主页(使用https://www.myserver.com/)? 如果无法工作,请确保可以从Web浏览器中使用(并检查页面属性以确保已加密)。 如果可以工作,则尝试在http://www.myserver.com/上使用不带SSL的上传代码,以确保此部分正常工作。 很可能有一部分无法正常工作,这将为您(和我们)提供一些提示来帮助您。 - poupou
使用 client.DownloadData("https://www.myserver.com/") 是可以正常工作的。然后使用 client.UploadValues("http://www.myserver.com/") 也是可以正常工作的。这意味着什么? - Honus Wagner
很奇怪,下一个逻辑罪犯应该是服务器端代码(虽然我不知道它如何导致特定的异常)或某些服务器配置错误。如果我遇到这样的问题,我会(a)安装Wireshark并查看SSL数据包(但这不是一个简单的选项),然后(b)尝试使用另一种工具/语言来完成相同的任务(即确保它在服务器端)。也许你应该在http://serverfault.com/上询问? - poupou
1
所以我尝试了 client.UploadValues("https://www.myserver.com/", nvc),结果它居然_成功了_(让我很惊讶)。然后我大胆地决定去尝试原始请求:client.UploadValues(BASE_URL + ACTION_PAGE, nvc),结果它也成功了。为什么呢? - Honus Wagner
不确定,我猜你没有发布任何数据,内部异常文本也不是很有帮助(可悲的常见情况)?!?我很高兴你的问题已经解决。 - poupou

4
客户端证书只有在私钥可用的情况下才能使用。当使用 .cer 文件时,通常情况下不包含私钥,因为 X.509 证书不包括私钥。
确保私钥可用的唯一安全方法是从 PKCS#12 文件中加载证书,其中证书和私钥都可用(即两者都导出到 .pfx 文件中)。
注:
- 我说“通常”是因为 Windows/CryptoAPI 有时会做一些魔法(与 X509Certificate 一起使用时),并自动将证书与证书.key 存储中的私钥关联。 - 我说“安全”是因为这对 Mono 和 MS .NET 都适用。

这个对我来说太高深了。我在哪里可以获取/创建/找到PKCS#12文件?然后如何从中加载私钥? - Honus Wagner
1
首先确保您需要客户端证书 :-) 您的源代码(mis)使用了此功能,但不清楚这是否是您想要的。如果您只想从服务器获得“https”,那么您不需要处理客户端应用程序的证书。 - poupou
我想保护我的WebClient.UploadValues调用的数据。这就是我真正知道如何表达的全部内容。所以我问你:我需要客户端证书吗? - Honus Wagner
不,看我的另一个答案(该答案不基于客户端证书)。 - poupou
@poupou: 您太棒了!我因为这个.cer问题被困扰了很长时间,现在有一个导出的.p12密钥,一切都能够顺利工作,而无需将密钥导入密钥库。 - balint

1
听起来你是在反着做。你应该在网络主机/服务器上安装SLL证书,然后创建一个Web请求到Web服务器并要求使用HTTPS协议。

我并不是完全精通于安全方面的知识。我可以使用HTTPS协议在浏览器中访问主机站点。我想要加密地向同一服务器/站点发布数据。 - Honus Wagner

0

只需使用RDP连接到您的服务器

打开IE并转到Internet选项

导航到高级选项卡并启用使用SSL 2.0和SSL 3.0

现在您的服务器请求将得到验证


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接