質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

iOSアプリからのクライアント認証について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 4,502

konryu304

score 8

前提・実現したいこと

iOSアプリにてクライアント証明書を使用したシステムを作成しています。
言語はObjective-Cを使用。
実現したいことは以下の2つです。
①オレオレサーバ証明書を使用した認証
テスト用のサーバを立てているのでサーバ証明書の認証が必要になります。
正式なサーバ証明書がある場合、このステップはいらないかもしれません。

②クライアント証明書を使用した認証
独自のiOSアプリよりクライアント証明書の認証を行いたいです。
iOSアプリに事前にクライアント証明書を組み込んでおき、https通信の前に読み込む方式を考えています。
下記の記事を参考にしています。
http://cocoadays-info.blogspot.jp/2012/01/blog-post_21.html
https://gist.github.com/shunsuke0125/9181757

発生している問題・エラーメッセージ

実現したいことについてそれぞれ問題が発生しています。
①オレオレサーバ証明書を使用した認証
下部に載せているコードを実行したところ、認証チャレンジにてkSecTrustResultRecoverableTrustFailureとなり認証失敗となります。
safariからの通信はできているため、サーバ証明書の内容は正しいと思われます。
下記のページを参考に実装しています。
http://miblog.guruguruheadslab.com/archives/46
SSL通信を行うためにxcode側で何か必要なのでしょうか・・・・

②クライアント証明書を使用した認証
認証が必要な時に呼ばれる下記のメソッドが呼ばれません。

  • (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    ①の認証が失敗しているためでしょうか。

③その他
下記に記載しているクライアント証明書の認証処理が仮に通った場合、GET要求が発行されることになるのでしょうか。
それとも、認証が成功し、コネクションが確立された後に再度発行する必要があるのでしょうか。

該当のソースコード

Objective-C

//GET要求処理 ここから開始

  • (void)testClientCertificate
    {
    NSString* url = @“GET要求先のURL”;

//リクエスト作成
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
//コネクション作成
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];

if (!connection) {
NSLog(@"コネクションなし");
}
else
{
NSLog(@"コネクションあり");

}
}

+ (SecCertificateRef)sslCertificate
{
if (!sslCertificate )
{
NSString *path = [[NSBundle mainBundle] pathForResource:@“サーバ証明書のファイル名” ofType:@"cer"];
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];
    }

/*

  • 認証が必要な場合に呼び出される
  • 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;
}
}
else if ( [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
{
NSLog(@"クライアント認証");

OSStatus status;
CFArrayRef importedItems = NULL;

// パスワード設定
NSString *password = @“パスワード”;

//クライアント証明書データ作成
NSString *pkcs12Path = [[NSBundle mainBundle] pathForResource:@“クライアント証明書の名前” ofType:@"pfx"];

//認証データP12のファイルを読み込み
NSData *PKCS12Data = [NSData dataWithContentsOfFile:pkcs12Path];

status = SecPKCS12Import((bridge CFDataRef)PKCS12Data,
(
bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:password,
kSecImportExportPassphrase,
nil],
&importedItems);

if (status == errSecSuccess) {

NSArray* items = (bridge NSArray*)importedItems;
NSLog(@"%@", items);
SecIdentityRef identityRef = (
bridge SecIdentityRef)[[items objectAtIndex:0] objectForKey:(__bridge id)kSecImportItemIdentity];
NSLog(@"%@", identityRef);
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identityRef
certificates:nil
persistence:NSURLCredentialPersistenceNone];
NSLog(@"%@", credential);
//認証送信(?)
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];

if (importedItems != NULL)
CFRelease(importedItems);

}
}
}

補足情報(言語/FW/ツール等のバージョンなど)

●言語
Objective-C
●開発環境
Xcode8.2.1
●iOS
iOS10.2

些細なことでも良いので情報を頂けると助かります。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

参考にされているサイトが古すぎるように思われます。
iOSは9.0以降からATS(App Transport Security)が導入されています。
これはセキュアな通信を確保するための仕組みです。
デフォルトは有効で、無効にするにはinfo.plistで設定しないといけません。
詳しくは以下のサイトを参考にしてください。
http://www.lac.co.jp/blog/category/security/20161017.html

①オレオレサーバ証明書を使用した認証

ATSでもオレオレサーバ証明書を使用することも可能ですが、ATSの要件に含まれる暗号強度で証明書を作成し、
端末にルート証明書をインストールする必要があります。
また、サーバ側の暗号化スイートも楕円暗号に対応したものでなくてはいけません。
前述のURLをご覧ください。
ATSが導入されて何度か経験したのが、ロードバランサーのSSLアクセラレータが
楕円暗号に対応していないものがあったり、楕円暗号では速度が出ない場合があります。
注意してください。

②クライアント証明書を使用した認証 

おそらくATSによって①がブロックされて②に進んでいないのだと思います。
ただし、NSURLConnectionではなくNSURLSessionを使用すべきです。
NSURLConnectionを使用したアプリは2016年6月1日以降はAppStoreの審査に通らなくなっています。
AppStoreに掲載しないにしても、deprecatedなので新規に作成するアプリで使用すべきではありません。
NSURLSessionでクライアント証明書を使用する方法には以下のteratailの過去の質問が参考になります。
https://teratail.com/questions/45840

③その他 

認証のデリゲートメソッドで正しく認証された結果が返ればGET要求が継続されるはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る