char* RTSPClient::createAuthenticatorString(char const* cmd, char const* url) {
Authenticator& auth = fCurrentAuthenticator; // alias, for brevity
if (auth.realm() != NULL && auth.username() != NULL && auth.password() != NULL) {
// We have a filled-in authenticator, so use it:
char* authenticatorStr;
if (auth.nonce() != NULL) { // Digest authentication
char const* const authFmt =
"Authorization: Digest username=\"%s\", realm=\"%s\", "
"nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
char const* response = auth.computeDigestResponse(cmd, url);
unsigned authBufSize = strlen(authFmt)
+ strlen(auth.username()) + strlen(auth.realm())
+ strlen(auth.nonce()) + strlen(url) + strlen(response);
authenticatorStr = new char[authBufSize];
sprintf(authenticatorStr, authFmt,
auth.username(), auth.realm(),
auth.nonce(), url, response);
auth.reclaimDigestResponse(response);
} else { // Basic authentication
char const* const authFmt = "Authorization: Basic %s\r\n";
unsigned usernamePasswordLength = strlen(auth.username()) + 1 + strlen(auth.password());
char* usernamePassword = new char[usernamePasswordLength+1];
sprintf(usernamePassword, "%s:%s", auth.username(), auth.password());
char* response = base64Encode(usernamePassword, usernamePasswordLength);
unsigned const authBufSize = strlen(authFmt) + strlen(response) + 1;
authenticatorStr = new char[authBufSize];
sprintf(authenticatorStr, authFmt, response);
delete[] response; delete[] usernamePassword;
}
return authenticatorStr;
}
// We don't have a (filled-in) authenticator.
return strDup("");
}
char const* Authenticator::computeDigestResponse(char const* cmd,
char const* url) const {
// The "response" field is computed as:
// md5(md5(::)::md5(:))
// or, if "fPasswordIsMD5" is True:
// md5(::md5(:))
char ha1Buf[33];
if (fPasswordIsMD5) {
strncpy(ha1Buf, password(), 32);
ha1Buf[32] = '\0'; // just in case
} else {
unsigned const ha1DataLen = strlen(username()) + 1
+ strlen(realm()) + 1 + strlen(password());
unsigned char* ha1Data = new unsigned char[ha1DataLen+1];
sprintf((char*)ha1Data, "%s:%s:%s", username(), realm(), password());
our_MD5Data(ha1Data, ha1DataLen, ha1Buf);
delete[] ha1Data;
}
unsigned const ha2DataLen = strlen(cmd) + 1 + strlen(url);
unsigned char* ha2Data = new unsigned char[ha2DataLen+1];
sprintf((char*)ha2Data, "%s:%s", cmd, url);
char ha2Buf[33];
our_MD5Data(ha2Data, ha2DataLen, ha2Buf);
delete[] ha2Data;
unsigned const digestDataLen
= 32 + 1 + strlen(nonce()) + 1 + 32;
unsigned char* digestData = new unsigned char[digestDataLen+1];
sprintf((char*)digestData, "%s:%s:%s",
ha1Buf, nonce(), ha2Buf);
char const* result = our_MD5Data(digestData, digestDataLen, NULL);
delete[] digestData;
return result;
}
basic认证是把用户和密码通过base64加密后发送给服务器进行验证
digest认证则是把服务器响应的401消息里面的特定的值和用户名以及密码结合起来进行不可逆的摘要算法运算得到一个值,然后把用户名和这个摘要值发给服务器,服务通过用户名去 在自己本地找到对应的密码,然后进行同样的摘要运算,再比较这个值是否和客户端发过来的摘要值一样。
TTP协议规范的另一种认证模式是Digest模式,在HTTP1.1时被提出来,它主要是为了解决Basic模式安全问题,用于替代原来的Basic认证模式,Digest认证也是采用challenge/response认证模式,基本的认证流程比较类似,整个过程如下:
①浏览器发送http报文请求一个受保护的资源。
②服务端的web容器将http响应报文的响应码设为401,响应头部比Basic模式复杂,WWW-Authenticate: Digest realm=”myTomcat”,qop=“auth”,nonce=“xxxxxxxxxxx”,opaque=“xxxxxxxx” 。其中qop的auth表示鉴别方式;nonce是随机字符串;opaque服务端指定的值,客户端需要原值返回。
③浏览器弹出对话框让用户输入用户名和密码,浏览器对用户名、密码、nonce值、HTTP请求方法、被请求资源URI等组合后进行MD5运算,把计算得到的摘要信息发送给服务端。请求头部类似如下,Authorization: Digest username=“xxxxx”,realm=“myTomcat”,qop=“auth”,nonce=“xxxxx”,uri=“xxxx”,cnonce=“xxxxxx”,nc=00000001,response=“xxxxxxxxx”,opaque=“xxxxxxxxx” 。其中username是用户名;cnonce是客户端生成的随机字符串;nc是运行认证的次数;response就是最终计算得到的摘要。
④服务端web容器获取HTTP报文头部相关认证信息,从中获取到username,根据username获取对应的密码,同样对用户名、密码、nonce值、HTTP请求方法、被请求资源URI等组合进行MD5运算,计算结果和response进行比较,如果匹配则认证成功并返回相关资源,否则再执行②,重新进行认证。
⑤以后每次访问都要带上认证头部。