1. 概要

このチュートリアルでは、 Generic Security Service API(GSS API)と、それをJavaに実装する方法について説明します。 JavaのGSSAPIを使用してネットワーク通信を保護する方法を説明します。

その過程で、単純なクライアントコンポーネントとサーバーコンポーネントを作成し、GSSAPIで保護します。

2. GSS APIとは何ですか?

では、Generic Security Service APIとは実際には何ですか? GSS APIは、アプリケーションがKerberos 、NTLM、SPNEGOなどのさまざまなセキュリティメカニズムをプラグイン可能な方法で使用するための汎用フレームワークを提供します。 その結果、アプリケーションがセキュリティメカニズムから直接切り離すのに役立ちます。

明確にするために、ここでのセキュリティは認証、データの整合性、および機密性に及びます。

2.1. GSS APIが必要なのはなぜですか?

Kerberos、NTLM、Digest-MD5などのセキュリティメカニズムは、機能と実装がまったく異なります。 通常、これらのメカニズムの1つをサポートするアプリケーションでは、別のメカニズムに切り替えるのが非常に困難です。

これは、GSSAPIのような汎用フレームワークがアプリケーションに抽象化を提供する場所です。 したがって、GSS APIを使用するアプリケーションは、適切なセキュリティメカニズムをネゴシエートし、それを通信に使用できます。 メカニズム固有の詳細を実際に実装する必要はありません。

2.2. GSS APIはどのように機能しますか?

GSS APIは、トークンベースのメカニズムです。 ピア間のセキュリティトークンの交換によって機能します。 この交換は通常、ネットワークを介して行われますが、GSSAPIはこれらの詳細に依存しません。

これらのトークンは、GSSAPIの特定の実装によって生成および処理されます。 これらのトークンの構文とセマンティクスは、ピア間でネゴシエートされるセキュリティメカニズムに固有です。

GSS APIの中心的なテーマは、セキュリティコンテキストを中心に展開しています。 トークンの交換を通じて、ピア間でこのコンテキストを確立できます。 コンテキストを確立するために、ピア間でトークンを複数回交換する必要がある場合があります。

両端で正常に確立されると、セキュリティコンテキストを使用してデータを安全に交換できます。 これには、基盤となるセキュリティメカニズムに応じて、データの整合性チェックとデータの暗号化が含まれる場合があります。

3. JavaでのGSSAPIサポート

Javaは、パッケージ「org.ietf.jgss」の一部としてGSSAPIをサポートしています。 パッケージ名は奇妙に見えるかもしれません。 これは、GSSAPIのJavaバインディングがIETF仕様で定義されているためです。 仕様自体はセキュリティメカニズムから独立しています。

Java GSSの一般的なセキュリティメカニズムの1つは、Kerberosv5です。

3.1. Java GSS API

JavaGSSを構築するコアAPIのいくつかを理解してみましょう。

  • GSSContext は、GSS APIセキュリティコンテキストをカプセル化し、そのコンテキストで利用可能なサービスを提供します
  • GSSCredential は、セキュリティコンテキストを確立するために必要なエンティティのGSSAPIクレデンシャルをカプセル化します
  • GSSName は、基盤となるメカニズムで使用されるさまざまな名前空間の抽象化を提供するGSSAPIプリンシパルエンティティをカプセル化します

上記のインターフェースとは別に、注意すべき重要なクラスがいくつかあります。

  • GSSManager は、 GSSName GSSCredential GSSContextなどの他の重要なGSSAPIクラスのファクトリクラスとして機能します。
  • Oid は、メカニズムと名前の形式を識別するためにGSS API内で使用される階層識別子であるユニバーサルオブジェクト識別子(OID)を表します
  • MessageProp は、保護の品質(QoP)やデータ交換の機密性などのGSSContextを示すためにプロパティをラップします
  • ChannelBinding は、ピアエンティティ認証が提供される品質を強化するために使用されるオプションのチャネルバインディング情報をカプセル化します

3.2. JavaGSSセキュリティプロバイダー

Java GSSは、JavaでGSS APIを実装するためのコアフレームワークを定義しますが、実装を提供しません。 Javaは、JavaGSSを含むセキュリティサービスプロバイダーベースのプラグ可能な実装を採用しています。

Java Cryptography Architecture (JCA)に登録されているそのようなセキュリティプロバイダーが1つ以上存在する可能性があります。 各セキュリティプロバイダーは、JavaGSSAPIやその下のセキュリティメカニズムなどの1つ以上のセキュリティサービスを実装できます。

JDKに同梱されているデフォルトのGSSプロバイダーがあります。 ただし、使用できるセキュリティメカニズムが異なるベンダー固有のGSSプロバイダーは他にもあります。 そのようなプロバイダーの1つは、 IBM JavaGSSです。 そのようなセキュリティプロバイダーを使用するには、JCAに登録する必要があります。

さらに、必要に応じて、独自のセキュリティプロバイダーを、場合によってはカスタムセキュリティメカニズムで実装できます。 ただし、これは実際にはほとんど必要ありません。

