XML暗号化の方法を探る:第1回

構造化データを安全に交換する方法のデモ

XML暗号化は、構造化データを安全に交換する必要のあるアプリケーションのために、エンドツーエンドのセキュリティーを提供します。XMLはデータ構造化のテクノロジーとして最も広く使用されているものであり、XMLベースの暗号化は、データ交換アプリケーションでの複雑なセキュリティー要件を処理する自然な方法と言えます。この記事は2回にわたるシリーズの第1回です。Bilal氏が、W3CのXML暗号化のワーキング・ドラフトを踏まえて、XMLとセキュリティーの問題をどう処理できるかを説明します。

Bilal Siddiqui, CEO, WAP Monster

Bilal Siddiqui は XML のコンサルタントです。パキスタン、ラホールの University of Engineering and Technology で電子工学を学んだのち、1995年に産業制御システム向けのソフトウェア・ソリューションの設計を始めました。その後 XML に転向し、C++ のプログラミング経験を生かして Web および WAP ベースの XML 処理ツール、サーバー・サイド構文解析ソリューション、およびサービス・アプリケーションの構築を行うようになりました。



2002年 3月 01日

ゾーン・リーダーのコメント

この記事はW3C XML Encryptionの古いワーキングドラフトに基づいています。最新の仕様では、いくつかの点で重要な変更があります。最新の情報を得るには、W3Cの公式サイトhttp://www.w3.org/Encryption/2001/ を参照してください。

現在のところ、トランスポート層セキュリティー (TLS) が、インターネットにおける保護通信のデ・ファクト・スタンダードとなっています。TLSは、よく知られたSecure Socket Layer (SSL) に準拠したエンドツーエンドのセキュリティー・プロトコルです。SSLは、もともとNetscapeで設計されたものであり、そのバージョン3.0に対して、TLSを設計中のInternet Engineering Task Force (IETF) が後に手を加えたものです。これは、非常に安全性と信頼性の高いプロトコルであり、2者間のエンドツーエンド・セキュリティー・セッションを提供します。XML暗号化は、SSL/TLSを置き換えることを意図したものではありません。むしろ、SSLではカバーし切れないセキュリティー要件のためのメカニズムを提供するためのものです。SSLで対応できないこととして、以下に示す2つの重要な点があります。

  • 交換するデータの一部を暗号化すること
  • 2者以上での保護セッション

XML暗号化を使用した場合、どの通信者も、他者との間で保護状態または無保護状態を保つことができます。1つの文書の中で、保護データと無保護データの両方を交換できます。たとえば、ある保護機能付きチャット・アプリケーションがあるとします。それには、複数のチャット・ルームが含まれており、各チャット・ルームには複数の人が関係しています。チャット・パートナー相互の間ではXML暗号化ファイルを交換できますが、あるチャット・ルームのデータは、それ以外のチャット・ルームからは見えないようになっています。

XML暗号化では、XMLデータと非XMLデータ (バイナリー・データなど) を処理できます。以下に、XML暗号化によって保護された簡単なデータ交換の例を示します。セキュリティー要件を少しずつ複雑にしながら、XML暗号化スキーマとそのさまざまな要素の使い方について説明します。

XMLデータを安全に交換する簡単な例

リスト1 に示すXMLファイルを、ある出版会社に送るとします。このファイルには、購入したい本についての詳細なデータが含まれています。さらに、支払いに使用するクレジット・カードの情報も含まれています。当然、このような個人情報には保護通信を使用したいと思うことでしょう。1つの方法は、通信の全体を保護するSSLを使用することです。別の方法は、XML暗号化を使用することです。すでに述べたように、XML暗号化はSSL/TLSに代わるものではありません。通信の全体を保護する必要のあるアプリケーションなら、SSLを使用することでしょう。しかし、保護通信と無保護通信を組み合わせる (データの一部は保護した状態で交換し、それ以外の部分は元のまま交換する) ことの必要なアプリケーションの場合は、XML暗号化が最善です。

リスト1. 暗号化するサンプルXMLファイル
<purchaseOrder>
	<Order>
		<Item>book</Item>
		<Id>123-958-74598</Id>
		<Quantity>12</Quantity>
	</Order>
	<Payment>
		<CardId>123654-8988889-9996874</CardId>
		<CardName>visa</CardName>
		<ValidDate>12-10-2004</ValidDate>
	</Payment>
</purchaseOrder>

注:リスト1 のXMLファイルは、意図的に簡単なものにしてあります。それにより、暗号化の問題に注意を集中できます。共同コマースやWeb Servicesでの実際のXMLファイルは、構造としては似たようなものですが、もっと冗長です。WSDL (Webサービス記述言語) およびSOAP (Simple Object Access Protocol) は、B2B統合でしばしば使用されるXMLベースの文法です。WSDLでもSOAPでも、XML暗号化を使用することによって、企業全体にわたる保護通信を提供できます。詳細については、W3Cのサイトをご覧ください (参考文献を参照)。

XML暗号化による文書全体の暗号化

XML暗号化には、さまざまなオプションがあります。リスト2リスト3、およびリスト4 に、それぞれ異なる暗号化の結果を示します。1つずつ詳しく見てみましょう。

リスト2. Encrypting the entire file
<?xml version='1.0' ?> 
<EncryptedData xmlns='http://www.w3.org/2001/04/xmlenc#'
		Type='http://www.isi.edu/in-notes/iana/assignments/media-types/text/xml'>
	<CipherData>
		<CipherValue>A23B45C56</CipherValue>
	</CipherData>
