SASL は、Lightweight Directory Access Protocol、バージョン 3 (LDAP v3) や Internet Message Access Protocol、バージョン 4 (IMAP v4) などのプロトコルによって使用され、プラグイン可能な認証を有効にします。認証メソッドをプロトコルに固定する代わりに、LDAP v3 および IMAP v4 は SASL を使用して認証を実行します。したがって、さまざまな SASL 機構による認証が可能になります。
さまざまなセキュリティーレベルおよび配備シナリオ用にインターネットコミュニティーによって定義された、多数の標準 SASL 機構があります。その範囲はセキュリティーなし (匿名認証など) から高セキュリティー (Kerberos 認証など) にいたるまで、さまざまなレベルがあります。
Java SASL API
Java SASL API は、SASL 機構を使用するアプリケーション用のクラスおよびインタフェースを定義します。Java SASL API は、機構に依存しないように定義されています。API を使用するアプリケーションを、特定の SASL 機構の使用に固定する必要はありません。この API は、クライアントとサーバーの両方のアプリケーションをサポートしています。アプリケーションは、受動的な辞書攻撃の影響を受けやすいかどうか、および匿名認証を受け入れるかどうかなど、必要なセキュリティー機能に基づいて使用する機構を選択できます。
Java SASL API では、開発者は独自のカスタム SASL 機構を使用することもできます。SASL 機構は、Java 暗号化アーキテクチャー (JCA) を使用してインストールされます。
SASL をいつ使用するか
SASL は、プラグイン可能な認証とセキュリティー層をネットワークアプリケーションに提供します。Java SE には、Java Secure Socket Extension (JSSE) や Java Generic Security Service (Java GSS) など、同様の機能を提供するその他の機能があります。JSSE は、SSL および TLS プロトコルの Java 言語バージョンに、フレームワークと実装を提供します。Java GSS は、Generic Security Service Application Programming Interface (GSS-API) 用の Java 言語バインディングです。Java SE では、この API の下で現在サポートされている機構は、Kerberos v5 のみです。
JSSE および Java GSS と比較すると、SASL は相対的に軽量であり、最近のプロトコルの中で一般的です。一般的かつ軽量な (インフラストラクチャーサポートの点で) SASL 機構がいくつか定義されているという利点もあります。一方、主要な JSSE および 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 を使用するアプリケーションおよびサービスを構築する場合、使用する API は JSSE です。JSSE と Java GSS をいつ使用するかについての詳細は、「Java セキュリティードキュメント」を参照してください。
API の概要
SASL はチャレンジ応答プロトコルです。サーバーはクライアントにチャレンジを発行し、クライアントはチャレンジに基づいて応答を送信します。この交換は、サーバーが充足してチャレンジを発行しなくなるまで続きます。これらのチャレンジおよび応答は、任意の長さのバイナリトークンです。カプセル化を行うプロトコル (LDAP や IMAP など) は、これらのトークンの符号化および交換方法を指定します。たとえば、LDAP は、SASL トークンが LDAP バインド要求および応答にどのようにカプセル化されるかを指定します。
Java SASL API は、この相互作用と使用方法のスタイルに従ってモデル化されています。インタフェース SaslClient および SaslServer があり、これらはそれぞれクライアント側およびサーバー側の機構を表します。アプリケーションはチャレンジおよび応答を表すバイト配列によって、機構と相互に作用します。サーバー側の機構は、充足されるまでチャレンジの発行と応答の処理を繰り返します。一方、クライアント側の機構は、サーバーが充足されるまでチャレンジの評価と応答の発行を繰り返します。機構を使用しているアプリケーションが、それぞれの繰り返しを制御します。つまり、機構を使用しているアプリケーションは、チャレンジまたは応答をプロトコルパケットから抽出して機構に渡し、機構から返された応答またはチャレンジをプロトコルパケットに入れて、ピアに送信します。
機構の作成
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; } }
セキュリティー層がネゴシエーション済みの場合、ピアとの後続の通信はすべてセキュリティー層を使用して発生します。セキュリティー層がネゴシエーション済みかどうかを判別するには、ネゴシエーション済みの保護の品質 (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);
これは、Java セキュリティープロパティーファイル ($JAVA_HOME/lib/security/java.security) にあります。security.provider.7=com.sun.security.sasl.Provider
SASL プロバイダを追加または削除するには、セキュリティープロパティーファイルで対応する行を追加または削除します。たとえば、SASL プロバイダを追加し、その機構が SunSASL プロバイダによって実装されている同じ機構よりも優先して選択されるようにする場合は、セキュリティープロパティーファイルに小さい番号で行を追加します。
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 プロバイダを探します。次に、プロバイダは、要求された機構が選択ポリシープロパティーに一致するかどうかを判別し、一致する場合は、機構の実装を返します。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 Subject 承認 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 内でデフォルトのユーザー名として使用して、アプリケーション/エンドユーザーに承認 ID を求めます。それ以外では、承認 ID は Cram-MD5 機構によって使用されません。認証 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 |
特定の強度に複数の選択肢がある場合、選択される暗号は、基盤となるプラットフォームでの暗号の可用性によって決まります。使用する暗号を明示的に指定するには、「com.sun.security.sasl.digest.cipher」プロパティーを対応する暗号 ID に設定します。このプロパティー設定は、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 資格を取得するには、Java Authentication and Authorization Service (JAAS) を使用し、Kerberos ログインモジュールを使用してログインします。詳細および例は、「Kerberos を使った Java GSS-API および JAAS のチュートリアル」を参照してください。JAAS 認証を使用して Kerberos 資格を取得したあとで、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 Subject プロトコル 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 | セキュリティー層で交換されるバッファー |
最初のステップでは、SASL 機構を実装します。クライアント機構を実装するには、SaslClient インタフェースで宣言されたメソッドを実装する必要があります。同様に、サーバー機構の場合は、SaslServer インタフェースで宣言されたメソッドを実装する必要があります。 この説明では、クライアント機構「SAMPLE-MECH」の実装を開発するとします。この機構は、クラス com.example.SampleMechClient によって実装されます。機構が必要とする入力、およびそれらを実装が収集する方法を決定します。たとえば、機構がユーザー名/パスワードベースの場合、実装ではその情報をコールバックハンドラパラメータ経由で収集する必要性が高くなります。
次のステップでは、com.example.SampleMechClient のインスタンスを作成するファクトリクラスを記述します。ファクトリは Sasl.POLICY_* プロパティーによって記述されるように、サポートする機構の特性を決定する必要があります。そうすることにより、互換性のあるポリシープロパティーを使用して API ユーザーが要求したときに、機構のインスタンスを返すことができます。ファクトリは、また、機構を作成する前にパラメータの妥当性をチェックする場合もあります。この説明では、ファクトリクラスの名前を com.example.MySampleClientFactory とします。サンプルファクトリは 1 つの機構のみを処理しますが、1 つのファクトリは任意の数の機構を処理できます。
最後のステップでは、JCA プロバイダを作成します。JCA プロバイダを作成する手順は、「Java 暗号化アーキテクチャー用プロバイダの実装方法」に詳細に説明されています。 SASL クライアントファクトリは、次の形式のプロパティー名を使用して登録されます。
SaslClientFactory.mechName一方、SASL サーバーファクトリは、次の形式のプロパティー名を使用して登録されます。
SaslServerFactory.mechNamemechName は、SASL 機構の名前です。これは、SaslClient.getMechanismName() および SaslServer.getMechanismName() によって返された名前です。この例で、プロバイダが「SAMPLE-MECH」機構を登録する方法を次に示します。
1 つの SASL プロバイダが、多数の機構を処理する場合があります。そのため、関連するファクトリを登録する put の呼び出しが多数ある場合があります。 完了した SASL プロバイダは、前述の手順を使用して、アプリケーションで利用可能にすることができます。put("SaslClientFactory.SAMPLE-MECH", "com.example.MySampleClientFactory");