XML Signatureの例

この例では、クライアントから発行されるSOAP要求にデジタル署名を追加する方法、およびSOAPメッセージがサーバに着信する際に署名を検証する方法について説明します。 SOAPメッセージに署名を追加したり、署名がSOAPメッセージに含まれていることを検証したりするには、さまざまなプロパティをスタブ/スケルトンに設定する必要があります。 設定可能なすべてのプロパティおよびその種類の詳細情報については、com.sssw.jbroker.web.security.XMLSignaturePropertiesインタフェースを参照してください。

1 Shopインタフェース

この例で使用されるRemoteインタフェースは、簡単なShoppingインタフェースです。このインタフェースには、在庫を一覧表示し、品目の値段を取得し、品目を注文するメソッドがあります。
package signature;
                                                                           
import java.rmi.Remote;
import java.rmi.RemoteException;
                                                                           
public interface Shop extends Remote
{
    String[] items() throws RemoteException;
                                                                           
    float price(String item) throws NoSuchItemException, RemoteException;
                                                                           
    int order(String item) throws NoSuchItemException, RemoteException;
}
Remoteインタフェースにはセキュリティ情報が表示されません。 これは、セキュリティ情報やトランザクションのコンテキストなどの低レベルの情報は、フレームワークによって「背後で」処理されるという分散システムで実績のある設計パターンに従っているためです。

2 Shoppingクライアント

Shoppingクライアントは、JNDIからサービスオブジェクトを取得するための通常の手順を実行し、スタブを取得します。 着信するSOAP応答メッセージを検証するためだけではなく、発信するSOAP要求にデジタル署名を追加するために、さまざまなプロパティがスタブに設定されます。
package signature;
                                                                           
import java.io.IOException;
import java.io.InputStream;
                                                                           
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
                                                                           
import java.awt.BorderLayout;
                                                                           
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
                                                                           
import java.util.ArrayList;
                                                                           
import javax.xml.rpc.Stub;
import javax.xml.namespace.QName;
import javax.xml.rpc.handler.HandlerInfo;
                                                                           
import javax.naming.InitialContext;
                                                                           
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
                                                                           
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
                                                                           
import com.sssw.jbroker.web.security.XMLSignatureProperties;
                                                                           
public class Client
{
    public static void main(String[] args) throws Exception
    {
    |   //get the shop service
    |   InitialContext ctx = new InitialContext();
    |   ShopService service = (ShopService)
    |       ctx.lookup("xmlrpc:soap:signature.ShopService");
    |                                                                      
    |   final Shop stub = service.getShopPort();
    |                                                                      
    |   //set this property to sign outgoing messages
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.SIGN_OUTGOING, Boolean.TRUE);
    |                                                                      
    |   //set this property to validate incoming messages
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.VALIDATE_INCOMING, Boolean.TRUE);
    |                                                                      
    |   //get signature information from key store
    |   char[] secret = "secret".toCharArray();
    |   KeyStore ks = KeyStore.getInstance("JKS");
    |   ClassLoader loader = Thread.currentThread().getContextClassLoader();
    |   InputStream is = loader.getResourceAsStream("keystore.jks");
    |   if (is == null)
    |       throw new IOException("failed to load key store resource");
    |   ks.load(is, secret);
    |   is.close();
    |                                                                      
    |   //signature method
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.SIGNATURE_METHOD_URI,
    |       XMLSignatureProperties.ALGO_ID_SIGNATURE_DSA);
    |                                                                      
    |   //digest method
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.DIGEST_URI,
    |       XMLSignatureProperties.ALGO_ID_DIGEST_SHA1);
    |                                                                      
    |   //transforms
    |   String[] tsfms = new String[] {
    |   |   XMLSignatureProperties.TRANSFORM_ENVELOPED_SIGNATURE,
    |   };
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.TRANSFORM_URIS, tsfms);
    |                                                                      
    |   //canonicalization method
    |   ((Stub)stub)._setProperty(
    |       XMLSignatureProperties.CANONICALIZATION_METHOD_URI,
    |       XMLSignatureProperties.ALGO_ID_C14N_OMIT_COMMENTS);
    |                                                                      
    |   //key info certificate
    |   X509Certificate cert = (X509Certificate) ks.getCertificate("test");
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.CERTIFICATE,
    |       cert.getEncoded());
    |                                                                      
    |   //validation public key
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.VALIDATION_PUBLIC_KEY,
    |       cert.getPublicKey().getEncoded());
    |                                                                      
    |   //signing key
    |   PrivateKey privateKey = (PrivateKey) ks.getKey("test", secret);
    |   byte[] prkba = privateKey.getEncoded();
    |   ((Stub)stub)._setProperty(XMLSignatureProperties.PRIVATE_KEY, prkba);
    |                                                                      
    |   //set the end point URL
    |   ((Stub)stub)._setProperty("javax.xml.rpc.service.endpoint.address",
    |       args.length > 0 ? args[0] : "http://localhost:9090/shop");
    |                                                                      
    |   //create a frame, combo box and button
    |   final JFrame frame = new JFrame("Shop Client");
    |   final JComboBox combo = new JComboBox(stub.items());
    |   frame.getContentPane().add(combo);
    |   JButton button = new JButton("Buy");
    |   frame.getContentPane().add(button, BorderLayout.EAST);
    |                                                                      
    |   //do something when user presses button
    |   button.addActionListener(new ActionListener() {
    |   |   public void actionPerformed(ActionEvent event) {
    |   |   |   String item = (String) combo.getSelectedItem();
    |   |   |   try {
    |   |   |   |   int order = stub.order(item);
    |   |   |   |   JOptionPane.showMessageDialog(frame, "order number " +
    |   |   |   |       order, "Confirmation", JOptionPane.INFORMATION_MESSAGE);
    |   |   |   } catch (Exception ex) { ex.printStackTrace(); }
    |   |   }
    |   });
    |                                                                      
    |   //exit on frame close
    |   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    |                                                                      
    |   //pack and display
    |   frame.pack();
    |   frame.setVisible(true);
    }
                                                                           
}