</EncryptedData>
リスト 3. Encrypting only the <Payment> element
<?xml version='1.0' ?> 
<PurchaseOrder>
	<Order>
		<Item>book</Item>
		<Id>123-958-74598</Id>
		<Quantity>12</Quantity>
	</Order>
	<EncryptedData Type='http://www.w3.org/2001/04/xmlenc#Element'
     						xmlns='http://www.w3.org/2001/04/xmlenc#'>
		<CipherData>
			<CipherValue>A23B45C564587</CipherValue>
		</CipherData>
	</EncryptedData>
</PurchaseOrder>
リスト 4. Encrypting only the content in the CardId element
<?xml version='1.0' ?> 
<PurchaseOrder>
	<Order>
		<Item>book</Item>
		<Id>123-958-74598</Id>
		<Quantity>12</Quantity>
	</Order>
	<Payment>
		<CardId>
			<EncryptedData Type='http://www.w3.org/2001/04/xmlenc#Content'
     							xmlns='http://www.w3.org/2001/04/xmlenc#'>
				<CipherData>
					<CipherValue>A23B45C564587</CipherValue>
				</CipherData>
			</EncryptedData></CardId>
		<CardName>visa</CardName>
		<ValidDate>12-10-2004</ValidDate>
	</Payment>
</PurchaseOrder>

リスト2 は、リスト1 のXML文書全体を暗号化した場合のXML暗号化ファイルです。<CipherData><CipherValue> のタグに注目してください。<CipherValue> タグの内容が、実際の暗号化データです。CipherData 要素は、EncryptedData 要素の中に含まれています。EncryptedData 要素には、暗号化に使用されるXML名前空間が含まれています。たとえば、暗号化前の元のデータはXMLであり、Internet Assigned Numbers Authority (IANA) によるXMLの公式の型定義は、http://www.isi.edu/in-notes/iana/assignments/media-types/text/xmlです。このことは、Type 属性の値に示されています。XML暗号化では、RTF、PDF、JPGなど、広く使用されているさまざまなデータ形式に関して、IANAによる型定義が使用されます。詳しくは、対応するWebサイトをご覧ください (参考文献を参照)。特別なアプリケーション・データ型 (各企業のコンテンツ・マネージメント・システムに属する独自のDTDやXSDなど) の場合は、それをEncryptedData 要素のType 属性に指定できます。もう1つの属性xmlnsは、XMLデータの暗号化に使用したXML暗号化名前空間を指定します。

XML暗号化による単一要素の暗号化

リスト1 のうち、1つの要素 (たとえばPayment 要素) だけを暗号化したいと思うかもしれません。その結果を、リスト3 に示します。リスト2リスト3 を比較すると、次のような違いがあります。

  1. リスト2 に含まれるのはXML暗号化のスキーマだけですが、リスト3 にはXML暗号化とリスト1 の元データの要素とが混在しています。リスト3 の場合、XML暗号化データはユーザーのXMLの内部に埋め込まれています。
  2. リスト3 でも、<EncryptedData>Type 属性が指定されていますが、その値はhttp://www.w3.org/2001/04/xmlenc#Elementです。IANA型は使用されていません。その代わり、XML暗号化で指定される型が使用されています。
  3. 特に末尾の #Elementに注目してください。これは、EncryptedDataが1つの要素を表すことを意味しています。

要素の内容の暗号化

リスト4 は、リスト1 に含まれる1つの要素CardId の内容だけを暗号化した結果です。この場合、Type 属性値にはhttp://www.w3.org/2001/04/xmlenc#Contentを使用しています。内容だけを暗号化する場合には、この値を使用します。

非XMLデータの暗号化

では、JPEGファイルなどをXML暗号化によって送信する場合は、どうなるのでしょうか? その典型的な例を、リスト5 に示します。JPEGファイルは、そのバイト・シーケンスを暗号化した上で、CipherValue 要素の内容として含められています。リスト2リスト5 の違いは、1つしかありません。それは、EncryptedData 要素のType 属性です。リスト5 には、JPEG形式に対応するIANA型が示されています。同じような方法でIANAの値を指定することによって、任意の形式を暗号化できます。IANAのWebサイトをご覧ください (参考文献を参照)。

リスト 5. Encrypting arbitrary non-XML data (JPEG)
<?xml version='1.0' ?> 
<EncryptedData xmlns='http://www.w3.org/2001/04/xmlenc#'
			 Type='http://www.isi.edu/in-notes/iana/assignments/media-types/jpeg' >
	<CipherData>
		<CipherValue>A23B45C56</CipherValue>
	</CipherData>
</EncryptedData>

XML暗号化の鍵

リスト1~5は暗号化の例ですが、鍵がなくては使い物になりません (この後のコラム「公開鍵、私有鍵、および共通鍵」を参照)。XML暗号化で鍵に関連する問題は、次の2つの部分に分けられます。

  • 鍵の交換 (非対称暗号化)
  • 以前に交換された鍵の使用 (対称暗号化)

こうしてユーザーは鍵を交換し、その後それを使用できます。

共通鍵の交換のための非対称鍵