4. 例によるGSSAPI

ここで、例を通してJavaGSSの動作を確認します。 簡単なクライアントおよびサーバーアプリケーションを作成します。 GSSでは、クライアントはより一般的にイニシエーターと呼ばれ、サーバーはアクセプターと呼ばれます。 認証には、その下にあるJavaGSSとKerberosv5を使用します。

4.1. クライアントとサーバーのGSSコンテキスト

まず、アプリケーションのサーバー側とクライアント側の両方でGSSContextを確立する必要があります

まず、クライアント側でこれを行う方法を見てみましょう。

GSSManager manager = GSSManager.getInstance();
String serverPrinciple = "HTTP/[email protected]";
GSSName serverName = manager.createName(serverPrinciple, null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
GSSContext clientContext = manager.createContext(
  serverName, krb5Oid, (GSSCredential)null, GSSContext.DEFAULT_LIFETIME);
clientContext.requestMutualAuth(true);
clientContext.requestConf(true);
clientContext.requestInteg(true);

ここではかなり多くのことが起こっています。それらを分解してみましょう。

  • GSSManagerのインスタンスを作成することから始めます
  • 次に、このインスタンスを使用して GSSContext を作成し、以下を渡します。
    • サーバープリンシパルを表すGSSName、ここでKerberos固有のプリンシパル名に注意してください
    • 使用するメカニズムのOid、ここではKerberosv5
    • イニシエーターのクレデンシャル、ここでは null は、デフォルトのクレデンシャルが使用されることを意味します
    • 確立されたコンテキストの存続期間
  • 最後に、相互認証、機密性、およびデータ整合性のコンテキストを準備します

同様に、サーバー側のコンテキストを定義する必要があります。

GSSManager manager = GSSManager.getInstance();
GSSContext serverContext = manager.createContext((GSSCredential) null);

ご覧のとおり、これはクライアント側のコンテキストよりもはるかに単純です。 ここでの唯一の違いは、nullとして使用したアクセプターの資格情報が必要なことです。 以前と同様に、 nullは、デフォルトの資格情報が使用されることを意味します。

4.2. GSSAPI認証

サーバー側とクライアント側のGSSContextを作成しましたが、この段階では確立されていないことに注意してください。

これらのコンテキストを確立するには、指定されたセキュリティメカニズム、つまりKerberosv5に固有のトークンを交換する必要があります。

// On the client-side
clientToken = clientContext.initSecContext(new byte[0], 0, 0);
sendToServer(clientToken); // This is supposed to be send over the network
		
// On the server-side
serverToken = serverContext.acceptSecContext(clientToken, 0, clientToken.length);
sendToClient(serverToken); // This is supposed to be send over the network
		
// Back on the client side
clientContext.initSecContext(serverToken, 0, serverToken.length);

これにより、最終的に両端でコンテキストが確立されます。

assertTrue(serverContext.isEstablished());
assertTrue(clientContext.isEstablished());

4.3. GSSAPIの安全な通信

これで、両端でコンテキストが確立されたので、整合性と機密性を備えたデータの送信を開始できます

// On the client-side
byte[] messageBytes = "Baeldung".getBytes();
MessageProp clientProp = new MessageProp(0, true);
byte[] clientToken = clientContext.wrap(messageBytes, 0, messageBytes.length, clientProp);
sendToClient(serverToken); // This is supposed to be send over the network
       
// On the server-side 
MessageProp serverProp = new MessageProp(0, false);
byte[] bytes = serverContext.unwrap(clientToken, 0, clientToken.length, serverProp);
String string = new String(bytes);
assertEquals("Baeldung", string);

ここで起こっていることがいくつかあります。分析してみましょう。

  • MessageProp は、クライアントが wrapメソッドを設定し、トークンを生成するために使用されます。
  • メソッドwrapは、データの暗号化MICも追加します。MICはトークンの一部としてバンドルされています
  • そのトークンはサーバーに送信されます(おそらくネットワーク呼び出しを介して)
  • サーバーはMessagePropを再度利用して、 unwrapメソッドを設定し、データを取得します
  • また、メソッド unwrap は、受信したデータのMICを検証し、データの整合性を保証します。

したがって、クライアントとサーバーは、整合性と機密性を備えたデータを交換できます。

4.4. 例のKerberosセットアップ

現在、 KerberosのようなGSSメカニズムは、通常、既存のサブジェクトから資格情報をフェッチすることが期待されています。 ここでのクラスSubjectは、人やサービスなどのエンティティを表すJAAS抽象化です。 これは通常、JAASベースの認証中に入力されます。

ただし、この例では、JAASベースの認証を直接使用しません。 この場合、keytabファイルを使用して、Kerberosに直接資格情報を取得させます。 これを実現するためのJVMシステムパラメータがあります。

-Djavax.security.auth.useSubjectCredsOnly=false

ただし、Sun Microsystemが提供するデフォルトのKerberos実装は、認証を提供するためにJAASに依存しています。

これは、先ほど説明した内容とは逆に聞こえるかもしれません。 Subjectにデータを入力するアプリケーションでJAASを明示的に使用できることに注意してください。 または、基礎となるメカニズムに任せて直接認証し、とにかくJAASを使用します。 したがって、基礎となるメカニズムにJAAS構成ファイルを提供する必要があります。

com.sun.security.jgss.initiate  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  principal="client/localhost"
  storeKey=true;
};
com.sun.security.jgss.accept  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  storeKey=true
  principal="HTTP/localhost";
};

