WebRequest/Response 的 WSSE 认证






4.60/5 (4投票s)
2007年6月25日
4分钟阅读

80897

2801
使用 IAuthenticationModule 接口为 AtomAPI 实现 WSSE 身份验证方法

引言
WebRequest
/Response
类支持互联网上几乎所有的认证方法,例如 Negotiate (SPNEGO)、Kerberos、NTLM、Digest 和 Basic,通常情况下,我们无需自行编写认证逻辑。 .NET 还允许我们在 WebRequest
/Response
认证层添加自定义认证模块。
本文描述了 WSSE 自定义授权模块的实现,大多数 AtomAPI
服务都使用 WSSE 作为授权方法。该模块实现了 IAuthenticationModule
接口,因此它与 WebRequest
/Response
认证层兼容。您可以在不更改 HTTP 客户端代码的情况下支持 WSSE 认证。
WSSE 认证
最初,WSSE 认证是为 SOAP Web 服务设计的。然而,WSSE 的 Username Token 算法可以轻松地适应 HTTP 认证,现在它被广泛用于 AtomAPI
Web 服务。有关 AtomAPI
的 WSSE 认证详情请参见 此处。
注意:AtomAPI
已更新为 AtomPP
,并且该草案仅提到了 Basic 认证。WSSE 认证在不久的将来可能会过时。
实现
WSSE 认证需要以下逻辑:
- Base64 编码
- sha1 哈希算法
- ISO8601 格式
- 安全的 Nonce 创建
由于 .NET Framework 在类库中包含了所有这些代码,因此认证可以非常简单地编写,如下所示:
//get credential
if (credentials == null) return null;
NetworkCredential credential = credentials.GetCredential(request.RequestUri, "WSSE");
if (credential == null) return null;
//check credential policy
//request.RequestURI is not actually challenged URI. but I don't know how I can get it...
ICredentialPolicy credentialpolicy = AuthenticationManager.CredentialPolicy;
if ((credentialpolicy != null) && (!credentialpolicy.ShouldSendCredential
(request.RequestUri, request, credential, this))) return null;
//make secure nonce
byte[] nonce = CreateNonce();
//make iso8601 formatted UTC time
string createtime = DateTime.Now.ToUniversalTime().ToString
(DateTimeFormatInfo.InvariantInfo.SortableDateTimePattern) + "Z";
//make buffer. createtime and password are encoded in UTF8
byte[] digestbuf = new byte[nonce.Length + Encoding.UTF8.GetByteCount
(createtime + credential.Password)];
nonce.CopyTo(digestbuf, 0);
Encoding.UTF8.GetBytes(createtime + credential.Password).CopyTo(digestbuf, nonce.Length);
//create default SHA1
SHA1 sha1 = SHA1.Create();
//make digest string
string digest = Convert.ToBase64String(sha1.ComputeHash(digestbuf));
//add X-WSSE header to HTTPWebRequest
request.Headers.Add("X-WSSE", string.Join("", new string[]
{ "UsernameToken ", "Username=\"", credential.UserName, "\", ",
"PasswordDigest=\"", digest, "\", ", "Nonce=\"",
Convert.ToBase64String(nonce), "\", ", "Created=\"", createtime, "\"" }));
请注意,您应该非常谨慎地创建 Nonce。System.Random
类对于 Nonce 来说不够安全。请使用 RNGCryptoServiceProvider
类来生成随机数。
自定义认证模块
在 .NET Framework 中,WebRequest
/Response
的认证过程由 AuthenticationManager
类和 IAuthenticationModule
接口管理。每种认证逻辑都包含在实现 IAuthenticationModule
的各个认证类中。例如,System.Net.NegotiateClient
类对应 SPNEGO 认证,System.Net.BasicClient
类对应 Basic 认证。AuthenticationManager
类包含 IAuthenticationModule
列表,其中默认的认证模块在应用程序启动时注册。
当服务器响应 Web 请求并要求认证时,AuthenticationManager
会使用相应的 WebRequest
、challenge 字符串和凭据,调用内部 IAuthenticationModule
列表的 IAuthenticationModule.Authenticate
函数。函数按注册顺序调用。如果调用的 IAuthenticateModule
无法执行认证,它将返回 null,AuthenticationManager
将调用下一个模块。如果模块可以执行认证,它将返回一个 Authorization
类的实例,其中包含认证信息。对于 HTTP,challenge 字符串来自 HTTP "WWW-Authenticate" 标头,而 HTTP "Authorization" 标头则由返回的 Authorization
对象生成。
AuthenticationManager
还支持 Preauthentication
。如果请求 URI 与之前请求的 URI 具有相同的 authority(模式、主机和端口),AuthenticationManager
会在发送请求前调用 IAuthenticationModule.Preauthenticate
函数,并确定认证字符串。预认证过程可以避免不必要的请求和响应往返。
实现 IAuthenticationModule
很简单。您只需实现两个属性和两个函数。
尽管 IAuthenticationModule
的成员可能从不同的线程调用,但您无需以线程安全的方式实现这些成员。调用已经过同步。
AuthenticationType 属性
认证类型的名称。此时,它是 "WSSE
"。
public string AuthenticationType {
get {
return "WSSE";
}
}
CanPreAuthenticate 属性
True
表示该模块支持预认证。WSSE
认证可以支持预认证。
public bool CanPreAuthenticate {
get {
return true;
}
}
Authenticate / PreAuthenticate 函数
当 WebRequest
需要认证信息时,AuthenticationManager
会调用这些函数。这些函数的实现几乎相同。唯一的区别是 PreAuthenticate
函数没有 challenge 参数,因为它在发送请求之前调用。
您应该如下实现这些方法:
- 确认 "
challenge
" 参数包含相应的签名 ("WSSE
")。这并不简单,因为 challenge 字符串中可能包含被引用的消息,应忽略这些消息。if (!ContainsSignatureInChallenge(challenge, Signature)) return null;
- 从 "credentials" 参数获取与请求 URI 对应的
NetworkCredential
。if (credentials == null) return null; NetworkCredential credential = credentials.GetCredential(request.RequestUri, AuthenticationType ); if (credential == null) return null;
- 调用
ShouldSendCredential.CredentialPolicy
来检查是否应发送凭据。ICredentialPolicy credentialpolicy = AuthenticationManager.CredentialPolicy; if ((credentialpolicy != null) && (!credentialpolicy.ShouldSendCredential (request.RequestUri, request, credential, this))) return null;
- 根据需要修改
WebRequest
对象。WSSE
认证需要 "X-WSSE
" 标头,其中包含用户名、Nonce、创建时间和密码摘要。request.Headers.Add("X-WSSE", ..... );
- 返回新的
Authorization
对象,其中包含 "Authorization
" 标头所需的认证信息。对于WSSE
,"Authorization
" 标头不包含任何安全信息。return new Authorization("WSSE profile=\"UsernameToken\"", true);
如何启用自定义认证模块
要使用自定义认证模块,您需要将其注册到 AuthenticationManager
。通常,在应用程序的初始化代码中调用 AuthenticationManager.Register
。
System.Net.AuthenticationManager.Register(New WSSEClient)
您也可以使用应用程序配置文件。
<configuration>
<system.net>
<authenticationModules>
<add type="Rei.Net.WSSEClient, WSSEClient" />
</authenticationModules>
</system.net>
</configuration>
历史
- 2007 年 6 月 25 日:初始发布