このシナリオでは、通信の一方の側がその公開鍵を相手に送ります。相手は、その公開鍵を使用して、自分の共通鍵を暗号化します。このデータ交換の様子をリスト6 (要求) とリスト7 (応答) に示します。通信の一方の側がImranで、相手がAliであるとしましょう。Imranは、公開鍵交換要求を初期化し、その公開鍵をKeyValueという要素に入れて送ります。属性CarriedKeyName は、送信する鍵の名前を表します。この構造のルート要素はEncryptedKey であり、それにはds:KeyInfo 要素とds:KeyValue 要素が含まれます。ds:KeyInfo 要素とds:KeyValue 要素は、XMLディジタル・シグニチャー (ds:) 名前空間に属しています。XML暗号化は、鍵の交換に関してはXMLディジタル・シグニチャーの仕様に完全に依存しています。そのため、<ds:EncryptedKey><ds:KeyValue> は、いずれもXMLディジタル・シグニチャー名前空間に属しています。リスト7 は、Aliが送る応答です。リスト7 の中のCipherValue 要素の内容は、新たに生成された共通鍵をImranの公開鍵によって暗したものです。 リスト6とリスト7をよく見ると、要求と応答の両方にEncryptedKey 要素が含まれていることがわかります。EncryptedKey 要素の中のds:KeyInfo 要素とds:KeyValue 要素によって、公開鍵が送信されます (リスト6)。一方、EncryptedKey 要素の中のCipherData 要素とCipherValue 要素 (リスト7) によって、共通鍵 (を暗号化したもの) が送信されます。さらに、EncryptedKey 要素には、常にCarriedKeyName 属性が含まれており、送信され名前がそれによって指定されます。

リスト 6. One party sends its public key to a second party for key exchange
<?xml version='1.0' ?> 
<SecureCommunicationDemonstration>
	<EncryptedKey  CarriedKeyName="Muhammad Imran" xmlns='http://www.w3.org/2001/04/xmlenc#'>
		<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
			<ds:KeyValue>1asd25fsdf2dfdsfsdfds2f1sd23</ds:KeyValue>
		</ds:KeyInfo>
	</EncryptedKey>
</SecureCommunicationDemonstration>
リスト 7. Second party sends back the randomly generated secret key encrypted with public key of first party
<?xml version='1.0' ?> 
<SecureCommunicationDemonstration>
	<EncryptedKey  CarriedKeyName="Imran Ali" xmlns='http://www.w3.org/2001/04/xmlenc#'>
		 <EncryptionMethod   Algorithm= "http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
		 	<CipherData>
				<CipherValue>xyza21212sdfdsfs7989fsdbc</CipherValue>
			</CipherData>   
	</EncryptedKey>
</SecureCommunicationDemonstration>

過去に交換した鍵の使用

前のセクションでは、共通鍵を交換しました。ここでは、その鍵を使用してデータを暗号化します。Imranがリスト7 への応答としてXMLメッセージ (リスト8) を送信するとします (リスト7 には、"Imran Ali" という名前の暗号化された共通鍵が含まれていることに注意)。Imranは、自分の (Imranの) 私有鍵を使ってこの共通鍵を暗号化解除します (Aliは共通鍵をImranの公開鍵を使って暗号化しました)。Imranは、この共通鍵を使用し、リスト8CipherValue 要素中にそれを入れることによって、Aliに送りたいデータを暗号化することができます。

リスト 8. Data is encrypted with a secret key and placed in the <CipherValue> element
<?xml version='1.0' ?> 
<<SecureCommunicationDemonstration>
	<Order>
      	<Item>book</Item>
	      <Id>123-958-74598</Id>
      	<Quantity>12</Quantity>
	      <CardName>Visa</CardName>
	      <ExpDate>10-10-2005</ExpDate>

		<EncryptedData  Type='http://www.w3.org/2001/04/xmlenc#Element' 
						xmlns='http://www.w3.org/2001/04/xmlenc#'>
			<EncryptionMethod Algorithm='http://www.w3.org/2001/04/xmlenc#tripledes-cbc '/>
			<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
			     	<ds:KeyName>Imran ali</ds:KeyName>
		  	</ds:KeyInfo>
			<CipherData>
				<CipherValue>A23B45C564587</CipherValue>
		   	</CipherData>	
		</EncryptedData>
	</Order>
</SecureCommunicationDemonstration>

リスト8ds:KeyInfo 要素の中には、KeyName 要素が含まれています。この組み合わせにより、データ暗号化のためにImranが使用した鍵の名前が参照されます。

図1に、保護データ交換でのXMLファイル交換の様子を示します。

図1. XML暗号化による鍵およびデータの交換のシーケンス
図1. XML暗号化による鍵およびデータの交換のシーケンス

XML暗号化ファイルから外部暗号化データを参照する

リスト5 および7 において、CipherData 要素はEncryptedData 要素またはEncryptedKey 要素の中に指定できます。 CipherData 要素は、暗号化されたデータ (EncryptedData 要素の中に指定した場合) または暗号化された鍵 (EncryptedKey 要素の中に指定した場合) を参照するために使用しています。リスト5 とリスト7 の両方において、CipherData 要素の中に、実際の暗号化データを含むCipherValue 子要素が含まれています。

さらには、外部の暗号化データまたは暗号化鍵も参照できます。つまり、実際の暗号化データまたは暗号化鍵が、XML暗号化ファイルの中ではなく、どこか別の場所 (おそらくインターネット上のどこか) にあるという場合です。その場合には、CipherData の中でCipherValue 子要素の代わりにCipherReference を使用します。実際の暗号化データは、URIによって参照します。リスト9 をご覧ください。

リスト 9. Using the <CipherReference> element to refer to external encrypted data
<?xml version='1.0' ?> 
<EncryptedData   xmlns='http://www.w3.org/2001/04/xmlenc#'  
				Type='http://www.w3.org/2001/04/xmlenc#Element'>
	<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
	     	<ds:KeyName>Imran ali</ds:KeyName>
	</ds:KeyInfo>
	<CipherData>
		<CipherReference URI="www.waxsys.com/secureData/waxFile.txt"/>
	</CipherData>
