SASL は LDAP v3 (Lightweight Directory Access Protocol, version 3) や IMAP v4 (Internet Message Access Protocol, version 4) などのプロトコルから使用され、プラグイン可能な認証を使用できるようにします。LDAP v3 および IMAP v4 では、認証方法をプロトコルに直接記述するのではなく、SASL を使用して認証を実行します。そのため、さまざまな SASL 機構経由での認証を使用できます。
インターネットコミュニティで定義された標準 SASL 機構は数多くあり、セキュリティなし (匿名認証など) から高セキュリティ (Kerberos 認証など) に至るまで、さまざまなレベルのセキュリティや配備環境に対応しています。
Java SASL API
Java SASL API では、SASL 機構を使用するアプリケーション用にクラスとインタフェースを定義しています。この API
は機構に依存しないように定義されています。この API を使用するアプリケーションは、特定の SASL
機構を使用するようにハードコーディングする必要はありません。API
ではクライアントとサーバ両方のアプリケーションをサポートしています。アプリケーションでは、受動的な辞書攻撃を許容するかどうか、匿名認証を許可する
かどうかなど、必要なセキュリティ機能に基づいて、使用する機構を選択することができます。
Java SASL API では、開発者が独自のカスタム SASL 機構を使用することもできます。SASL 機構は、Java 暗号化アーキテクチャ (JCA)
を使用してインストールされます。
SASL を使用する場合
SASL では、ネットワークアプリケーションに対して、プラグイン可能な認証およびセキュリティ層を提供します。J2SE には、Java Secure Socket Extension
(JSSE) や Java Generic
Security Service (Java GSS) など、同様の機能を提供するほかの機能もあります。JSSE
ではフレームワークと、Java 言語版の SSL および TLS プロトコルの実装を提供します。Java
GSS は、Generic Security Service Application Programming Interface (GSS-API) の Java
言語バインディングです。J2SE のこの API でサポートされている機構は、現在のところ Kerberos v5 だけです。
JSSE や Java GSS と比べて、SASL は比較的軽量で、最新のプロトコルでは一般的です。また SASL には、インフラストラクチャサポートの点で、一般的かつ軽量な SASL 機構がいくつか定義されているという利点もあります。一方で主な JSSE および Java GSS 機構は、SASL と比べて重量で、より複雑なインフラストラクチャ (JSSE の場合は公開鍵インフラストラクチャ (PKI)、Java GSS の場合は Kerberos) を必要とします。
SASL、JSSE、および Java GSS は組み合わせて使用される場合もあります。たとえばよくある例として、保護されたチャネルの確立に JSSE を使用し、クライアントのユーザ名/パスワードベースの認証に SASL を使用するアプリケーションがあります。GSS-API 機構の上位に置かれる SASL 機構もあります。LDAP で使用される SASL GSS-API/Kerberos v5 機構は一般的な例です。
プロトコルを一から定義し作成する場合を除いて、使用する API を決定する一番大きな要因は、多くの場合プロトコル定義です。たとえば
LDAP および IMAP が SASL を使用するように定義されているため、これらのプロトコルに関連するソフトウェアでは、必然的に Java
SASL API を使用します。Kerberos アプリケーションやサービスの構築時に使用される API は Java GSS
です。SSL/TLS をプロトコルとして使用するアプリケーションやサービスでは、JSSE が API として使用されます。JSSE と
Java GSS を使用するときの比較についての詳細は、『Java
セキュリティドキュメント』を参照してください。
API の概要
SASL
はチャレンジ応答プロトコルです。サーバはクライアントにチャレンジを発行し、クライアントはチャレンジを基に応答を送信します。この交換は、サーバが充
足し、チャレンジが発行されなくなるまで続けられます。これらのチャレンジと応答は、任意長のバイナリトークンです。LDAP や IMAP
などのカプセル化プロトコルでは、これらのトークンが符号化されて交換される方法を指定します。たとえば LDAP では、SASL トークンが
LDAP バインド要求および応答内でカプセル化される方法を指定します。
Java SASL API は、このスタイルの相互作用と使用に従ってモデル化されています。Java SASL API には、SaslClient および SaslServer インタフェースがあり、それぞれクライアント側とサーバ側の機構を表します。アプリケーションは、チャレンジと応答を表すバイト配列を介して機構と対話し ます。サーバ側の機構では、チャレンジの発行と応答の処理が、充足されるまで反復されます。一方クライアント側の機構では、チャレンジの評価と応答の発行 が、サーバが充足されるまで反復されます。これらの機構を使用するアプリケーションでは、各反復を扱います。つまり、アプリケーションではプロトコルパ ケットからチャレンジまたは応答を抽出し、機構に渡します。そして、機構から返された応答またはチャレンジをプロトコルパケットに埋め込み、相手側に送信 します。
機構の作成
SASL 機構を使用するクライアントおよびサーバのコードは、特定の機構を使用するようにハードコーディングされていません。SASL
を使用するプロトコルの多くでは、サーバはサポートする SASL
機構のリストを静的または動的に通知します。するとクライアントは、そのセキュリティ要件に応じて SASL 機構のうちの 1 つを選択します。
Sasl クラスは、SaslClient および SaslServer のインスタンスを作成するために使用されます。アプリケーションが可能な SASL 機構のリストを使用して、SASL クライアント機構を作成する方法について、以下に例を示します。
プラットフォームでサポートされている機構の利用可能性、およびパラメータ経由で指定されるその他の構成情報を基に、Java SASL フレームワークではリストされた機構のうち 1 つを選択し、SaslClient のインスタンスを返します。String[] mechanisms = new String[]{"DIGEST-MD5", "PLAIN"}; SaslClient sc = Sasl.createSaslClient(mechanisms, authzid, protocol, serverName, props, callbackHandler);
選択した機構の名前は、通常はアプリケーションプロトコルを介してサーバに転送されます。機構の名前を受け取ると、サーバは対応する SaslServer オブジェクトを作成して、クライアントが送信した応答を処理します。サーバが SaslServer のインスタンスを作成する方法を、以下の例に示します。
SaslServer ss = Sasl.createSaslServer(mechanism, protocol, myName, props, callbackHandler);
Java SASL API は汎用フレームワークなので、各種の機構を扱うことができる必要があります。それぞれの機構は、入力で初期化されなければならず、場合によっては先に進め るために入力が必要な場合があります。この API では、アプリケーションが入力を機構に渡すための方法を 3 つ用意しています。
機構では javax.security.auth.callback パッケージで定義したコールバックを使用できる。これらのコールバックは汎用のコールバックで、認証を実行するアプリケーションを構築するときに便利であ る。また機構では、レルムや認証情報を収集する SASL 固有のコールバックや、標準化されていない機構固有のコールバックも必要になる。 アプリケーションでは、さまざまな機構を扱えなければならない。したがって、コールバックハンドラは、機構が要求する可能性のあるコールバックをすべて提 供できる必要がある。これは、一般には任意の機構では不可能である。しかし、配備されて使用される機構の数が制限されるので、通常は実行できる可能性があ る
クライアントアプリケーションは、機構 (sc) を使用して認証の各手順を反復し、サーバから取得したチャレンジを評価し、応答をサーバに返します。このサイクルは、機構またはアプリケーションレベルの プロトコルが認証が完了したことを提示するまで、または機構がチャレンジを評価できないことが判明するまで続けられます。機構がチャレンジを評価できない と、エラーを示す例外がスローされ、認証が中断されます。機構とプロトコル間に完了状態の不一致がある場合、その不一致はエラーとして扱われなければなり ません。これは、認証の交換がうまくいかないことを意味している可能性があるためです。// Get optional initial response byte[] response = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[]) :null); String mechanism = sc.getName(); // Send selected mechanism name and optional initial response to server send(mechanism, response); // Read response msg = receive(); while (!sc.isComplete() && (msg.status == CONTINUE || msg.status == SUCCESS)) { // Evaluate server challenge response = sc.evaluateChallenge(msg.contents); if (msg.status == SUCCESS) { // done; server doesn't expect any more SASL data if (response != null) { throw new IOException( "Protocol error:attempting to send response after completion"); } break; } else { send(mechanism, response); msg = receive(); } }
サーバが認証に SaslServer を使用する方法を以下の例に示します。
サーバアプリケーションは、クライアントの応答を機構 (ss) に渡して処理させることで、認証の各手順を反復します。応答が不適切な場合、機構は SaslException をスローしてエラーを提示します。そしてサーバはエラーを報告し、認証を中断します。応答が適切な場合、機構はクライアントに送信されるチャレンジデータ を返し、認証が完了したかどうかを提示します。チャレンジデータには、「成功」を示すデータを付属させることができます。これは、たとえばネゴシエートさ れた状態をファイナライズするようにクライアントに通知するときに使用します。// Read request that contains mechanism name and optional initial response msg.receive(); // Obtain a SaslServer to perform authentication SaslServer ss = Sasl.createSaslServer(msg.mechanism, protocol, myName, props, callbackHandler); // Perform authentication steps until done while (!ss.isComplete()) { try { // Process response byte[] challenge = sc.evaluateResponse(msg.contents); if (ss.isComplete()) { send(mechanism, challenge, SUCCESS); } else { send(mechanism, challenge, CONTINUE); msg.receive(); } } catch (SaslException e) { send(ERROR); sc.dispose(); break; } }
セキュリティ層がネゴシエートされると、それ以降、相手側とのすべての通信は、セキュリティ層を使用して行う必要があります。セキュリティ層のネゴ シエートが完了しているかどうかを判断するには、ネゴシエートされた quality-of-protection (QOP、保護の品質) を機構から取得します。セキュリティ層がネゴシエートされたかどうかを判断する方法について、以下に例を示します。
整合性や機密性がネゴシエートされたことを Sasl.QOP プロパティが示している場合は、セキュリティ層のネゴシエートも完了しています。String qop = (String) sc.getNegotiatedProperty(Sasl.QOP); boolean hasSecurityLayer = (qop != null && (qop.equals("auth-int") || qop.equals("auth-conf")));
ネゴシエートされた層を使用して相手側と通信するときは、アプリケーションはまず wrap メソッドを使用して、相手側に送信されるデータを符号化し、「ラップされた」バッファを生成します。次に、ラップされたバッファ内のオクテット数を表す長 さフィールド、そしてラップされたバッファの内容を相手側に転送します。オクテットのストリームを受け取る相手側は、バッファを (長さフィールドなしで) unwrap に渡し、送信されたバイトをデコードしたデータを取得します。 このプロトコルの詳細については、RFC 2222 を参照してください。クライアントアプリケーションがセキュリティ層を使用して、アプリケーションデータを送受信する方法について、以下の例に示します。
// Send outgoing application data to peer byte[] outgoing = ...; byte[] netOut = sc.wrap(outgoing, 0, outgoing.length); send(netOut.length, netOut); // send to peer // Receive incoming application data from peer byte[] netIn = receive(); // read length and ensuing bytes from peer byte[] incoming = sc.unwrap(netIn, 0, netIn.length);
security.provider.7=com.sun.security.sasl.Provider
SASL プロバイダを追加または削除するには、セキュリティプロパティファイルで対応する行を追加または削除します。たとえば SASL プロバイダを追加し、SunSASL プロバイダで実装された同じ機構よりも、この追加した SASL プロバイダの機構を選択する場合は、より小さい番号の行をセキュリティプロパティファイルに追加します。
security.provider.7=com.example.MyProvider security.provider.8=com.sun.security.sasl.Provider
その代わりに、java.security.Security クラスを使用して、独自のプロバイダをプログラムで追加することもできます。たとえば以下のコード例では、利用可能な SASL セキュリティプロバイダのリストに com.example.MyProvider を登録します。
アプリケーションが 1 つまたは複数の SASL 機構名を渡して SASl 機構を要求すると、SASL フレームワークでは、登録済みプロバイダのリストを順番に調べ、その機構をサポートする登録済み SASL プロバイダを検索します。プロバイダでは、要求された機構が選択 ポリシープロパティに一致するかどうかを判断し、一致する場合は機構の実装を返します。Security.addProvider(new com.example.MyProvider());
選択ポリシープロパティでは、特定の攻撃に対する許容性など、機構のセキュリティ面を指定します。これらは機構の特性 (定義) であり、実装ではありません。そのため、すべてのプロバイダは、特定の機構について同じ結果になるはずです。たとえば PLAIN 機構では、その実装方法に関係なく、プレーンテキスト攻撃を許容します。選択ポリシープロパティが指定されない場合は、選択された機構に制限はありませ ん。これらのプロパティを使用するとアプリケーションでは、ランタイムに配備される可能性のある不適切な機構を使用しないようにできます。たとえばプレー ンテキスト攻撃を許容する機構を使用したくない場合は、アプリケーションでは以下のようなコードを使用します。
選択ポリシープロパティの説明については、Sasl クラスを参照してください。Map props = new HashMap();
props.add(Sasl.POLICY_NOPLAINTEXT, "true");
SaslClient sc = Sasl.createSaslClient(mechanisms,
authzid, protocol, serverName, props, callbackHandler);
クライアントの機構の名前 | パラメータ/入力 | コールバック | 構成プロパティ | 選択ポリシー |
---|---|---|---|---|
CRAM-MD5 | 認証 ID (デフォルトユーザ名として) | NameCallback PasswordCallback |
Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
|
DIGEST-MD5 | 認証 ID プロトコル ID サーバ名 |
NameCallback
PasswordCallback RealmCallback RealmChoiceCallback |
Sasl.QOP Sasl.STRENGTH Sasl.MAX_BUFFER Sasl.SERVER_AUTH "javax.security.sasl.sendmaxbuffer" "com.sun.security.sasl.digest.cipher" |
Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
EXTERNAL | 認証 ID 外部チャネル |
Sasl.POLICY_NOPLAINTEXT Sasl.POLICY_NOACTIVE Sasl.POLICY_NODICTIONARY |
||
GSSAPI | JAAS
被認証者 認証 ID プロトコル ID サーバ名 |
Sasl.QOP Sasl.MAX_BUFFER Sasl.SERVER_AUTH "javax.security.sasl.sendmaxbuffer" |
Sasl.POLICY_NOACTIVE Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
|
PLAIN | 認証 ID | NameCallback PasswordCallback |
Sasl.POLICY_NOANONYMOUS |
これらの機構を SunSASL プロバイダから使用するアプリケーションでは、必須パラメータ、コールバック、およびプロパティを渡す必要があります。プロパティには、適切なデフォルト 値が設定されているので、アプリケーションでデフォルト値をオーバーライドする場合以外は設定する必要はありません。ほとんどのパラメータ、コールバッ ク、プロパティについては、API のマニュアルで説明しています。以下のセクションでは、API のマニュアルではまだ扱われていない、機構固有の動作およびパラメータについて説明します。
CRAM-MD5
Cram-MD5 クライアント機構では、認証 ID パラメータを使用します。このパラメータを指定した場合は、認証 ID
をアプリケーションやエンドユーザに要求する場合に、
NameCallback
のデフォルトユーザ名として使用されます。
それ以外の場合、Cram-MD5 機構では認証 ID を使用しません。サーバと交換されるのは認証 ID だけです。
DIGEST-MD5
Digest-MD5 機構は、ダイジェスト認証とセキュリティ層の確立 (オプション)
に使用されます。セキュリティ層で使用する暗号として、Triple DES、DES、および RC4 (128、56、および 40 ビット)
を指定します。Digest-MD5 機構では、使用するプラットフォームで利用可能な暗号だけをサポートします。たとえばプラットフォームで RC4
暗号をサポートしていない場合は、Digest-MD5 機構ではその暗号方式を使用しません。
Sasl.STRENGTH プロパティでは、「high」、「medium」、および「low」設定をサポートしており、デフォルトは「high,medium,low」です。暗号 は、以下のように強度設定にマッピングされています。
強度 | 暗号 | 暗号 ID |
---|---|---|
high | Triple DES RC4 128 ビット |
3des rc4 |
medium | DES RC4 56 ビット |
des rc4-56 |
low | RC4 40 ビット | rc4-40 |
特定の強度に複数の選択肢がある場合、選択される暗号は、配下のプラットフォームでの暗号の利用可能性で決まります。使用する暗号の名前を明示する には、対応する暗号 ID を「com.sun.security.sasl.digest.cipher」プロパティに設定します。このプロパティ設定は Sasl.STRENGTH、 および配下のプラットフォームで利用可能な暗号と互換性がなければなりません。たとえば「low」に設定されている Sasl.STRENGTH と、「3des」に設定されている「com.sun.security.sasl.digest.cipher」では互換性がありません。 「com.sun.security.sasl.digest.cipher」プロパティにはデフォルトがありません。
「javax.security.sasl.sendmaxbuffer」プロパティでは、送信バッファの最大サイズ (バイト)
を文字列表現で指定します。デフォルトは、65536 です。バイト数の実際の最大値はこの値の最小値であり、相手側の受信バッファの最大値です。
GSSAPI
GSSAPI 機構は、Kerberos v5 認証とセキュリティ層の確立 (オプション)
に使用されます。この機構では、呼び出し側スレッドの
Subject
にクライアントの Kerberos 資格が含まれていること、または暗黙的に Kerberos
にログインすることで資格を取得できることが想定されます。クライアントの Kerberos 資格を取得するには、Kerberos
ログインモジュールを使用したログインで、Java
Authentication and Authorization Service (JAAS) を使用します。詳細および例は、「Kerberos を使った Java GSS-API および
JAAS のチュートリアル」を参照してください。Kerberos 資格の取得で JAAS 認証を使用したあとは、SASL GSSAPI
機構を使用するコードを doAs
または doAsPrivileged
内に埋め込みます。
明示的に JAAS をプログラミングせずに Kerberos 資格を取得する方法は、「Kerberos を使った Java GSS-API および JAAS のチュートリアル」を参照してください。このアプローチを使用する場合は、コードを doAs または doAsPrivileged 内でラップする必要はありません。LoginContext lc = new LoginContext("JaasSample", new TextCallbackHandler()); lc.login(); lc.getSubject().doAs(new SaslAction()); class SaslAction implements java.security.PrivilegedAction { public class run() { ... String[] mechanisms = new String[]{"GSSAPI"}; SaslClient sc = Sasl.createSaslClient(mechanisms, authzid, protocol, serverName, props, callbackHandler); ... } }
「javax.security.sasl.sendmaxbuffer」プロパティでは、送信バッファの最大サイズ (バイト)
を文字列表現で指定します。デフォルトは、65536 です。バイト数の実際の最大値はこの値の最小値であり、相手側の受信バッファの最大値です。
サーバの機構
サーバの機構と要求される入力について、以下の表にまとめます。
サーバの機構の名前 | パラメータ/入力 | コールバック | 構成プロパティ | 選択ポリシー |
---|---|---|---|---|
CRAM-MD5 | サーバ名 | AuthorizeCallback NameCallback PasswordCallback |
Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
|
DIGEST-MD5 | プロトコル ID サーバ名 |
AuthorizeCallback
NameCallback PasswordCallback RealmCallback |
Sasl.QOP Sasl.STRENGTH Sasl.MAX_BUFFER "javax.security.sasl.sendmaxbuffer" "com.sun.security.sasl.digest.realm" "com.sun.security.sasl.digest.utf8" |
Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
GSSAPI | JAAS
被認証者 プロトコル ID サーバ名 |
AuthorizeCallback | Sasl.QOP Sasl.MAX_BUFFER "javax.security.sasl.sendmaxbuffer" |
Sasl.POLICY_NOACTIVE Sasl.POLICY_NOANONYMOUS Sasl.POLICY_NOPLAINTEXT |
これらの機構を SunSASL プロバイダから使用するアプリケーションでは、必須パラメータ、コールバック、およびプロパティを渡す必要があります。プロパティには、適切なデフォルト 値が設定されているので、アプリケーションでデフォルト値をオーバーライドする場合以外は設定する必要はありません。
サーバ機構の全ユーザは、AuthorizeCallback を扱うコールバックハンドラが必要です。このコールバックハンドラは、要求された認証 ID に代わって認証済みのユーザが操作できるかどうかを判断するために、また標準化が適用可能な場合に、認証済みのユーザの標準化された名前を取得するために 機構が使用します。
ほとんどのパラメータ、コールバック、プロパティについては、API のマニュアルで説明しています。以下のセクションでは、API のマニュアルではまだ扱われていない、機構固有の動作およびパラメータについて説明します。
「javax.security.sasl.sendmaxbuffer」プロパティについては、Digest-MD5 クライアントのセクションで説明しています。
「com.sun.security.sasl.digest.realm」プロパティでは、サーバがサポートするレルムの名前を空白文字で区切っ たリストを指定します。このリストは、チャレンジの一部としてクライアントに送信されます。このプロパティが設定されていない場合、デフォルトのレルムは サーバの名前 (パラメータで指定) になります。
「com.sun.security.sasl.digest.utf8」プロパティでは、使用する文字エンコーディングを指定します。 「true」は UTF-8 エンコーディングを使用すること、「false」は ISO Latin 1 (ISO-8859-1) を使用することをそれぞれ意味します。デフォルト値は「true」です。
javax.security.sasl.level=FINEST handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level=FINEST
機構と、機構が生成するロギング出力について、以下の表にまとめます。
機構 | ロギングレベル | ログ記録される情報 |
---|---|---|
CRAM-MD5 | FINE | 構成プロパティ、チャレンジまたは応答メッセージ |
DIGEST-MD5 | INFO | エンコーディングの問題 (MAC の不一致や、不適切なパディングなど) により破棄されたメッセージ |
DIGEST-MD5 | FINE | 構成プロパティ、チャレンジまたは応答メッセージ |
DIGEST-MD5 | FINER | チャレンジまたは応答メッセージについての詳細情報 |
DIGEST-MD5 | FINEST | セキュリティ層で交換されるバッファ |
GSSAPI | FINE | 構成プロパティ、チャレンジまたは応答メッセージ |
GSSAPI | FINER | チャレンジまたは応答メッセージについての詳細情報 |
GSSAPI | FINEST | セキュリティ層で交換されるバッファ |
1 番目の手順では、SASL 機構を実装します。クライアントの機構を実装するには、SaslClient インタフェースで宣言されたメソッドを実装する必要があります。同様にサーバの機構については、SaslServer インタフェースで宣言されたメソッドを実装する必要があります。 ここでの説明のために、com.example.SampleMechClient クラスに実装されるクライアントの機構「SAMPLE-MECH」の実装を開発しているとします。機構が必要とする入力の内容と、実装でその入力を収集す る方法を決定する必要があります。たとえばこの機構がユーザ名/パスワードを基にしたものである場合、実装では、コールバックハンドラパラメータを介して 情報を収集する必要があるかもしれません。
2 番目の手順では、com.example.SampleMechClient のインスタンスを作成するファクトリクラスを記述します。ファクトリでは、サポートされる機構の特性 (Sasl.POLICY_* プロパティで記述) を判別する必要があります。こうすることで、互換性のあるポリシープロパティを使用してAPI ユーザが機構を要求したときに、機構のインスタンスを返すことができます。また、ファクトリでは機構を作成する前に、パラメータの妥当性を確認できます。 ここでの説明のために、ファクトリクラスの名前が com.example.MySampleClientFactory であるとします。この例のファクトリは 1 つの機構だけを扱いますが、1 つのファクトリは任意の数の機構を扱えます。
最後の手順では、JCA プロバイダを作成します。JCA プロバイダを作成する手順についての詳細は、マニュアル「Java 暗号化アーキテクチャ用プロバイダの実装方法」で説明してい ます。 SASL クライアントファクトリは、以下の形式のプロパティ名を使用して登録されます。
SaslClientFactory.mechName一方、SASL サーバファクトリは、以下の形式のプロパティ名を使用して登録されます。
SaslServerFactory.mechNameここで「mechName」は SASL 機構の名前です。この値は SaslClient.getMechanismName() および SaslServer.getMechanismName() で返される値です。ここでの例を続けて、プロバイダが「SAMPLE-MECH」機構を登録する方法について、以下に示します。
1 つの SASL プロバイダは、多くの機構を扱えます。そのため、適切なファクトリを登録するために、 put が複数回呼び出される場合があります。 問題のない SASL プロバイダであれば、先に説明した手順によって、アプリケーションで利用可能にで きます。put("SaslClientFactory.SAMPLE-MECH", "com.example.MySampleClientFactory");