ここでは、セキュアな通信を実行するアプリケーションを構築する方法について説明します。Java SE 6 プラットフォームは、アプリケーションがセキュアな通信を実行できるようにする 3 つの標準 API を提供します。Java Generic Security Service (GSS)、Java SASL API、および Java Secure Socket Extension (JSSE) です。アプリケーションを構築する場合、これらの API のうちのどれを使用すればよいでしょうか。その答えは、プロトコルまたはサービスの要件、配備インフラストラクチャー、ほかのセキュリティーサービスとの統合など、数多くの要因によって異なります。たとえば、LDAP クライアントライブラリを構築する場合は、Java SASL API を使用する必要があります。SASL の使用は、LDAP のプロトコル定義の一部であるためです。別の例として、サービスが SSL をサポートする場合、サービスにアクセスしようとするクライアントアプリケーションは JSSE を使用する必要があります。
課題 3: Java Generic Security Service (GSS) API の使用
この課題の目標:
 この課題の目標は、Java GSS API を使用してセキュアな認証および通信を実行する方法について学習することです。 
この課題の内容:
Generic Security Service API は、認証、メッセージの整合性、メッセージの機密性などのさまざまなセキュリティーサービスにアクセスするための、統一された C 言語インタフェースを提供します。Java GSS API は、対応するインタフェースを Java アプリケーションに提供します。 この API を使用すると、アプリケーションは認証を実行してピアとのセキュアな通信を確立できます。GSS-API および Java GSS-API によってアクセスされるもっとも一般的なセキュリティーサービスの 1 つは、Kerberos です。
この課題のリソース:
- JAAS および Java GSS-API チュートリアル
- 「Generic Security Service API Version 2: Java Bindings」(RFC 2853)
- Java GSS javadoc: org.ietf.jgss。
この課題の概要:
この課題では、Java GSS API を使用してセキュアに通信する方法を示すクライアントサーバーアプリケーションを扱います。クライアント部分とサーバー部分は、課題 1 に示されているように、最初に Kerberos に対して認証します。これにより、資格がサブジェクトに格納されます。アプリケーションは、サブジェクトを使用して Subject.doAs の内部で、(基盤となる GSS メカニズムとして Kerberos を使用して) Java GSS 操作を実行するアクションを実行します。Java GSS Kerberos メカニズムは doAs の内部で実行されるため、Kerberos 資格をサブジェクトから取得し、それらを使用してピアとの認証およびメッセージのセキュアな交換を行います。
実行手順:
- 次のコードを参照してください。これは、src/GssServer.javaにあります。
 
 このコードは、サービスプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。このコードは、最初にGSSManagerのインスタンスを作成し (行 8)、それを使用して独自の資格を取得して (行 10 - 11)GSSContextのインスタンスを作成します (行 18)。このコンテキストを使用して認証を実行します (行 22 - 34 のループ)。認証が完了すると、暗号化された入力をクライアントから受け入れ、確立されたセキュリティーコンテキストを使用してデータを復号化します (行 45)。次に、セキュリティーコンテキストを使用して元の入力と日付を含む応答を暗号化し (行 49)、クライアントに返信します。
GssServer.java のコードリスト。
| 
 
static class GssServerAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create server socket for accepting connections    ServerSocket ss = new ServerSocket(localPort);
    // Get own Kerberos credentials for accepting connection    GSSManager manager = GSSManager.getInstance();    Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");    GSSCredential serverCreds = manager.createCredential(null,      GSSCredential.DEFAULT_LIFETIME, krb5Oid, GSSCredential.ACCEPT_ONLY);
    while (true) {      Socket socket = ss.accept();      DataInputStream inStream = new DataInputStream(socket.getInputStream());      DataOutputStream outStream = new DataOutputStream(socket.getOutputStream());
      GSSContext context = manager.createContext((GSSCredential)serverCreds);
      // Do the context establishment loop      byte[] token = null;      while (!context.isEstablished()) {        // Read token        token = new byte[inStream.readInt()];        inStream.readFully(token);
        // Process token        token = context.acceptSecContext(token, 0, token.length);
        // Send a token to the peer if one was generated by acceptSecContext        outStream.writeInt(token.length);        outStream.write(token);        outStream.flush();      }
      // Context established!
      // Create MessageProp for use with unwrap (will be set upon return from unwrap)      MessageProp prop = new MessageProp(0, false);
      // Read token from client      token = new byte[inStream.readInt()];      inStream.readFully(token);      // Unwrap (decrypt) token sent by client      byte[] input = context.unwrap(token, 0, token.length, prop);      ...      // Create new token and send to client      byte[] reply = ...;      token = context.wrap(reply, 0, reply.length, prop);
      outStream.writeInt(token.length);      outStream.write(token);      outStream.flush();      context.dispose();      socket.close();    }  }} 
 | 
