※本記事は、旧ブログからの再掲です。
SSL通信を行う際には認証処理も必要となります。
今回はテスト用に自己証明書を使って認証を行う方法です。
まず自己証明書の作成からです。
SSL環境構築 という記事にSSLサーバー証明書の作成方法を書いているので、その続きからとします。
証明書の形式をPEMからDERに変換します。
ターミナル上で下記コマンドを実行します。
# openssl x509 -inform PEM -outform DER -in server.crt -out server.der
作成されたserver.derをアプリのプロジェクトにコピーします。
TARGETSの”Copy Bundle Resources”に追加されていることを確認します。
次に認証処理の追加です。
通信処理を行うクラスにNSURLConnectionのdelegateメソッドを追加します。
iOS5.0で以下3つのメソッドは非推奨となり、その代わりにconnection:willSendRequestForAuthenticationChallengeが呼ばれます。
・connection:canAuthenticateAgainstProtectionSpace
・connection:didReceiveAuthenticationChallenge
・connection:didCancelAuthenticationChallenge
(同期通信・非同期通信の記事にある”サーバーと送受信を行うクラス”に追加しています)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
static SecCertificateRef sslCertificate = NULL; + (SecCertificateRef)sslCertificate { if ( ! sslCertificate ) { NSString *path = [[NSBundle mainBundle] pathForResource:@"server" ofType:@"der"]; NSData *data = [[NSData alloc] initWithContentsOfFile:path]; sslCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data); } return sslCertificate; } /* * 認証が必要な場合に呼び出される * (NSURLConnectionに対しある種類の認証手順を知っている旨を伝える) * ※Deprecated in iOS5.0 */ - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { return [[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]; } /* * 認証が必要な場合に呼び出される * (サーバまたはクライアントが指定したトラストポリシー、キー、ホスト名を修正してトラストポリシーが正常に評価されるようにする) * ※Deprecated in iOS5.0 */ - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ( [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] || [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest] ) { NSLog(@"Basic認証"); } else if ( [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] ) { NSLog(@"SSL認証"); NSURLProtectionSpace *protecitionSpace = [challenge protectionSpace]; SecTrustRef trust = [protecitionSpace serverTrust]; NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; NSArray *certs = [[NSArray alloc] initWithObjects:(id)[[self class] sslCertificate], nil]; OSStatus status = SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certs); if ( status != errSecSuccess ) { NSLog(@"SecTrustSetAnchorCertificates err:%ld", status); [connection cancel]; return; } SecTrustResultType trustResult = kSecTrustResultInvalid; status = SecTrustEvaluate(trust, &trustResult); if ( status != errSecSuccess ) { NSLog(@"SecTrustEvaluate err:%ld", status); NSLog(@"trustResult:%ld", trustResult); [connection cancel]; return; } switch ( trustResult ) { case kSecTrustResultProceed: // valid and user has explicitly accepted it. case kSecTrustResultUnspecified: // valid and user has not explicitly accepted or reject it. generally you accept it in this case. { [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; return; } break; case kSecTrustResultRecoverableTrustFailure: // invalid, but in a way that may be acceptable, such as a name mismatch, expiration, or lack of trust (such as self-signed certificate) { [challenge.sender cancelAuthenticationChallenge:challenge]; [connection cancel]; } break; default: NSLog(@"cancelAuthenticationChallenge"); [challenge.sender cancelAuthenticationChallenge:challenge]; [connection cancel]; break; } } } /* * 認証をキャンセルした時に呼び出される * ※Deprecated in iOS5.0 */ - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { [connection cancel]; [self connection:connection didFailWithError:nil]; } /* * 認証が必要な場合に呼び出される * for iOS5.0 and later */ - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ( [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] || [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest] ) { NSLog(@"Basic認証"); } else if ( [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust] ) { NSLog(@"SSL認証"); NSURLProtectionSpace *protecitionSpace = [challenge protectionSpace]; SecTrustRef trust = [protecitionSpace serverTrust]; NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; NSArray *certs = [[NSArray alloc] initWithObjects:(id)[[self class] sslCertificate], nil]; OSStatus status = SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)certs); if ( status != errSecSuccess ) { NSLog(@"SecTrustSetAnchorCertificates err:%ld", status); [connection cancel]; return; } SecTrustResultType trustResult = kSecTrustResultInvalid; status = SecTrustEvaluate(trust, &trustResult); if ( status != errSecSuccess ) { NSLog(@"SecTrustEvaluate err:%ld", status); NSLog(@"trustResult:%ld", trustResult); [connection cancel]; return; } switch ( trustResult ) { case kSecTrustResultProceed: // valid and user has explicitly accepted it. case kSecTrustResultUnspecified: // valid and user has not explicitly accepted or reject it. generally you accept it in this case. { [challenge.sender useCredential:credential forAuthenticationChallenge:challenge]; return; } break; case kSecTrustResultRecoverableTrustFailure: // invalid, but in a way that may be acceptable, such as a name mismatch, expiration, or lack of trust (such as self-signed certificate) { [challenge.sender cancelAuthenticationChallenge:challenge]; [connection cancel]; } break; default: [challenge.sender cancelAuthenticationChallenge:challenge]; [connection cancel]; break; } } } |