</EncryptedData>

外部XMLファイルの特定の要素を参照する

リスト10 は、外部XMLファイルを参照する別の方法を示すものです。ここでは、URIの指す外部ファイルの一部分だけを参照しています。CipherReference 要素の中にTransforms 子要素が含まれています。このTransforms 要素には、それぞれ単一のXPath要素を内容とするTransform 要素をいくつか含めることができます。このXPath要素には、外部XML文書の特定のノードを参照するXPath式を指定します。

リスト 10. Using the <Transforms> element and XPath to refer to a particular node of the external XML File
<?xml version='1.0' ?> 
<EncryptedData  ID="Enc-Data" xmlns='http://www.w3.org/2001/04/xmlenc#'
					 Type='http://www.w3.org/2001/04/xmlenc#Element' >
	<CipherReference URI="http://www.waxsys.com/EncFile.xml" >	
		<Transforms  xmlns:ds="http://www.w3.org/2000/09/xmldsig#" >
      		<ds:Transform 	Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
	            	<wax:XPath xmlns:wax="http://www.waxsys.com/xpathNS">
		       		PruchaseOrder/EncryptedData [@Id="Imran-Enc-Data"]    
	            	</wax:XPath>
			</ds:Transform>
          </Transforms>
	</CipherReference>
</EncryptedData>

API (案) のDOM構造

ここまでで、XML暗号化ファイルを作成し、暗号化データを交換する方法を見てきました。次に、XML暗号化のためのJava APIの案と、その実装例を示します。そのために、DOMを使用することにします。

このDOM実装は、一群のクラスで構成されます (リスト11~16)。XmlEncryptionクラス (リスト11) は、それ以外のクラスのラッパーであり、APIのユーザーが実際に必要とするのはこのクラスだけです。その内部では、その他のクラスの機能が使用されています。

リスト 11. XmlEncryption.java
 /*
 	DW/BS
	20020204
	XmlEncryption.java
	Listing 11
	A wrapper class that can generate complete XML encrypted file.
	It uses all the other classes. 
	Users of our XML Encryption Engine will only need to interact with 
	this class.
 */

import java.io.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.apache.crimson.tree.XmlDocument;

public class XmlEncryption {
	
	// Source and Result file names.
	private String fileSource = null;
	private String fileResult = null;
	
	// Name of Algorithm which will be used to encrypt data.
	private String algoName = null;

	// Name of Secret key which was previously agreed upon 
	// and saved with the given name.
	private String keyName = null; 
	
	// Id attribute of Main structure 
	private String encId = null;

	// It will be used to get New Document Objects.
	private DocumentBuilder docBuilder = null;

	// Default Constructor
	public XmlEncryption() {
		// Create DocumentBuilder object from DocumentBuilderFactory.
		try {
			docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		} catch (ParserConfigurationException e){ docBuilder = null; }
	}
	
	// Get the new Document object for DocumentBuilder
	private Document getNewDocument() {
		if (docBuilder != null)
			return docBuilder.newDocument();
		else
			return null;	
	}
	
	// Generate Complete XML Encrypted File.
	public void encryptCompleteXmlFile(){
		// Take an Object of EncryptedData Class. 
		// It represents EncryptedData Element.
		EncryptedData encDataObj = this.getEncryptedDataDoc(this.encId, "DOCUMENT");

		// Get XML Structure for EncryptionMehtod element.
		Document encMethodDoc = this.getEncryptionMethodDoc(this.algoName);	

		// Get XML Structure for KeyInfo element.		
		Document encKeyInfoDoc = this.getKeyInfoDoc(this.keyName);

		// Read the given file data which will be encrypted.
		String plainData = this.readFile(fileSource);

		// Use of JCA/JCE to get encrypted data.
		String cipherData = this.getEncryptedData(plainData);

		// Get XML Structure for CipherData element.				
		Document cipherDataDoc = this.getCipherDataDoc(cipherData);
		

		// Join these XML Structures.
		encDataObj.addChild(encMethodDoc);
		encDataObj.addChild(encKeyInfoDoc);
		encDataObj.addChild(cipherDataDoc);
		
		// Now Save this Document as an XML File
		this.saveEncryptedFile(this.fileResult, encDataObj.getEncData());		
	}// End encryptCompleteXmlFile()
	
	
	//********** All Set/Get methods to related fields.
	
	public void setFileSource(String file) {
		this.fileSource = file;
	}// End setFileSource()
	
	public String getFileSource() {
		return this.fileSource;
	}// End getFileSource()

	public void setFileResult(String file) {
		this.fileResult = file;
	}// End setFileResult() 
	
	public String getFileResult() {
		return this.fileResult;
	}// End getFileResult() 
	
	public void setAlgoName(String name) {
		this.algoName = name;
	}// End setAlgoName() 

	public String getAlgoName() {
		return this.algoName;
	}// End getAlgoName() 
	
	public void setKeyName (String key) {
		this.keyName = key; 
	}// End setKeyName()
	
	public String getKeyName() {
		return this.keyName; 
	}// End getKeyName()

	public void setEncId (String id) {
		this.encId = id;
	}// End setEncId()

	public String getEncId() {
		return this.encId;
	}// End setEncId()

	//**************

	// Reads the given file and returns it as string.		
	public String readFile(String fileName){
		String xml = "";
		try {
			FileInputStream in = new FileInputStream(fileName);
			byte [] data = new byte[in.available()];
			in.read(data);
			xml = new String(data);
		} catch (IOException e) { }
		return xml;
	}// End readFile()
	
