HTTP認證-摘要認證
摘要認證 digest authentication ← HTTP1.1提出的基本認證的替代方法
伺服器端以nonce進行質詢,客戶端以使用者名稱,密碼,nonce,HTTP方法,請求的URI等資訊為基礎產生的response資訊進行認證的方式。
※ 不包含密碼的明文傳遞
摘要認證步驟:
1.
2. 伺服器返回401狀態以及nonce等資訊,要求客戶端進行認證。
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest
realm="[email protected]",
qop="auth,auth-int",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
3.
認證必須的五個情報:
? realm : 響應中包含資訊
? nonce : 響應中包含資訊
? username : 使用者名稱
? digest-uri :
? response : 以上面四個資訊加上密碼資訊,使用MD5演算法得出的字串。
Authorization: Digest
username="Mufasa", ← 客戶端已知資訊
realm="[email protected]", ← 伺服器端質詢響應資訊
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", ← 伺服器端質詢響應資訊
uri="/dir/index.html", ← 客戶端已知資訊
qop=auth, ← 伺服器端質詢響應資訊
nc=00000001, ← 客戶端計算出的資訊
cnonce="0a4f113b", ← 客戶端計算出的客戶端nonce
response="6629fae49393a05397450978507c4ef1", ← 最終的摘要資訊 ha3
opaque="5ccc069c403ebaf9f0171e9517f40e41" ← 伺服器端質詢響應資訊
4. 如果認證成功,則返回相應的資源。如果認證失敗,則仍返回401狀態,要求重新進行認證。
特記事項:
1. 避免將密碼作為明文在網路上傳遞,相對提高了HTTP認證的安全性。
2. 當用戶為某個realm首次設定密碼時,伺服器儲存的是以使用者名稱,realm,密碼為基礎計算出的雜湊值(ha1),而非密碼本身。
3. 如果qop=auth-int,在計算ha2時,除了包括HTTP方法,URI路徑外,還包括請求實體主體,從而防止PUT和POST請求表示被人篡改。
4. 但是因為nonce本身可以被用來進行摘要認證,所以也無法確保認證後傳遞過來的資料的安全性。
※ nonce:隨機字串,每次返回401響應的時候都會返回一個不同的nonce。
※ nounce:隨機字串,每個請求都得到一個不同的nounce。
※ MD5(Message Digest algorithm 5,資訊摘要演算法)
① 使用者名稱:realm:密碼 ⇒ ha1
② HTTP請求方式:URI ⇒ ha2
③ ha1:nonce:nc:cnonce:qop:ha2 ⇒ ha3
以上摘自:http://blog.csdn.net/hotnet522/article/details/5824716
下面是程式碼:
-(void)initInterNetImageData:(NSString *)urlStr name:(NSString *)username password:(NSString *)password{
NSLog(@"urlStr====%@",urlStr);
NSURL *url1=[NSURLURLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//自定義的request
NSMutableURLRequest *request1 = [NSMutableURLRequestrequestWithURL:url1];
//請求過期時間
request1.timeoutInterval = 5;
//get請求
request1.HTTPMethod = @"GET";
NSURLSession * session1=[NSURLSessionsharedSession];
[[session1 dataTaskWithRequest:request1completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError * _Nullable error) {
NSHTTPURLResponse * Hresponse1=(NSHTTPURLResponse *)response;
NSLog(@"response=======%@",Hresponse1);
if (Hresponse.statusCode==401) {
NSArray * UrlArr=[urlStr componentsSeparatedByString:@"/"];
NSMutableString * urlStr2=[[NSMutableStringalloc]init];
if ([UrlArr isKindOfClass:[NSArrayclass]]) {
for (int i=3; i<UrlArr.count; i++) {
[urlStr2 appendFormat:@"/%@",UrlArr[i]];
}
}
NSString * autho=[selfhandle401Code:Hresponse1 url2:urlStr2 user:username pass:password];
NSMutableURLRequest *request2 = [[NSMutableURLRequestalloc]initWithURL:url];
request2.timeoutInterval=5;
[request2 addValue:autho forHTTPHeaderField:@"Authorization"];
[request2 setHTTPMethod:@"GET"];
NSURLSession * session2=[NSURLSessionsharedSession];
[[session2 dataTaskWithRequest:request2completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError * _Nullable error) {
NSHTTPURLResponse * Hresponse2=(NSHTTPURLResponse *)response;
if (data.length>100) {
NSString *URLResult = [Hresponse2.URLabsoluteString];
NSLog(@"URLResult=======%@",URLResult);
[self chaxun:URLResult data:data];
}
}]resume];
}
}] resume] ;
}
-(NSString *)handle401Code:(NSHTTPURLResponse *)response url2:(NSString *)url2 user:(NSString *)username pass:(NSString *)password
{
NSString *wwwAuthenticate = [[response allHeaderFields ]objectForKey:@"Www-Authenticate"];
NSString *realm = nil;
NSString *qop = nil;
NSString*nonce = nil;
NSString *opaque = nil;
//解析wwwAuthenticate
NSArray *arr = [wwwAuthenticate componentsSeparatedByString:@","];
for (NSString *perStr in arr) {
NSRange range= [perStr rangeOfString:@"="];
//提取=後面的
NSString *perStrFirst = [perStr substringToIndex:range.location];
NSString *perStrSecond =[perStr substringWithRange:NSMakeRange(range.location+range.length+1,perStr.length-range.location-range.length-2)];
if ([perStrFirst rangeOfString:@"realm"].location !=NSNotFound) {
realm = perStrSecond;
NSLog(@"realm = %@",realm);
}
if ([perStrFirst rangeOfString:@"qop"].location !=NSNotFound) {
qop = perStrSecond;
NSLog(@"qop = %@",qop);
}
if ([perStrFirst rangeOfString:@"nonce"].location !=NSNotFound) {
nonce=perStrSecond;
NSLog(@"nonce = %@",nonce);
}
if ([perStrFirst rangeOfString:@"opaque"].location !=NSNotFound) {
opaque=perStrSecond;
NSLog(@"opaque = %@",opaque);
}
}
// 以從1970年到現在時間差作為branch的值
NSTimeInterval time = [[NSDatedate]timeIntervalSince1970];
long long int t = (long long int)time;
NSString *cnonce = [[NSStringalloc]initWithFormat:@"%lld%d",t,arc4random()+10000];
static unsigned int ncCnt = 1;
NSString *nc = [[NSStringalloc]initWithFormat:@"%d",++ncCnt];
NSString *algorithm = @"MD5";
NSString *uri = url2;
NSString *method = @"GET";
NSString *md5First = [[NSStringalloc]initWithFormat:@"%@:%@:%@",username,realm,password];
NSString * ha1=[md5First MD5Digest];
NSString *md5Second = [[NSStringalloc]initWithFormat:@"%@:%@",method,uri];
NSString * ha2=[md5Second MD5Digest];
NSString *responseStr = [[NSStringalloc]initWithFormat:@"%@:%@:%@:%@:%@:%@",ha1,nonce,nc,cnonce,qop,ha2];
NSString *ha3 = [responseStr MD5Digest];
NSString *authorization = nil;
if (opaque != nil) {
authorization = [[NSStringalloc]initWithFormat:@"Digest username=\"%@\",realm=\"%@\",nonce=\"%@\",uri=\"%@\",response=\"%@\", opaque=\"%@\",qop=%@,nc=\"%@\",cnonce=\"%@\",algorithm=\"%@\"",@"admin",realm,nonce,uri,ha3,opaque,qop,nc,cnonce,algorithm];
}else{
authorization = [[NSStringalloc]initWithFormat:@"Digest username=\"%@\",realm=\"%@\",nonce=\"%@\",uri=\"%@\",response=\"%@\", qop=\"%@\",nc=\"00000001\",cnonce=\"%@\",algorithm=\"%@\"",@"admin",realm,nonce,uri,ha3,qop,cnonce,algorithm];
}
NSLog(@"authorization=========%@",authorization);
return authorization;
}
#import <CommonCrypto/CommonDigest.h>
#import "NSString+MD5.h"
@implementation NSString (MD5)
- (NSString *)MD5Digest
{
const char* input = [self UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(input, (CC_LONG)strlen(input), result);
NSMutableString *digest = [NSMutableStringstringWithCapacity:CC_MD5_DIGEST_LENGTH *2];
for (NSInteger i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[digest appendFormat:@"%02x", result[i]];
}
return digest;
}
個人遇到的問題總結:
1.-(NSString *)handle401Code:(NSHTTPURLResponse *)response url2:(NSString *)url2 user:(NSString *)username pass:(NSString *)password