- サンプルコードをコンパイルします。% javac GssServer.java
 
 
- 次のコードを参照してください。これは、src/GssClient.javaにあります。
 
 このコードは、クライアントプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。このコードは、最初にGSSManagerのインスタンスを作成し (行 10)、それを使用して通信対象のサービスのプリンシパル名を取得します (行 12)。次に、GSSContextのインスタンスを作成し (行 15、16)、サービスとの認証を実行します (行 22 - 33 のループ)。認証が完了すると、確立されたセキュリティーコンテキストを使用してメッセージを暗号化し (行 42)、サーバーに送信します。次に、暗号化されたメッセージをサーバーから読み取り、確立されたセキュリティーコンテキストを使用して復号化します (行 53)。
GssClient.java のコードリスト。
| 
 
static class GssClientAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create socket to server    Socket socket = new Socket(hostName, port);    DataInputStream inStream = new DataInputStream(socket.getInputStream());    DataOutputStream outStream = new DataOutputStream(socket.getOutputStream());
    // Get service's principal name    GSSManager manager = GSSManager.getInstance();    Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");    GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE);
    // Get the context for authentication    GSSContext context = manager.createContext(serverName, krb5Oid, null,       GSSContext.DEFAULT_LIFETIME);    context.requestMutualAuth(true); // Request mutual authentication    context.requestConf(true); // Request confidentiality
    // Do the context establishment loop    byte[] token = new byte[0];    while (!context.isEstablished()) {      token = context.initSecContext(token, 0, token.length);      outStream.writeInt(token.length);      outStream.write(token);      outStream.flush();
      // Check if we're done      if (!context.isEstablished()) {        token = new byte[inStream.readInt()];        inStream.readFully(token);      }    }
    // Context established!
    // Create MessageProp for use with unwrap (true means request confidentiality)    MessageProp prop = new MessageProp(0, true);
    // Create encrypted message and send to server    byte[] reply = ...;    token = context.wrap(reply, 0, reply.length, prop);
    outStream.writeInt(token.length);    outStream.write(token);    outStream.flush();
    // Read token from server    token = new byte[inStream.readInt()];    inStream.readFully(token);
    // Unwrap (decrypt) token sent by server    byte[] input = context.unwrap(token, 0, token.length, prop);    ...    context.dispose();    socket.close();    return null;  }} 
 | 
- サンプルコードをコンパイルします。
% javac GssClient.java
 
- 新規のウィンドウを起動してサーバーを起動します。
% xterm &
% java -Djava.security.auth.login.config=jaas-krb5.conf \
       GssServer
- クライアントアプリケーションを実行します。GssClient は、次の 2 つのパラメータを取ります。サービス名、およびそのサービスが実行されているサーバーの名前です。たとえば、サービスがマシン j1hol-001上で実行されているhostの場合は、次のように入力します。パスワードの入力を求められた場合は、changeit を入力します。
    
% java -Djava.security.auth.login.config=jaas-krb5.conf \
       GssClient host j1hol-001
- クライアントとサーバーのアプリケーションウィンドウに次の出力が表示されます。
GssServer の例を実行した場合の出力。
| 
 
Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]Waiting for incoming connections...Got connection from client /192.0.2.102Context Established!Client principal is test@J1LABS.EXAMPLE.COMServer principal is host/j1hol-001@J1LABS.EXAMPLE.COMMutual authentication took place!Received data "Hello There!" of length 12Confidentiality applied: trueSending: Hello There! Thu May 06 12:11:15 PDT 2005 
 | 
GssClient の例を実行した場合の出力。
| 
 
Kerberos password for test: changeitAuthenticated principal: [test@J1LABS.EXAMPLE.COM]Connected to address j1hol-001/192.0.2.102Context Established!Client principal is test@J1LABS.EXAMPLE.COMServer principal is host@j1hol-001Mutual authentication took place!Sending message: Hello There!Will read token of size 93Received message: Hello There! Thu May 06 12:11:15 PDT 2005 
 | 
