課題 8:SPNEGO と Java Generic Security Services (GSS) API の使用
この課題の目標:
現在、Java GSS で使用可能なセキュリティー機構は「Kerberos」のみです。この課題の目標は、SPNEGO などのほかの Java GSS 機構を使用して安全に関連付けを行う方法について学習することです。この機能は、Java SE 6 以降で使用できます。
SPNEGO とは 
Java GSS は、複数のセキュリティー機構をサポートするフレームワークです。GSS-API の下でセキュリティー機構とネゴシエーションを行う方法が必要となります。これは、SPNEGO を介して使用できます。
SPNEGO は、IETF の RFC 4178 で標準化されている Simple and Protected GSS-API Negotiation 機構です。これは、基礎となるセキュリティー機構とのネゴシエーションを行う場合に使用する擬似セキュリティー機構です。SPNEGO を使用すると、クライアントとサーバーに柔軟性が付与され、共通の GSS セキュリティー機構と安全にネゴシエーションを行うことができます。
Microsoft は SPNEGO を多用しています。SPNEGO は、HTTP を介して Microsoft サーバーと相互運用したり、Negotiate プロトコルを介して HTTP ベースのクロスプラットフォーム認証をサポートする場合に使用されます。
Java GSS で SPNEGO を使用する場合に行う必要がある設定
現在、Kerberos とともに Java GSS を使用する場合は、Kerberos OID を指定して Kerberos を使用します。 
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
SPNEGO を使用するには、次のように SPNEGO OID を指定する必要があります。
Oid spnegoOid = new Oid("1.3.6.1.5.5.2");
そのあと、GSSCredential、GSSContext などを作成する際に SPNEGO OID を使用します。
実行手順:
- 次のコードを参照してください。これは 
src/GssSpNegoClient.java にあります。 
GssSpNegoClient.java のコードリスト
  
    
        
      
static class GssSpnegoClientAction 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 spnegoOid = new Oid("1.3.6.1.5.5.2"); 
    GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE, spnegoOid); 
         
         
    // Get the context for authentication 
    GSSContext context = manager.createContext(serverName, spnegoOid, 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 GssSpNegoClient.java
      
- 次のコードを参照してください。これは 
src/GssSpNegoServer.java にあります。
   
GssSpNegoServer.java のコードリスト
  
    
        
      
static class GssSpNegoServerAction 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 spnegoOid = new Oid("1.3.6.1.5.5.2"); 
    GSSCredential serverCreds = manager.createCredential(null, 
      GSSCredential.DEFAULT_LIFETIME, spnegoOid, 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 GssSpNegoServer.java
     
- 新規のウィンドウを起動してサーバーを起動します。  
  
    
% xterm &
% java -Djava.security.auth.login.config=jaas-krb5.conf \
       GssSpNegoServer
       
- クライアントアプリケーションを実行します。GssClient は、次の 2 つのパラメータを取ります。サービス名、およびそのサービスが実行されているサーバーの名前です。たとえば、サービスがマシン 
j1hol-001 上で実行されている host の場合は、次を入力します。パスワードの入力を求められた場合は、changeit を入力します。    
    
% java -Djava.security.auth.login.config=jaas-krb5.conf \
       GssSpNegoClient host j1hol-001
       
 GssSpNegoServer の例を実行した場合の出力
  
    
        
      
Authenticated principal:[host/j1hol-001@J1LABS.EXAMPLE.COM] 
Waiting for incoming connections... 
Got connection from client /129.145.128.102 
- SPNEGO Negotiated Mechanism = 1.2.840.113554.1.2.2 Kerberos V5
 
         
Context Established! 
Client principal is test@J1LABS.EXAMPLE.COM 
Server principal is host/j1hol-001@J1LABS.EXAMPLE.COM 
Mutual authentication took place! 
Received data "Hello There!" of length 12 
Confidentiality applied:true 
Sending:Hello There!Thu May 06 12:11:15 PDT 2005 
       
       
       | 
    
  
GssSpNegoClient の例を実行した場合の出力
  
    
        
      
Kerberos password for test:changeit 
Authenticated principal:[test@J1LABS.EXAMPLE.COM] 
Connected to address j1hol-001/129.145.128.102 
- SPNEGO Negotiated Mechanism = 1.2.840.113554.1.2.2 Kerberos V5
 
Context Established! 
Client principal is test@J1LABS.EXAMPLE.COM 
Server principal is host@j1hol-001 
Mutual authentication took place! 
Sending message:Hello There! 
Will read token of size 93 
Received message:Hello There!Thu May 06 12:11:15 PDT 2005 
       
       
       | 
    
  
 まとめ:
この課題では、Kerberos などの基礎となるセキュリティー機構とネゴシエーションを行い、基礎となる認証システムとして Kerberos を使用して安全に通信を行うために SPNEGO とともに Java GSS API を使用するクライアント/サーバーアプリケーションを記述する方法について学習しました。
注: Microsoft は特定のバリエーションの SPNEGO プロトコルを実装しています。このため、Microsoft と相互運用を行うために、新しいシステムプロパティー「sun.security.spnego.msinterop」を使用して MS モードを追加しました。このプロパティーは、デフォルトで「true」になっています。無効にするには、このプロパティーを明示的に「false」に設定する必要があります。SPNEGO デバッグを有効にするには、システムプロパティー「sun.security.spnego.debug=true」を設定します。