Java GSS APIのガイド

  • Java

  • link:/category/security-2/ [セキュリティ]

1. 概要

このチュートリアルでは、https://www.ietf.org/rfc/rfc2743.txt [Generic Security Service API(GSS API)]とJavaでの実装方法を理解します。 JavaのGSS APIを使用して、ネットワーク通信を保護する方法について説明します。
このプロセスでは、単純なクライアントおよびサーバーコンポーネントを作成し、GSS APIでそれらを保護します。

2. GSS APIとは

では、Generic Security Service APIとは実際には何ですか? * GSS APIは、アプリケーションがKerberos *、NTLM、SPNEGOなどのさまざまなセキュリティメカニズムをプラグ可能な方法で使用するための汎用フレームワークを提供します。 したがって、アプリケーションがセキュリティメカニズムからアプリケーションを直接切り離すのに役立ちます。
明確にするために、ここでのセキュリティは、*認証、データの整合性、および機密性*に及びます。

2.1. GSS APIが必要な理由

Kerberos、NTLM、Digest-MD5などのセキュリティメカニズムは、機能と実装がまったく異なります。 通常、これらのメカニズムのいずれかをサポートするアプリケーションでは、別のメカニズムに切り替えるのは非常に困難です。
これは、GSS APIのような汎用フレームワークがアプリケーションに抽象化を提供する場所です*。 したがって、GSS APIを使用するアプリケーションは、適切なセキュリティメカニズムをネゴシエートし、それを通信に使用できます。 メカニズム固有の詳細を実際に実装する必要のないすべてのこと。

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

GSS APIは*トークンベースのメカニズム*です。 *ピア間のセキュリティトークンの交換*によって機能します。 通常、この交換はネットワークを介して行われますが、GSS APIはこれらの詳細には依存しません。
これらのトークンは、GSS APIの特定の実装によって生成および処理されます。 これらのトークンの*構文とセマンティクスは、ピア間でネゴシエートされるセキュリティメカニズム*に固有です。
link:/uploads/Screenshot-2019-08-12-at-12.08.43-100x79.png%20100w []
GSS APIの中心テーマは、セキュリティコンテキストを中心に展開します。 トークンの交換により、ピア間でこのコンテキストを確立できます。 コンテキストを確立するには、ピア間でトークンを複数回交換する必要がある場合があります。
両端で正常に確立されると、セキュリティコンテキストを使用してデータを安全に交換できます。 これには、基礎となるセキュリティメカニズムに応じて、データの整合性チェックとデータの暗号化が含まれます。

3. JavaでのGSS APIサポート

Javaは、パッケージorg.ietf.jgssの一部としてGSS APIをサポートしています。 パッケージ名は独特のように見えるかもしれません。 これは、GSS APIの* Javaバインディングがhttps://www.ietf.org/rfc/rfc2853.txt[IETF仕様] *で定義されているためです。 仕様自体は、セキュリティメカニズムとは無関係です。
Java GSSの一般的なセキュリティメカニズムの1つはKerberos v5です。

3.1. Java GSS API

Java GSSを構築するいくつかのコアAPIを理解してみましょう。
  • _GSSContext_はGSS APIセキュリティコンテキストをカプセル化し、提供します
    コンテキストで利用可能なサービス

  • _GSSCredential_は、エンティティのGSS API資格情報をカプセル化します
    セキュリティコンテキストを確立するために必要です

  • _GSSName_は、GSS APIプリンシパルエンティティをカプセル化します。
    基礎となるメカニズムによって使用される異なる名前空間の抽象化

    上記のインターフェースとは別に、注意すべき重要なクラスは他にほとんどありません。
  • GSSManager_は、他の重要なGSS APIのファクトリクラスとして機能します
    _GSSName _、
    GSSCredential GSSContext_などのクラス

  • _Oid_は、Universal Object Identifiers(OID)を表します。
    GSS API内でメカニズムと名前の形式を識別するために使用される階層識別子

  • _MessageProp_は、プロパティをラップして、GSSContextなどを示します
    データ交換の保護品質(QoP)と機密性

  • _ChannelBinding_は、オプションのチャネルバインディング情報をカプセル化します
    ピアエンティティ認証が提供される品質を強化するために使用されます

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