	// Saves the given document as an XML (Text) file with given name.
	public void saveEncryptedFile (String fileName, Document doc) {
		XmlDocument xmlDoc = (XmlDocument)doc;
		try { 
			OutputStream out = new FileOutputStream(fileName);
			xmlDoc.write(out);
			out.close();
		} catch (IOException e) { }

	}// End saveEncryptedFile()
	
	// Returns the EncryptedData Object.
	public EncryptedData getEncryptedDataDoc(String Id, String encType) {
		EncryptedData ed = new EncryptedData(this.getNewDocument());
		ed.setId(Id);
		if (encType.equals("DOCUMENT"))
			ed.setType(AlgoNames.DOCUMENT);
	
		return ed;
	}// End getEncryptedDataDoc()

	// Returns the EncryptionMehtod Structure.
	public Document getEncryptionMethodDoc (String algoName) {
		EncryptionMethod em = new EncryptionMethod(this.getNewDocument());
		if (algoName.equals("TripleDes-cbc"))
			em.setAlgorithm(AlgoNames.TRIPLE_DES);
		return em.getEncMethod();
	}// End getEncryptionMethodDoc()

	// Returns the KeyInfo Structure.
	public Document getKeyInfoDoc (String keyName) {
		GenericKeyInfo ki = new GenericKeyInfo(this.getNewDocument(),"ds", AlgoNames.XML_DSIG);
		ki.setKeyName(keyName);
		return ki.getKeyInfo();	
	}// End getKeyInfoDoc()
	
	// Returns the CipherData Structure.
	public Document getCipherDataDoc (String data) {
		CipherData cd = new CipherData(this.getNewDocument());
		cd.setValue(data);
		return cd.getCipherData();
	}// getCipherDataDoc()
	
	// In the future, all JCA/JCE related classes will be used here.
	// It will take plain text and return its encrypted form. 
	// All necessary Infromation about keys and algos will be 
	// taken from the fields representing them. 
	// For the time being it is not doing any thing.
	public String getEncryptedData(String data) {
		return "This is Cipher Data";
	}// End getEncryptedData()
	
	// This main method is only included to demonstrate functionality.
	public static void main (String args[]) {
		XmlEncryption xmlEnc = new XmlEncryption();
		xmlEnc.setFileSource("Order.xml");
		xmlEnc.setFileResult("EncryptedOrder.xml");
		xmlEnc.setAlgoName("TripleDes-cbc");
		xmlEnc.setKeyName("ImranAli"); 
		xmlEnc.setEncId("Test");
		xmlEnc.encryptCompleteXmlFile();
	}// End main()
}// End Class DemoApplication

リスト11 は、XML暗号化ファイル全体を生成するラッパー・クラスです。

リスト12 は、EncryptedData 要素を作成します。

リスト 12. Authors the EncryptedData element

リスティングを見るにはここをクリック

リスト 12. Authors the EncryptedData element

 /*
 	DW/BS
	20020204
	EncryptedData.java
	Listing 12
	Authors the EncryptedData element which is the parent of
	all XML Encryption structures except EncryptedData element.
 */

import org.w3c.dom.*;

public class EncryptedData{
	
	// Element to hold complete XML Structure.
	Element encData = null;
	Document doc = null;

	// Element will be appended in Document only once.
	private boolean elementAppendedToDoc = false;

	// Default Constructor
	public EncryptedData(Document document) {
		doc = document;
		encData = doc.createElement("EncryptedData");
		
		// Add the namespace attribute
		encData.setAttribute("xmlns","http://www.w3.org/2001/04/xmlenc#");
	}// End EncryptedData()

	// Specify the ID of this EncryptedData as there
	// can be many EncryptedData elements in the same XML file.
	public void setId(String id){
		encData.setAttribute("Id", id);
	}// End setId()
	
	// There can be three types:
	// 201 for ELEMENT
	// 202 for CONTENT
	// 303 for DOCUMENT
	public void setType(int type){
		encData.setAttribute("Type", AlgoNames.getAlgoNSValue(type));
	}// end setType()
	

	// If an Arbitrary type is to be Encrypted, specify its mimeType.
	public void setArbitraryType(String mimeType){
		String typeValue = "http://www.isi.edu/in-notes/iana/assignments/media-types/" + mimeType;
		encData.setAttribute("Type", typeValue);		
	}// End setArbitraryType()
	
	// Add any of these XML Documents KeyInfo | CipherData | Transforms | EncryptionMethod.
	public void addChild(Document document){
		NodeList nList = document.getChildNodes();

		for (int i=0 ; i<nList.getLength(); i++){
			Node tempNode = nList.item(i);
			// If it is a Root Element  
			if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
				Node importedNode = (Node)doc.importNode(tempNode, 
					true /* import all childern*/);
				encData.appendChild((Element)importedNode);
				return;
			}// end if (tempNode.getNodeType() == Node.ELEMENT_NODE) 
		}// end for (int i=0 ; i<nList.getLength(); i++)
	}// End addChild()	
	
	// Return the complete XML structure as a Document Object.
	public Document getEncData(){
		if (elementAppendedToDoc == false) {
			doc.appendChild(encData);
			elementAppendedToDoc = true;
		}
		return doc;
	}// end getEncData()
}// End class EncryptedData

リスト13 は、EncryptionMethod 要素を作成します。

リスト 13. Authors the EncryptionMethod element
 /*
 	DW/BS
	20020204
	EncryptionMethod.java
	Listing 13
	Authors the EncryptionMethod element.
 */