サマリー:
この課題では、Java GSS API を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習しました。
次の手順
- 課題 4 に進み、Java SASL API を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習します。
- 課題 5 に進み、JSSE を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習します。
- 課題 6 に進み、Kerberos 環境でシングルサインオンを行う場合に使用したサンプルプログラムを構成する方法について学習します。
課題 4: Java SASL API の使用
この課題の目標:
この課題の目標は、Java SASL API を使用してセキュアな認証および通信を実行する方法について学習することです。 
この課題の内容:
SASL (Simple Authentication and Security Layer) は、チャレンジレスポンスプロトコルを指定します。このプロトコルでは、1 つには認証目的、もう 1 つには認証後通信を続行するセキュリティーレイヤーを確立する目的 (オプション) で、クライアントとサーバーがデータを交換します。SASL では異なるメカニズムを使用できます。このようなメカニズムはそれぞれ、交換されるデータと名前を定義するプロファイルによって識別されます。SASL は、LDAPv3 や IMAPv4 などの接続ベースのプロトコルとともに使用されます。SASL は、RFC 4422 で説明されています。
Java SASL API は、メカニズムに依存しない方法で SASL を使用するアプリケーション用の API を定義します。たとえば、SASL を使用するネットワークプロトコルのライブラリを記述する場合、Java SASL API を使用して、ピアと交換するデータを生成できます。ライブラリが配備されるときに、ライブラリで使用するメカニズムを動的に構成できます。
認証以外に、SASL を使用して認証後に使用されるセキュリティー層とのネゴシエーションを行うことができます。ただし、GSS-API とは異なり、セキュリティー層のプロパティー (整合性を求めるか機密性を求めるかなど) はネゴシエーション時に決定されます。GSS-API では、メッセージごとに機密性をオンまたはオフにすることができます。
この課題のリソース:
- Java SASL API プログラミングおよび配備ガイド
- Java SASL API javadoc
- Simple Authentication and Security Layer (SASL) (RFC 4422)
 
この課題の概要:
この課題では、Java SASL API を使用してセキュアに通信する方法を示すクライアントサーバーアプリケーションを扱います。クライアント部分とサーバー部分は、課題 1 を使用して最初に Kerberos に対して認証します。これにより、資格がサブジェクトに格納されます。アプリケーションは、サブジェクトを使用して Subject.doAs の内部で、(基盤となる SASL メカニズムとして Kerberos を使用して) Java SASL API 操作を実行するアクションを実行します。SASL/Kerberos メカニズムは、doAs の内部で実行されるため、Kerberos 資格をサブジェクトから取得し、それらを使用してピアとの認証およびメッセージのセキュアな交換を行います。
この例は、AppConnection クラスによって実装される単純なプロトコルを使用します。このプロトコルは、認証コマンドおよびデータコマンドを交換します。各コマンドは、タイプ (AppConnection.AUTH_CMD など)、後続のデータの長さ、およびデータ自体で構成されています。データが認証または暗号化された/整合性が保護されたアプリケーションデータ用の場合、そのデータは SASL バッファーです。それ以外の場合は、プレーンなアプリケーションデータです。
実行手順:
- 次のコードを参照してください。これは、src/SaslTestServer.javaにあります。
 
 このコードは、サービスプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。サーバーは、サポートする保護の品質を指定し (行 9)、SaslServer のインスタンスを作成して認証を実行します (行 21)。SASL のチャレンジ応答プロトコルは、while ループ (行 33 - 49) の中で実行されています。そこでサーバーは、チャレンジをクライアントに送信しクライアントからの応答を処理します。認証後、認証されたクライアントのアイデンティティーを getAuthorizedID() の呼び出しによって取得できます (行 61)。セキュリティー層がネゴシエーションされた場合、サーバーはクライアントとセキュアにデータを交換できます (行 66、70)。
SaslTestServer.java のコードリスト。
| 
 
