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(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
// or, if "fPasswordIsMD5" is True:
// md5(<password>:<nonce>:md5(<cmd>:<url>))
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進行比較,如果匹配則認證成功並返回相關資源,否則再執行②,重新進行認證。
⑤以後每次訪問都要帶上認證頭部。