import org.w3c.dom.*;
import javax.xml.parsers.*;

public class EncryptionMethod{
	
	// Element to hold complete XML Structure.
	private Element encMethod = null;
	private Document doc = null;

	// Element will be appended in Document only once.
	private boolean elementAppendedToDoc = false;

	// Constructor
	public EncryptionMethod (Document document) {
		doc = document;
		encMethod = doc.createElement("EncryptionMethod");
	}// End EncryptionMethod()
	
	// Set the name of Algorithm.
	public void setAlgorithm(int algoNo){
		encMethod.setAttribute("Algorithm", AlgoNames.getAlgoNSValue(algoNo));
	}// End setAlgorithm()
	
	// Get the complete Document Sturcture.
	public Document getEncMethod() {
		if (elementAppendedToDoc == false) {
			doc.appendChild(encMethod);
			elementAppendedToDoc = true;
		}
		return doc;
	}// End getEncMethod()
}// End Class EncryptionMethod

リスト14 は、KeyInfo 要素を作成します。

リスト 14. Authors the KeyInfo element
 /*
 	DW/BS
	20020204
	GenericKeyInfo.java
	Listing 14
	Authors the KeyInfo element.
 */

import org.w3c.dom.*;

public class GenericKeyInfo {

	// Element to hold complete XML Structure.
	protected Element keyInfo = null;
	protected Document doc = null;

	// Element will be appended in Document only once.
	protected boolean elementAppendedToDoc = false;

	
	// Default constructor.
	public GenericKeyInfo (Document document, String nsQlif, int typeNo) {
		doc = document;
		
		keyInfo = doc.createElement(nsQlif + ":KeyInfo");
		
		// Add the namespace attribute.
		keyInfo.setAttribute("xmlns:" + nsQlif, AlgoNames.getAlgoNSValue(typeNo));
	}// End GenericKeyInfo
	
	// Add KeyName child in KeyInfo
	public void setKeyName(String keyName) {
		Element tempElem = doc.createElement("KeyName");
		tempElem.appendChild(doc.createTextNode(keyName));
		keyInfo.appendChild(tempElem);
	}// End setKeyName()
	
	// Add KeyValue child in KeyInfo
	public void setKeyValue (String keyValue){
		Element tempElem = doc.createElement("KeyValue");
		tempElem.appendChild(doc.createTextNode(keyValue));
		keyInfo.appendChild(tempElem);
	}// End setKeyValue()
	
	// Set retrieval Method URI and and Its type.
	public void setRetrievalMethod (String uriValue, int typeNo) {
		Element tempElem = doc.createElement("RetrievalMethod");
		tempElem.setAttribute("URI", uriValue);
		tempElem.setAttribute("Type", AlgoNames.getAlgoNSValue(typeNo));
		keyInfo.appendChild(tempElem);
	}// End setRetrievalMethod() 
	
	// Return the complete XML structure as a Document Object.
	public Document getKeyInfo() {
		if (elementAppendedToDoc == false ) {
			doc.appendChild(keyInfo);
			elementAppendedToDoc = true;
		}
	 	return doc;
	}// End getKeyInfo()
} // end class KeyInfo

リスト15 は、CipherData要素を作成します。

リスト 15. Authors the CipherData element
/*
 	DW/BS
	20020204
	CipherData.java
	Listing 15
	Authors the CipherData element.
 */

import org.w3c.dom.*;

public class CipherData {
	
	// We will create child elements within this document.
	private Document doc = null;
	
	// The main structure.
	private Element cipherData = null;

	// Element will be appended in Document only once.
	private boolean elementAppendedToDoc = false;
	
	// Constructor
	public CipherData(Document document){
		doc = document;
		cipherData = doc.createElement("CipherData");

		elementAppendedToDoc = false;
	}// End CipherData()

	// Sets the encrypted Data inside CipherValue Child tag.
	public void setValue(String value){
		Element tempElem = doc.createElement("CipherValue");
		tempElem.appendChild(doc.createTextNode(value));
		cipherData.appendChild(tempElem);
	}// End setValue()

	// Adds CipherReference element inside CipherData element.
	// Its Attribute URI is passed as parameter.
	// Transform element is optional.
	// If want to set only URI then pass NULL in place of transforms Element
	public void setCipherReference (String uriValue, Element transforms) {
		Element tempElem = doc.createElement("CipherReference");
		tempElem.setAttribute("URI", uriValue);
		
		if ( transforms != null )
			tempElem.appendChild(transforms);
		
		cipherData.appendChild(tempElem);
	}// End setCipherReference()
	
	// Retuns the completed CipherData structure.
	public Document getCipherData() {
		if (elementAppendedToDoc == false ) {
			doc.appendChild(cipherData);
			elementAppendedToDoc = true;
		}
		return doc;
	}// End getCipherData()
}// end Class CipherData

リスト16 には、アルゴリズムの名前 (静的整数) とそれに対応する名前空間 (ストリング) が含まれています。

リスト 16. AlgoNames.java

リスティングを見るにはここをクリック

リスト 16. AlgoNames.java

 /*
 	DW/BS
	20020204
	AlgoNames.java
	Listing 16
	This class is only for convenience of use. 
	It contains names of Algorithms as static integers
	and their corresponding namespaces as strings.
 */

public class AlgoNames {