3 Shopの実装

Shopサーバは、Remoteインタフェースに最小限のメソッドを実装しています。 これは、この例がデジタル署名の使用を説明するためのもので、買い物用アプリケーションの記述方法については説明していないためです。
package signature;
                                                                           
import java.io.InputStream;
import java.io.IOException;
                                                                           
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
                                                                           
import java.rmi.RemoteException;
                                                                           
import javax.servlet.ServletException;
                                                                           
import javax.xml.rpc.handler.HandlerInfo;
                                                                           
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
                                                                           
import com.sssw.jbroker.web.security.XMLSignatureProperties;
                                                                           
public class ShopImpl extends Shop_ServiceSkeleton
{
    public void init() throws ServletException
    {
    |   super.init();
    |                                                                      
    |   try {
    |   |                                                                  
    |   |   //set this property to sign outgoing messages
    |   |   _setProperty(XMLSignatureProperties.SIGN_OUTGOING, Boolean.TRUE);
    |   |                                                                  
    |   |   //set this property to validate incoming messages
    |   |   _setProperty(XMLSignatureProperties.VALIDATE_INCOMING, Boolean.TRUE);
    |   |                                                                  
    |   |   //get signature information from key store
    |   |   char[] secret = "secret".toCharArray();
    |   |   KeyStore ks = KeyStore.getInstance("JKS");
    |   |   ClassLoader loader = Thread.currentThread().getContextClassLoader();
    |   |   InputStream is = loader.getResourceAsStream("keystore.jks");
    |   |   if (is == null)
    |   |       throw new IOException("failed to load key store resource");
    |   |   ks.load(is, secret);
    |   |   is.close();
    |   |                                                                  
    |   |                                                                  
    |   |   //signature method
    |   |   _setProperty(XMLSignatureProperties.SIGNATURE_METHOD_URI,
    |   |       XMLSignatureProperties.ALGO_ID_SIGNATURE_DSA);
    |   |                                                                  
    |   |   //digest method
    |   |   _setProperty(XMLSignatureProperties.DIGEST_URI,
    |   |       XMLSignatureProperties.ALGO_ID_DIGEST_SHA1);
    |   |                                                                  
    |   |   //transforms
    |   |   String[] tsfms = new String[] {
    |   |   |   XMLSignatureProperties.TRANSFORM_ENVELOPED_SIGNATURE,
    |   |   };
    |   |   _setProperty(XMLSignatureProperties.TRANSFORM_URIS, tsfms);
    |   |                                                                  
    |   |   //canonicalization method
    |   |   _setProperty(XMLSignatureProperties.CANONICALIZATION_METHOD_URI,
    |   |       XMLSignatureProperties.ALGO_ID_C14N_OMIT_COMMENTS);
    |   |                                                                  
    |   |   //key info certificate
    |   |   X509Certificate cert = (X509Certificate) ks.getCertificate("test");
    |   |   _setProperty(XMLSignatureProperties.CERTIFICATE, cert.getEncoded());
    |   |                                                                  
    |   |   //signing key
    |   |   PrivateKey privateKey = (PrivateKey) ks.getKey("test", secret);
    |   |   byte[] prkba = privateKey.getEncoded();
    |   |   _setProperty(XMLSignatureProperties.PRIVATE_KEY, prkba);
    |   |                                                                  
    |   } catch (Exception ex) {
    |   |   throw new javax.servlet.ServletException(ex.getMessage());
    |   }
    |                                                                      
    }
                                                                           
    public float price(String item)
        throws NoSuchItemException, RemoteException
    {
    |   String price = (String) _inventory.get(item);
    |   if (price == null) throw new NoSuchItemException(item);
    |   return Float.valueOf(price).floatValue();
    }
                                                                           
    public String[] items()
        throws RemoteException
    {
    |   String[] items = new String[_inventory.size()];
    |   return (String[]) _inventory.keySet().toArray(items);
    }
                                                                           
    public int order(String item)
        throws NoSuchItemException, RemoteException
    {
    |   return _orderNo++;
    }
                                                                           
    private int _orderNo;
                                                                           
    private static Map _inventory = new HashMap();
                                                                           
    static {
    |   _inventory.put("milk", "2.19");
    |   _inventory.put("apple", "1.19");
    |   _inventory.put("juice", "2.49");
    |   _inventory.put("candy", "1.49");
    |   _inventory.put("soap", "1.79");
    }
}
サーバの最も重要な部分は、Signatureメソッド、URIダイジェストなどのデジタル署名のためのプロパティを設定するinitメソッドです。プロパティが設定されると、ランタイムによって発信SOAPメッセージ、つまり応答にSignatureが追加されます。 また、着信要求SOAPメッセージの署名も検証されます。

4 例の実行

例を実行する前に、次の環境が整っていることを確認してください。 また、JDK 1.2または1.3を使用している場合は、ビルドスクリプトを変更してCLASSPATHにJCEを含める必要があります。 JCEは、SunのJavaサイトからダウンロードできます。

JDK1.4を使用している場合は、Xalanの適切なバージョンがjre/lib/endorsedディレクトリにコピーされていることを確認する必要があります。 Xalan 2.2.0以降では、XML Signatureツールキットが使用できます。
Copyright © 2000-2003, Novell, Inc.All rights reserved.