Java GSSは、GSS APIをJavaで実装するためのコアフレームワークを定義しますが、実装を提供しません。 Javaは、Java GSSを含むセキュリティサービスに* _Provider_ベースのプラグ可能な実装を採用しています。
Java Cryptography Architecture *(JCA)に登録されている1つ以上の*セキュリティプロバイダーが存在する場合があります。 各セキュリティプロバイダーは、Java GSSAPIやその下のセキュリティメカニズムなど、1つ以上のセキュリティサービスを実装できます。
JDKに同梱されているデフォルトのGSSプロバイダーがあります。 ただし、使用できるセキュリティメカニズムが異なるベンダー固有のGSSプロバイダーが他にもあります。 そのようなプロバイダーの1つはhttps://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_74/rzaha/rzahajgssover.htm[IBM Java GSS]です。 そのようなセキュリティプロバイダーを使用するには、JCAに登録する必要があります。
さらに、必要に応じて、カスタムセキュリティメカニズムを備えた独自のセキュリティプロバイダを実装できます*。 ただし、これは実際にはほとんど必要ありません。

4. 例によるGSS API

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

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_、ここではKerberos v5

  • イニシエーターの資格情報、ここで_null_はそのデフォルトを意味します
    資格情報が使用されます

  • 確立されたコンテキストのライフタイム

  • 最後に、相互認証のコンテキストを準備します。
    機密性とデータの整合性*

    同様に、サーバー側のコンテキストを定義する必要があります。
GSSManager manager = GSSManager.getInstance();
GSSContext serverContext = manager.createContext((GSSCredential) null);
ご覧のとおり、これはクライアント側のコンテキストよりもはるかに簡単です。 ここでの唯一の違いは、_null_として使用したアクセプターの資格情報が必要なことです。 前と同様に、* _ null_はデフォルトの資格情報が使用されることを意味します。*

4.2. GSS API認証

サーバーとクライアント側の_GSSContext_を作成しましたが、これらはこの段階では確立されていないことに注意してください。
これらのコンテキストを確立するには、指定されたセキュリティメカニズム、つまりKerberos v5に固有のトークンを交換する必要があります。
// 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. GSS APIセキュア通信

両端でコンテキストが確立されたので、*整合性と機密性を備えたデータの送信を開始できます*:
// 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_ *から資格情報を取得することが期待されています。 ここの_Subject_クラスは、人やサービスのようなエンティティを表すJAAS抽象化です。 これは通常、JAASベースの認証中に入力されます。
ただし、この例では、JAASベースの認証を直接使用しません。 キータブファイルを使用して、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
ここでは、Kerberos KDCにアクセスできることを前提としています。 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. 現実世界のGSS API

GSS APIは、プラガブルなメカニズムを通じて多くのセキュリティ問題を解決することを約束しますが、より広く採用されているユースケースはほとんどありません。
  • 特に、セキュリティメカニズムとしてSASLで広く使用されています。特に
    Kerberosは選択の基礎となるメカニズムです。 Kerberosは、特にエンタープライズネットワーク内で広く使用されている認証メカニズムです。 Kerberosインフラストラクチャを活用して新しいアプリケーションを認証すると、非常に便利です。 したがって、GSS APIはそのギャップをうまく埋めます。

  • また、セキュリティをネゴシエートするために、* SPNEGOと結合して使用されます*
    事前に知られていない場合のメカニズム。 この点で、SPNEGOはある意味でGSS APIの疑似メカニズムです。 これは、すべての最新のブラウザーで広くサポートされており、Kerberosベースの認証を活用できます。

6. 比較のGSS API

GSS APIは、プラグ可能な方法でアプリケーションにセキュリティサービスを提供するのに非常に効果的です。 ただし、Javaでこれを達成する唯一の選択肢ではありません。
Javaが提供する他の機能と、GSS APIと比較する方法を理解しましょう。
  • Java
    Secure Socket Extension
    (JSSE):* JSSEは、Java *用のSecure Sockets Layer(SSL)を実装するJavaのパッケージのセットです。 データの暗号化、クライアントとサーバーの認証、およびメッセージの整合性を提供します。 GSS APIとは異なり、JSSEは公開鍵インフラストラクチャ(PKI)に依存して動作します。 したがって、GSS APIはJSSEよりも柔軟で軽量であることがわかります。

  • Java
    シンプル認証およびセキュリティレイヤー
    (SASL):* SASLは、インターネットプロトコルの認証およびデータセキュリティのフレームワーク*であり、特定の認証メカニズムからそれらを分離します。 これは、GSS APIと範囲が似ています。 ただし、Java GSSでは、利用可能なセキュリティプロバイダーを介した基本的なセキュリティメカニズムのサポートが制限されています。

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

7. 結論

要約すると、このチュートリアルでは、セキュリティフレームワークとしてのGSS APIの基本を理解しました。 GSSのJava APIを調べて、それらを活用する方法を理解しました。 その過程で、相互認証を実行し、データを安全に交換する単純なクライアントおよびサーバーコンポーネントを作成しました。
さらに、GSS APIの実用的なアプリケーションと、Javaで利用可能な代替手段も確認しました。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-security[GitHub上]にあります。