この構成は単純で、Kerberosをイニシエーターとアクセプターの両方に必要なログインモジュールとして定義しています。 さらに、keytabファイルのそれぞれのプリンシパルを使用するように構成しました。 このJAAS構成をシステムパラメータとしてJVMに渡すことができます。

-Djava.security.auth.login.config=login.conf

ここでは、KerberosKDCにアクセスできることを前提としています。 KDCでは、必要なプリンシパルを設定し、使用するキータブファイルを取得しました。たとえば、「example.keytab」。

さらに、適切なKDCを指すKerberos構成ファイルが必要です。

[libdefaults]
default_realm = EXAMPLE.COM
udp_preference_limit = 1
[realms]
EXAMPLE.COM = {
    kdc = localhost:52135
}

この単純な構成では、デフォルトのレルムをEXAMPLE.COMとしてポート52135で実行されるKDCを定義します。 これをシステムパラメータとしてJVMに渡すことができます。

-Djava.security.krb5.conf=krb5.conf

4.5. 例の実行

この例を実行するには、前のセクションで説明したKerberosアーティファクトを利用する必要があります。

また、必要なJVMパラメーターを渡す必要があります。

java -Djava.security.krb5.conf=krb5.conf \
  -Djavax.security.auth.useSubjectCredsOnly=false \
  -Djava.security.auth.login.config=login.conf \
  com.baeldung.jgss.JgssUnitTest

これは、KerberosがkeytabおよびGSSからの資格情報を使用して認証を実行し、コンテキストを確立するのに十分です。

5. 実世界のGSSAPI

GSS APIは、プラグ可能なメカニズムを通じて多くのセキュリティ問題を解決することを約束していますが、より広く採用されているユースケースはほとんどありません。

  • これはセキュリティメカニズムとしてSASLで広く使用されています。特にKerberosが基本的なメカニズムとして選択されている場合はそうです。 Kerberosは、特にエンタープライズネットワーク内で広く使用されている認証メカニズムです。 Kerberisedインフラストラクチャを活用して、新しいアプリケーションを認証することは非常に便利です。 したがって、GSSAPIはそのギャップをうまく埋めます。
  • また、 SPNEGO と組み合わせて使用され、事前に不明なセキュリティメカニズムをネゴシエートします。 この点で、SPNEGOはある意味でGSSAPIの疑似メカニズムです。 これは、最新のすべてのブラウザーで広くサポートされており、Kerberosベースの認証を利用できるようになっています。

6. 比較におけるGSSAPI

GSS APIは、プラグイン可能な方法でアプリケーションにセキュリティサービスを提供するのに非常に効果的です。 ただし、Javaでこれを実現するための選択肢はこれだけではありません。

Javaが他に何を提供しなければならないか、そしてそれらがGSSAPIとどのように比較されるかを理解しましょう。

  • Java Secure Socket Extension (JSSE): JSSEは、Java用のSecureSockets Layer(SSL)を実装するJavaのパッケージのセットです。 データの暗号化、クライアントとサーバーの認証、およびメッセージの整合性を提供します。 GSS APIとは異なり、JSSEは公開鍵インフラストラクチャ(PKI)に依存して機能します。 したがって、GSS APIは、JSSEよりも柔軟で軽量であることがわかります。
  • Java Simple Authentication and Security Layer (SASL): SASLは、インターネットプロトコルの認証とデータセキュリティのためのフレームワークであり、特定の認証メカニズムからそれらを切り離します。 これは、範囲がGSSAPIに似ています。 ただし、Java GSSは、利用可能なセキュリティプロバイダーを介した基盤となるセキュリティメカニズムのサポートが制限されています。

全体として、GSS APIは、メカニズムに依存しない方法でセキュリティサービスを提供する上で非常に強力です。 ただし、Javaでより多くのセキュリティメカニズムがサポートされると、これがさらに採用されるようになります。

7. 結論

要約すると、このチュートリアルでは、セキュリティフレームワークとしてのGSSAPIの基本を理解しました。 GSS用のJavaAPIを調べ、それらを活用する方法を理解しました。 その過程で、相互認証を実行し、データを安全に交換する単純なクライアントおよびサーバーコンポーネントを作成しました。

さらに、GSS APIの実用的なアプリケーションと、Javaで利用可能な代替手段についても説明しました。

いつものように、コードはGitHubにあります。