現在、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
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.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 2005GssSpNegoClient の例を実行した場合の出力。
Kerberos password for test: changeitAuthenticated principal: [test@J1LABS.EXAMPLE.COM]Connected to address j1hol-001/129.145.128.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
サマリー:
この課題では、Kerberos などのベースとなるセキュリティーメカニズムとネゴシエーションを行い、ベースとなる認証システムとして Kerberos を使用してセキュアに通信を行うために SPNEGO とともに Java GSS API を使用するクライアント/サーバーアプリケーションを記述する方法について学習しました。
注: Microsoft は特定のバリエーションの SPNEGO プロトコルを実装しています。このため、Microsoft と相互運用を行うために、新しいシステムプロパティー「sun.security.spnego.msinterop」を使用して MS モードを追加しました。このプロパティーは、デフォルトで「true」になっています。無効にするには、このプロパティーを明示的に「false」に設定する必要があります。SPNEGO デバッグを有効にするには、システムプロパティー「sun.security.spnego.debug=true」を設定します。