在 C# 中通过 HttpWebRequest 实现 Digest 身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/594323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Implement Digest authentication via HttpWebRequest in C#
提问by
Does anyone know how to screen scrape web-sites that use digest http authentication? I use code like this:
有谁知道如何筛选使用摘要式 http 身份验证的抓取网站?我使用这样的代码:
var request = (HttpWebRequest)WebRequest.Create(SiteUrl);
request.Credentials=new NetworkCredential(Login, Password)
I'm able to access the site's mainpage, but when I try to surf to any other pages (using another request with the same credentials) I get "HTTP/1.1 400 Bad Request" error.
我可以访问该站点的主页,但是当我尝试浏览任何其他页面(使用具有相同凭据的另一个请求)时,我收到“HTTP/1.1 400 错误请求”错误。
I used Fiddler to compare requests of my C# application with Mozilla Firefox requests.
我使用 Fiddler 将我的 C# 应用程序的请求与 Mozilla Firefox 请求进行比较。
2 URLs that I try to access are: https://mysiteurl/forum/index.phphttps://mysiteurl/forum/viewforum.php?f=4&sid=d104363e563968b4e4c07e04f4a15203
我尝试访问的 2 个 URL 是: https://mysiteurl/forum/index.php https://mysiteurl/forum/viewforum.php?f=4&sid=d104363e563968b4e4c07e04f4a15203
Here are 2 requests () of my C# app:
这是我的 C# 应用程序的 2 个请求 ():
Authorization: Digest username="xxx",realm="abc",nonce="NXa26+NjBAA=747dfd1776c9d585bd388377ef3160f1ff265429",uri="/forum/index.php",algorithm="MD5",cnonce="89179bf17dd27785aa1c88ad976817c9",nc=00000001,qop="auth",response="3088821620d9cbbf71e775fddbacfb6d"
Authorization: Digest username="xxx",realm="abc",nonce="1h7T6+NjBAA=4fed4d804d0edcb54bf4c2f912246330d96afa76",uri="/forum/viewforum.php",algorithm="MD5",cnonce="bb990b0516a371549401c0289fbacc7c",nc=00000001,qop="auth",response="1ddb95a45fd7ea8dbefd37a2db705e3a"
And that's what Firefox sending to the server:
这就是 Firefox 发送到服务器的内容:
Authorization: Digest username="xxx", realm="abc", nonce="T9ICNeRjBAA=4fbb28d42db044e182116ac27176e81d067a313c", uri="/forum/", algorithm=MD5, response="33f29dcc5d70b61be18eaddfca9bd601", qop=auth, nc=00000001, cnonce="ab96bbe39d8d776d"
Authorization: Digest username="xxx", realm="abc", nonce="T9ICNeRjBAA=4fbb28d42db044e182116ac27176e81d067a313c", uri="/forum/viewforum.php?f=4&sid=d104363e563968b4e4c07e04f4a15203", algorithm=MD5, response="a996dae9368a79d49f2f29ea7a327cd5", qop=auth, nc=00000002, cnonce="e233ae90908860e1"
So in my app I have different values in "nonce" field while in Firefox this field is the same. On the other hand I have same values in "nc" field while Firefox increments this field.
所以在我的应用程序中,我在“nonce”字段中有不同的值,而在 Firefox 中这个字段是相同的。另一方面,我在“nc”字段中有相同的值,而 Firefox 会增加这个字段。
Also when my app tries to access site pages in Fiddler i can see that it always gets response "HTTP/1.1 401 Authorization Required", while Firefox authorizes only once. I've tried to set request.PreAuthenticate = true; but it seems to have no effect...
此外,当我的应用程序尝试访问 Fiddler 中的站点页面时,我可以看到它总是收到响应“HTTP/1.1 401 需要授权”,而 Firefox 仅授权一次。我试图设置 request.PreAuthenticate = true; 但是好像没什么效果……
My question is: how to properly implement digest authentication using C#? Are there any standard methods or do I have to do it from scratch? Thanks in advance.
我的问题是:如何使用 C# 正确实现摘要认证?是否有任何标准方法或我必须从头开始?提前致谢。
回答by Sam Axe
This article from 4GuysFromRolla appears to be what you are looking for:
来自 4GuysFromRolla 的这篇文章似乎正是您要查找的内容:
回答by Cygon
I'm currently observing the same issue, though the web server I'm testing this against is my own. The server logs show:
我目前正在观察同样的问题,尽管我正在测试的网络服务器是我自己的。服务器日志显示:
Digest: uri mismatch - </var/path/some.jpg> does not match request-uri
</var/path/some.jpg?parameter=123456789>
I tried removing the arguments from the URL (as that seemed to be what's different), but the error still occurred just like before.
我尝试从 URL 中删除参数(因为这似乎有所不同),但错误仍然像以前一样发生。
My conclusion is that the URL arguments have to be included in the digest hash as well and that the HttpWebRequest
is for some reason removing it.
我的结论是 URL 参数也必须包含在摘要哈希中,并且HttpWebRequest
出于某种原因将其删除。
回答by kitwalker
Create a class Digest.cs
创建一个类 Digest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
namespace NUI
{
public class DigestAuthFixer
{
private static string _host;
private static string _user;
private static string _password;
private static string _realm;
private static string _nonce;
private static string _qop;
private static string _cnonce;
private static DateTime _cnonceDate;
private static int _nc;
public DigestAuthFixer(string host, string user, string password)
{
// TODO: Complete member initialization
_host = host;
_user = user;
_password = password;
}
private string CalculateMd5Hash(
string input)
{
var inputBytes = Encoding.ASCII.GetBytes(input);
var hash = MD5.Create().ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var b in hash)
sb.Append(b.ToString("x2"));
return sb.ToString();
}
private string GrabHeaderVar(
string varName,
string header)
{
var regHeader = new Regex(string.Format(@"{0}=""([^""]*)""", varName));
var matchHeader = regHeader.Match(header);
if (matchHeader.Success)
return matchHeader.Groups[1].Value;
throw new ApplicationException(string.Format("Header {0} not found", varName));
}
private string GetDigestHeader(
string dir)
{
_nc = _nc + 1;
var ha1 = CalculateMd5Hash(string.Format("{0}:{1}:{2}", _user, _realm, _password));
var ha2 = CalculateMd5Hash(string.Format("{0}:{1}", "GET", dir));
var digestResponse =
CalculateMd5Hash(string.Format("{0}:{1}:{2:00000000}:{3}:{4}:{5}", ha1, _nonce, _nc, _cnonce, _qop, ha2));
return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", " +
"algorithm=MD5, response=\"{4}\", qop={5}, nc={6:00000000}, cnonce=\"{7}\"",
_user, _realm, _nonce, dir, digestResponse, _qop, _nc, _cnonce);
}
public string GrabResponse(
string dir)
{
var url = _host + dir;
var uri = new Uri(url);
var request = (HttpWebRequest)WebRequest.Create(uri);
// If we've got a recent Auth header, re-use it!
if (!string.IsNullOrEmpty(_cnonce) &&
DateTime.Now.Subtract(_cnonceDate).TotalHours < 1.0)
{
request.Headers.Add("Authorization", GetDigestHeader(dir));
}
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (WebException ex)
{
// Try to fix a 401 exception by adding a Authorization header
if (ex.Response == null || ((HttpWebResponse)ex.Response).StatusCode != HttpStatusCode.Unauthorized)
throw;
var wwwAuthenticateHeader = ex.Response.Headers["WWW-Authenticate"];
_realm = GrabHeaderVar("realm", wwwAuthenticateHeader);
_nonce = GrabHeaderVar("nonce", wwwAuthenticateHeader);
_qop = GrabHeaderVar("qop", wwwAuthenticateHeader);
_nc = 0;
_cnonce = new Random().Next(123400, 9999999).ToString();
_cnonceDate = DateTime.Now;
var request2 = (HttpWebRequest)WebRequest.Create(uri);
request2.Headers.Add("Authorization", GetDigestHeader(dir));
response = (HttpWebResponse)request2.GetResponse();
}
var reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
}
}
Now in your application, you can use the following code:
现在在您的应用程序中,您可以使用以下代码:
DigestAuthFixer digest = new DigestAuthFixer(url, username, password);
string strReturn = digest.GrabResponse(url);