static class SaslServerAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create server socket for accepting connections    ServerSocket ss = new ServerSocket(localPort);
    // Support all quality-of-protection options    HashMap<String,Object> props = new HashMap<String,Object>();    props.put(Sasl.QOP, "auth-conf,auth-int,auth");
    while (true) {      // Create application-level connection to handle request      Socket socket = ss.accept();      AppConnection conn = new AppConnection(socket);
      // Normally, the application protocol will negotiate which      // SASL mechanism to use. In this simplified example, we      // will always use "GSSAPI" (the name of the mechanism that does Kerberos via GSS-API)
      // Create SaslServer to perform authentication      SaslServer srv = Sasl.createSaslServer("GSSAPI", service, serviceName, props, cbh);
      if (srv == null) {        // ... handle error      }
      // Read the initial response from client      byte[] response = conn.receive(AppConnection.AUTH_CMD);      AppConnection.AppReply clientMsg;      boolean auth = false;
      // Perform authentication      while (!srv.isComplete()) {        try          // Generate challenge based on response          byte[] challenge = srv.evaluateResponse(response);          if (srv.isComplete()) {            conn.send(AppConnection.SUCCESS, challenge);            auth = true;          } else {            clientMsg = conn.send(AppConnection.AUTH_IN_PROGRESS, challenge);            response = clientMsg.getBytes();          }        } catch (SaslException e) {          // Send failure notification to client          conn.send(AppConnection.FAILURE, null);          break;        }      }
      // Authentication completed!
      // Check status of authentication      if (srv.isCompleted() && auth) {        System.out.println("authorized client is: " + srv.getAuthorizationID());      } else {        // Report failure ...      }
      // Find out whether security layer was negotiated      String qop = (String) srv.getNegotiatedProperty(Sasl.QOP);      boolean sl = (qop.equals("auth-conf") || qop.equals("auth-int"));
      // Read and decrypt message from client      byte[] msg = conn.receive(AppConnection.DATA_CMD);      byte[] realMsg = (sl ? srv.unwrap(msg, 0, msg.length) : msg);      ...      // Create and encrypt message to send to client      byte[] reply = ...;      byte[] realReply = (sl ? srv.wrap(reply, 0, reply.length) : reply);      conn.send(AppConnection.SUCCESS, realReply);    }  }} 
 | 
- サンプルコードをコンパイルします。
% javac SaslTestServer.java
 
 
- 次のコードを参照してください。これは、src/SaslTestClient.javaにあります。このコードは、クライアントプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。プログラムは、必要な保護の品質 (この場合は、機密性) を最初に指定し (行 8)、次に SaslClient のインスタンスを作成して認証に使用します (行 11 - 12)。次に、メカニズムに初期応答があるかどうかをチェックし、ある場合は、空のバイト配列で evaluateChallenge() を呼び出すことによって応答を取得します (行 20)。次に、応答をサーバーに送信して認証を開始します。SASL のチャレンジ応答プロトコルは、while ループ (行 24 - 39) の中で実行されています。そこでクライアントは、サーバーから取得したチャレンジを評価してチャレンジに対応する応答をサーバーに送信します。認証後、クライアントは、ネゴシエーションされたセキュリティー層を使用してサーバーとの通信に進むことができます (行 48、55)。
SaslTestClient.java のコードリスト。
| 
 
static class SaslClientAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create application-level connection    AppConnection conn = new AppConnection(serverName, port);
    HashMap<String,Object> props = new HashMap<String,Object>();    props.put(Sasl.QOP, "auth-conf"); // Request confidentiality
    // Create SaslClient to perform authentication    SaslClient clnt = Sasl.createSaslClient(      new String[]{"GSSAPI"}, null, service, serverName, props, cbh);    if (clnt == null) {      // ... handle error    }
    byte[] challenge;
    // Get initial response for authentication    byte[] response = clnt.hasInitialResponse() ? clnt.evaluateChallenge(EMPTY) : EMPTY;    AppConnection.AppReply reply = conn.send(AppConnection.AUTH_CMD, response);
    // Repeat until authentication terminates    while (!clnt.isComplete() &&      (reply.getStatus() == AppConnection.AUTH_INPROGRESS ||       reply.getStatus() == AppConnection.SUCCESS))      // Evaluate challenge to generate response      challenge = reply.getBytes()      response = clnt.evaluateChallenge(challenge)
      if (reply.getStatus() == AppConnection.SUCCESS) {        if (response != null) {          throw new Exception("Protocol error")        }        break;      }      // Send response to server and read server's next challenge      reply = conn.send(AppConnection.AUTH_CMD, response);    }    // Authentication completed!    // Find out whether security layer was negotiated    String qop = (String) srv.getNegotiatedProperty(Sasl.QOP);    boolean sl = (qop.equals("auth-conf") || qop.equals("auth-int"));
    byte[] msg = ...;
    // Create and send encrypted data to server    byte[] encrypted = (sl ? clnt.wrap(msg, 0, msg.length) : msg);    reply = conn.send(AppConnection.DATA_CMD, encrypted);) {
    ...
    // Read and decrypt data from server    byte[] encryptedReply = reply.getBytes();    byte[] clearReply = (sl ? clnt.unwrap(encryptedReply, 0, encryptedReply.length)      : encryptedReply);    conn.close();    return null;  }} 
 | 
- サンプルコードをコンパイルします。
% javac SaslTestClient.java
 
 
- 新規のウィンドウを起動してサーバーを起動します。SaslTestServer は、次の 2 つのパラメータを取ります。サービス名、およびそのサービスが実行されているサーバーの名前です。たとえば、サービスがマシン j1hol-001上で実行されているhostの場合は、次のように入力します。
% xterm &
 % java
 -Djava.security.auth.login.config=jaas-krb5.conf \
 SaslTestServer host j1hol-001
 
 
 
- クライアントアプリケーションを実行します。SaslTestClient は、次の 2 つのパラメータを取ります。サービス名、およびそのサービスが実行されているサーバーの名前です。たとえば、サービスがマシン j1hol-001上で実行されているhostの場合は、次のように入力します。パスワードの入力を求められた場合は、changeit を入力します。
 % java -Djava.security.auth.login.config=jaas-krb5.conf \
 SaslTestClient host j1hol-001
 
- クライアントとサーバーのアプリケーションウィンドウに次の出力が表示されます。
SaslTestServer の例を実行した場合の出力。
| 
 
Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]Waiting for incoming connections...Got connection from client /192.0.2.102Client authenticated; authorized client is: test@J1LABS.EXAMPLE.COMNegotiated QOP: auth-confReceived: Hello There!Sending: Hello There! Fri May 07 15:32:37 PDT 2005Received data "Hello There!" of length 12 
 | 
SaslTestClient の例を実行した場合の出力。
| 
 
Kerberos password for test: changeitAuthenticated principal: [test@J1LABS.EXAMPLE.COM]Connected to address j1hol-001/192.0.2.102Client authenticated.Negotiated QOP: auth-confSending: Hello There!Received: Hello There! Fri May 07 15:32:37 PDT 2005 
 | 
- 異なる保護の品質を使用してプログラムを試行するには、SaslTestClientの行 8 を変更します。たとえば、行 8 を次の行で置き換えて、整合性保護を使用します (機密性なし)。
props.put(Sasl.QOP, "auth-int");
 
サマリー:
この課題では、Java SASL API を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習しました。
次の手順
- 課題 5 に進み、JSSE を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習します。
- 課題 6 に進み、Kerberos 環境でシングルサインオンを行う場合に使用したサンプルプログラムを構成する方法について学習します。
課題 5: Kerberos での Java Secure Socket Extension の使用
この課題の目標:
この課題の目標は、JSSE API を使用して、Kerberos 暗号化方式群を使用したセキュアな認証および通信を実行する方法について学習することです。 
この課題の内容:
Secure Socket Layer (SSL) と Transport Layer Security (TLS) は、インターネットで暗号化を実装する場合にもっともよく使用されるプロトコルです。TLS は、SSL から発展したインターネット標準です。SSL/TLS は、アプリケーションレベルのプロトコル (HTTP や LDAP など) にセキュアな認証と通信を提供します。たとえば、HTTPS は SSL/TLS を介して HTTP を使用した結果のプロトコルです。SSL/TLS は、HTTP などの標準プロトコルに対して使用されるだけでなく、セキュアに通信する必要がある、カスタムプロトコルを使用するカスタムアプリケーションを構築する場合にも広く使用されます。
SSL/TLS は、従来は証明書ベースの認証を使用しており、一般にサーバー認証に使用されます。たとえば、ブラウザなどの Web クライアントがユーザーに代わってセキュアな Web サイト (サーバー) にアクセスすると、サーバーは、ブラウザがサーバーのアイデンティティーを検証できるようにサーバーの証明書をブラウザに送信します。これにより、ユーザーが機密情報 (クレジットカード情報など) を偽のサーバーに公開することはありません。最近、新しい標準によって、TLS で Kerberos を使用できるようになりました。つまり、証明書ベースの認証を使用する代わりに、アプリケーションは Kerberos 資格を使用して、配備環境で Kerberos インフラストラクチャーを利用できます。Kerberos 暗号化方式群を使用すると、サーバーだけでなくクライアントも認証される相互認証が自動的にサポートされます。
特定のアプリケーションで Java GSS、Java SASL、または JSSE を使用するかどうかの決定は、複数の要因に依存する場合がよくあります。アプリケーションが相互に作用するサービス (によって使用されているプロトコル)、配備環境 (PKI または Kerberos ベース)、アプリケーションのセキュリティー要件などです。JSSE は、I/O およびトランスポートを行うセキュアな終端間チャネルを提供します。一方、Java GSS および Java SASL は、データの暗号化および整合性保護を提供しますが、セキュリティー保護されたデータをピアにトランスポートするのはアプリケーションです。JSSE と Java GSS をいつ使用するかを決定する要因の詳細の一部が、ドキュメント「Java GSS-API および JSSE をいつ使用するか」に示されています。
この課題のリソース:
- Java Secure Socket Extension (JSSE) リファレンスガイド
- JSSE javadoc: javax.net および javax.net.ssl
- The SSL Protocol version 3.0
- The TLS Protocol Version 1.0 (RFC 2246)
- Addition of Kerberos Cipher Suites to Transport Layer Security TLS (RFC 2712)
- Java GSS および JSSE をいつ使用するか
この課題の概要:
この課題では、JSSE および Kerberos 暗号化方式群を使用してセキュアに通信する方法を示すクライアントサーバーアプリケーションを扱います。クライアント部分とサーバー部分は、課題 1 を使用して最初に Kerberos に対して認証します。これにより、資格がサブジェクトに格納されます。アプリケーションは、サブジェクトを使用して Subject.doAs の内部で、(Kerberos 暗号化方式群を使用して) JSSE 操作を実行するアクションを実行します。Kerberos 暗号化方式群の実装は doAs の内部で実行されるため、Kerberos 資格をサブジェクトから取得し、それらを使用してピアとの認証およびメッセージのセキュアな交換を行います。この例では、改行で終了されるメッセージ (ネゴシエーションが行われた暗号化方式群を使用して暗号化され、整合性が保護される) を、クライアントとサーバー間で送受信します。
標準 (RFC 2712) に従って、Kerberos が有効なすべての TLS アプリケーションは、同じサービス名、つまり「host」を使用します。このため、この課題では、Kerberos サービス名を明示的に指定する必要はありません。
実行手順:
- 次のコードを参照してください。これは、src/JsseServer.javaにあります。
 
 このコードは、サービスプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。サーバーは最初に、SSLServerSocket を作成します (行 5 - 8)。これは、SSLServerSocket が必要に応じて自動的な認証、暗号化、および復号化を提供する点を除いて、プレーンな ServerSocket を作成するアプリケーションと似ています。次に、サーバーは使用する暗号化方式群を設定します (行 11 - 12)。次に、サーバーはループで実行され、SSL クライアントからの接続を受け入れ (行 17)、SSL ソケットから読み書きします (行 23、28)。サーバーは、getLocalPrincipal() および getPeerPrincipal() メソッドを呼び出すことによって (行 32 - 33)、ソケットの所有者のアイデンティティーを調べることができます。
JsseServer.java のコードリスト。
| 
 
static class JsseServerAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create TLS socket for accepting connections    SSLServerSocketFactory sslssf =      (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();    SSLServerSocket sslServerSocket =      (SSLServerSocket) sslssf.createServerSocket(localPort);
    // Enable only a Kerberos cipher suite    String enabledSuites[] = { "TLS_KRB5_WITH_3DES_EDE_CBC_SHA" };    sslServerSocket.setEnabledCipherSuites(enabledSuites);    // Should handle exception if enabledSuites is not supported
    while (true) {      // Create socket to handle request      SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();      BufferedReader in = new BufferedReader(new InputStreamReader(        sslSocket.getInputStream()));      BufferedWriter out = new BufferedWriter(new OutputStreamWriter(        sslSocket.getOutputStream()));
      String inStr = in.readLine();      // ... use inStr
      // Compose and send reply      String outStr = inStr + " " + new Date().toString() + "\n";      out.write(outStr);      out.flush();
      // Get names of principal at both ends of secure connection      Principal self = sslSocket.getSession().getLocalPrincipal();      Principal peer = sslSocket.getSession().getPeerPrincipal();
      sslSocket.close();    }  }} 
 | 