	public static final int TRIPLE_DES    =  1; 
	public static final int AES_128	      =  2; 
	public static final int	AES_256	      =  3;  
	public static final int RSA_V1_5      =  4;  
	public static final int RSA_OAEP      =  5;  
	public static final int DH		      =  6; 
	public static final int	KW_TRIPLE_DES =  7;   
	public static final int KW_AES_128    =  8;  
	public static final int	KW_AES_256    =  9;  
	public static final int	SHA1   	      = 10; 
	public static final int	SHA256        = 11; 

	public static final int	XPATH         = 100; 
	public static final int BASE_64		  = 101;
	public static final int ENC_KEY		  = 102;
	public static final int XML_DSIG	  = 103;

	// Fields to represent what type of Data to be encrypted.
	public static final int ELEMENT   	  = 201; 
	public static final int CONTENT   	  = 202; 
	public static final int DOCUMENT  	  = 203; 
	
	
	
	public static String getAlgoNSValue(int algoNo){
		String algoNS = "";
		switch (algoNo){
			case TRIPLE_DES :  algoNS = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"; break;
			case AES_128	:  algoNS = "http://www.w3.org/2001/04/xmlenc#aes128-cbc";    break;
			case AES_256	:  algoNS = "http://www.w3.org/2001/04/xmlenc#aes256-cbc";    break;
			case RSA_V1_5	:  algoNS = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"; 	  break;
			case RSA_OAEP	:  algoNS = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p";break;
			case DH			:  algoNS = "http://www.w3.org/2001/04/xmlenc#dh";			  break;
			case KW_TRIPLE_DES:algoNS = "http://www.w3.org/2001/04/xmlenc#kw-tripledes";  break;
			case KW_AES_128	:  algoNS = "http://www.w3.org/2001/04/xmlenc#kw-aes128";	  break;
			case KW_AES_256	:  algoNS = "http://www.w3.org/2001/04/xmlenc#kw-aes256";	  break;
			case SHA1		:  algoNS = "http://www.w3.org/2000/09/xmldsig#sha1"; 		  break;
			case SHA256		:  algoNS = "http://www.w3.org/2000/09/xmldsig#sha256";		  break;
			case XPATH		:  algoNS = "http://www.w3.org/TR/1999/REC-xpath-19991116";   break;
			case BASE_64	:  algoNS = "http://www.w3.org/2000/09/xmldsig#base64";		  break;
			case ENC_KEY	:  algoNS = "http://www.w3.org/2001/04/xmlenc#EncryptedKey";  break;
			case XML_DSIG	:  algoNS = "http://www.w3.org/2000/09/xmldsig#";		 	  break;	
			case ELEMENT	:  algoNS = "http://www.w3.org/2001/04/xmlenc#Element";		  break;	
			case CONTENT	:  algoNS = "http://www.w3.org/2001/04/xmlenc#Content";		  break;	
			case DOCUMENT	:  algoNS = "http://www.isi.edu/in-notes/iana/assignments/media-types/text/xml";  break;
	
		} // end switch(algoNo)
		return algoNS;
	}// End getAlgoNsValue()
}// End Class

XmlEncryption クラス (リスト11) には、さまざまなpublic get/setメソッドが含まれています。ユーザーは、setメソッドを呼び出すことによって、次のような暗号化パラメーターを指定します。

  1. 暗号化するファイルの名前
  2. 結果出力先のXML暗号化ファイルの名前
  3. 暗号化のアルゴリズムの名前
  4. 暗号化のために使用する鍵の名前
  5. <EncryptedData> 構造の識別用ID

main () メソッドでXmlEncryption クラスを使う方法については、すでに示しました (リスト11)。main () メソッドでは、このクラスのインスタンスを作成します。そのコンストラクターではDOMのインスタンスを生成し、基本となるクラスのすべてが同じオブジェクトを使用するようにします。

この記事の中で、鍵に関連した3種類の技術用語、つまり「公開鍵」、「私有鍵」、そして「共通鍵」が使用されています。これらの用語は、エンドツーエンド・セキュリティーを扱う開発者にとってはおなじみの語ですが、XMLの開発者にとっては、あまりなじみがないかもしれません。これらの用語について説明しておきましょう。

公開鍵と私有鍵: これらの用語は対で使用されます。アルゴリズムによっては、公開鍵と私有鍵の対を生成するものがあります。暗号化データを交換したい相手に対しては、まず公開鍵を送ります。その公開鍵によって暗号化できるデータは、サイズが限られています。通信の相手は、こちらが送った公開鍵を使用してデータを暗号化し、その結果を送り返しますそれを受け取ったなら、私有鍵を使ってそのデータを暗号化解除します。これは非対称暗号化です。

共通鍵: 共通鍵の交換には公開鍵と私有鍵を使用します。通常、共通鍵はランダムに生成されます。非対称暗号化によって共通鍵を相手と交換したなら、それ以降は、両方の側でその鍵を使用してデータを暗号化します。これは対称暗号化です。