- サンプルコードをコンパイルします。
 % javac JsseServer.java
 
- 次のコードを参照してください。これは src/JsseClient.javaにあります。このコードは、クライアントプリンシパルが KDC に対して認証したあとに実行するアクションを定義します。このコードによって、課題 1 の行 11 の MyAction が置き換えられます。強調表示されている行に注目してください。クライアントは最初に、SSLSocket を作成します。次に、クライアントは使用する暗号化方式群を設定します (行 11 - 12)。次に、クライアントは、SSLSocket を使用してソケットの入力/出力ストリームを読み書きすることによって、サーバーとメッセージを交換します。クライアントは、getLocalPrincipal() および getPeerPrincipal() メソッドを呼び出すことによって (行 26 - 27)、ソケットの所有者のアイデンティティーを調べることができます。
JsseClient.java のコードリスト
| 
 
static class JsseClientAction implements PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create SSL connection    SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();    SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port);
    // Enable only a Kerberos cipher suite    String enabledSuites[] = { "TLS_KRB5_WITH_3DES_EDE_CBC_SHA" };    sslSocket.setEnabledCipherSuites(enabledSuites);    // Should handle exception if enabledSuites is not supported
    BufferedReader in = new BufferedReader(new InputStreamReader(      sslSocket.getInputStream()));    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(      sslSocket.getOutputStream()));
    String outStr = ...;    out.write(outStr);    out.flush();
    String inStr = in.readLine();    // ... use inStr
    // Get names of principal at both ends of secure connection    Principal self = sslSocket.getSession().getLocalPrincipal();    Principal peer = sslSocket.getSession().getPeerPrincipal();
    sslSocket.close();    return null;  }} 
 | 