この実装でサポートされているのは、リスト2 の場合のようなファイル全体の暗号化だけです。これはEncryptCompleteXmlFile () メソッドによって実行されます。そのメソッドの中では、次のメソッドが順に呼び出されます。

  1. GetEncryptedDataDoc()EncryptedData クラス (リスト12) のオブジェクトを戻します。それには、EncryptedData 要素の構造が含まれます。
  2. GetEncryptionMethodDoc() が、EncryptionMethod 要素に対応するXML構造を含むDocumentオブジェクトを戻します。GetEncryptionMethodDoc() は、EncryptionMethod クラス (リスト13) を使用してXMLを作成します。
  3. GetKeyInfoDoc() が、Document オブジェクトを戻します。それには、KeyInfo要素に対応するXML構造が含まれます。GetKeyInfoDoc() は、GenericKeyInfo クラス (リスト14) のオブジェクトを使用してXMLを作成します。このクラスには、最低限必要な機能しかありません (KeyName 要素とKeyValue 要素のサポート)。それで、機能全体を提供するため、GenericKeyInfo クラスから継承することになります。それには、X509証明書、PGPデータなどのサポートが含まれています。
  4. ReadFile() によって、暗号化するデータ (XMLファイル全体) を取り出します。
  5. 今のところ、GetEncryptedData() は何もしません。このメソッドは、このシリーズの第2回の記事で実装します。これは、ステップ4で取り出したXMLデータの暗号化形式を作成するためにサポートされています。使用する暗号化ストラテジーについては、最後のセクション (「Java Cryptographic Architecture」) で簡単に説明します。
  6. GetCipherDataDoc() は、暗号化データを引き数とし、CipherData 要素を含むDocumentオブジェクトを戻します。GetCipherDataDoc() は、CipherData クラス (リスト12) のオブジェクトを使用することによってXMLを作成します。
  7. 最後に、EncryptedData (リスト15) のオブジェクトのaddChild() メソッドを3回呼び出しています。それぞれステップ2、3、および6のDocumentオブジェクトを引き数に指定し、それらをその親である<EncryptedData> 構造に追加します。
  8. SaveEncryptedFile() によって、XML暗号化ファイル全体を保存します。

AlgoNames (リスト16) は、XML暗号化で必要な名前空間宣言を指定するだけのヘルパー・クラスです。

XmlEncryption クラス (リスト11) は、サーバー・サイド・コンポーネントとしても使用できます。このシリーズの第2回では、内部で独立して使用する方法とサーバー・サイド・アプリケーションとして使用する方法を示します。

ここで開発したクラスは、DOMベースのXMLオーサリングを実行するだけです。実際には、暗号化機能の実装も必要になります。ここで、暗号化サポートのためのストラテジーを構成することにします。そのためには、Java Cryptographic Architecture (JCA) について調べる必要があります。


Java Cryptographic Architecture (JCA)

Javaには、完全に揃った暗号化のサポートが含まれています。J2SEの中には、そのためのパッケージがいくつか含まれており、それによって、アクセス制御、シグニチャー、証明書、鍵ペア、鍵の保管、およびメッセージ・ダイジェストなど、セキュリティー・アーキテクチャーの主要な機能のすべてがカバーされています。

JCA設計の主要な原則となっているのは、暗号の概念とアルゴリズムの実装を切り離すことにより、JCAフレームワーク内で、異なるベンダーがそれぞれのツールを提供できるようにすることです。

JCAエンジン・クラス

JCAでは、それぞれが暗号化機能を提供する一連のエンジン・クラスが定義されています。たとえば、MD (メッセージ・ダイジェスト) アルゴリズムの複数の異なる標準規格があります。実装の点でそれらの標準規格はすべて異なっていますが、エンジンAPIのレベルではすべて同じです。異なるベンダーが、それぞれ特定のアルゴリズムの実装を提供することが可能です。

Java Cryptographic Extension (JCE)

暗号アルゴリズムの独立した (サード・パーティー) ベンダー実装は、Java Cryptographic Extension (JCE) と呼ばれます。Sun Microsystemsも、JCEの実装を提供しています。JCEを使用する場合には、それをJCAによって構成する必要があります。そのためには、次のことをする必要があります。

1.CLASSPATH 環境変数の中に、プロバイダーを構成するためのjarファイルのアドレスを追加する (JCE実装はすべてプロバイダーと呼ばれます)。

2. java.securityファイルを編集することによって、承認済みプロバイダーのリストの中でプロバイダーを構成する。このファイルは、JavaHome/jre/lib/securityフォルダーにあります。優先順位指定のための構文は、security.provider.<n>=<masterClassName> というものです。ここで、nは優先順位番号 (1、2、3、など) です。MasterClassName は、特定のアルゴリズム実装のためにエンジン・クラスが呼び出すマスター・クラスの名前です。マスター・クラスの名前は、プロバイダーのドキュメンテーションで指定されます。たとえば、java.securityファイルの中の次の項目について考慮してみましょう。

  • security.provider.1=sun.security.provider.Sun
  • security.provider.2=com.sun.rsajca.Provider
  • security.provider.3=com.sun.net.ssl.internal.ssl.Provider

これらの項目は、エンジン・クラスがアルゴリズム実装を上記の順序で検索する、ということを示しています。最初に見つかった実装が実行されます。これらの簡単なステップを実行したなら、XML暗号化アプリケーションの中でJCA/JCEを使用することになります。

XML暗号化実装例でJCAおよびJCEを使う

JCA/JCEに関連したすべての処理を実行する場所は、ラッパー・クラスXmlEncryption (リスト11) のGetEncryptedData() 関数です。今のところ、このメソッドは "This is Cipher Data" というストリングを戻すだけです。JCA/JCE関連のクラスは、まだ作成していません。このメソッドは、暗号化前のデータを引き数とし、それを暗号化したストリングを戻します。JCA/JCEのラッパー・クラスの作成後、アルゴリズムや鍵に関連したことは、すべてこのメソッドの中で処理することになります。

次回: このシリーズの第2回では、暗号化の詳細を検討し、それを実装します。暗号化および暗号化解除のクラスの動作や解析ロジックとの対話について示し、さらに、Web ServicesにおけるXML暗号化の応用について紹介します。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=297341
ArticleTitle=XML暗号化の方法を探る:第1回
publish-date=03012002