- サンプルコードをコンパイルします。
% javac JsseClient.java
 
 
- 新規のウィンドウを起動してサーバーを起動します。JsseServer は、パラメータを 1 つ取ります。JSSE サービスが実行されているサーバーの名前です。たとえば、マシン j1hol-001上で実行されている場合は、次のように入力します。
% xterm &
 % java
 -Djava.security.auth.login.config=jaas-krb5.conf \
 JsseServer j1hol-001
 
 
 
- クライアントアプリケーションを実行します。JsseClient は、パラメータを 1 つ取ります。JSSE サービスが実行されているサーバーの名前です。たとえば、サービスがマシン j1hol-001上で実行されている場合は、次のように入力します。パスワードの入力を求められた場合は、changeit を入力します。
 % java -Djava.security.auth.login.config=jaas-krb5.conf \
 JsseClient j1hol-001
 
- クライアントとサーバーのアプリケーションウィンドウに次の出力が表示されます。
JsseServer の例を実行した場合の出力。
| 
 
Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]Waiting for incoming connections...Got connection from client /192.0.2.102Received: Hello There!Sending: Hello There! Fri May 07 15:32:37 PDT 2005Cipher suite in use: TLS_KRB5_WITH_3DES_EDE_CBC_SHAI am: host/j1hol-001@J1LABS.EXAMPLE.COMClient is: test@J1LABS.EXAMPLE.COM 
 | 
JsseClient の例を実行した場合の出力。
| 
 
Kerberos password for test: changeitAuthenticated principal: [test@J1LABS.EXAMPLE.COM]Sending: Hello There!Received: Hello There! Fri May 07 15:32:37 PDT 2005Cipher suite in use: TLS_KRB5_WITH_3DES_EDE_CBC_SHAI am: test@J1LABS.EXAMPLE.COMServer is: host/j1hol-001@J1LABS.EXAMPLE.COM 
 | 
サマリー:
この課題では、基盤となる認証システムとして Kerberos を使用し、JSSE を使用してお互いセキュアに認証および通信を行うクライアント/サーバーアプリケーションを記述する方法について学習しました。
次の手順
- 課題 6 に進み、課題 3、4、および 5 のサンプルプログラムを構成して Kerberos 環境でシングルサインオンを行う方法について学習します。