このドキュメントでは、Java XMLデジタル署名API仕様(JSR 105)について説明します。このJSRの目的は、XML署名を生成および検証するための標準のJava APIを定義することです。
この仕様が完成すると、このAPIの機能について説明するリファレンス実装が作成されることになり、この仕様のオペレーションの定義が提供されます。この仕様の実装に対応しているかどうかを確認するためのTechnology Compatibility Kit (TCK)も使用可能になります。Java Community Process 2.1に従ってこれらが必要になります。
JSR 105 APIは、次の2種類のユーザーをターゲットにしています。
JSR 105 APIを使用してXML署名を生成および検証するJavaプログラマ。
JSR 105 APIの固定実装を作成し、JCAプロバイダの暗号化サービスとしてこの実装を登録するJavaプログラマ(Providerクラスを参照)。
JSR 105エキスパート・グループ:
Nicolas Catania、HP社
Donald E.Eastlake 3rd、Motorola社
Christian Geuer-Pollmann、Apache Software Foundation
Hans Granqvist、VeriSign社
Kazuyuki Harada、富士通
Anthony Ho、DSTC社
Merlin Hughes、Baltimore Technologies
Joyce Leung、IBM社
Gregor Karlinger、IAIK
Serge Mister、Entrust Technologies
Takuya Mori、NEC Corporation
Sean Mullan、Sun Microsystems (共同仕様リード)
Anthony Nadalin、IBM (共同仕様リード)
Erwin van der Koogh、Apache Software Foundation
Chris Yeung、XML Asia
また、次のメンバーにも感謝を捧げます。Sun MicrosystemsのValerie Peng、Vincent Ryan、Sharon Liu、Chok Poh、K. Venugopal Rao.、Paul Rank、Alexey Gavrilov、Bill Situ、Eric Jendrock、Andrew Fan、Manveen Kaur、Tom Amiro、Michael Mi、Dmitri Silaev、Roman Makarchuk、Vanitha Venkatraman, Arkadiy Sutchilin, Scott Fordin、ApacheのVishal Mahajan、IAIKのMartin Centner。
このドキュメントのキーワード「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」、および「OPTIONAL」は、RFC 2119に説明されているとおりに解釈されます。
APIを使用して、プログラマはW3C勧告で規定されているSHOULDおよびMUSTのすべての要件を満たすXML署名を生成および検証できる必要があります。
APIを使用して、W3C勧告で規定されているSHOULDおよびMUSTのすべての要件を満たすAPIの実装を作成できる必要があります。
実装は、W3C Recommendation, XML-Signature XPath Filter Transform 2.0をサポートする必要があります。
実装は、W3C Recommendation, Exclusive XML Canonicalization Version 1.0をサポートする必要があります。
DOMに依存しないAPI。APIは、DOMなどの特定のXML表現に依存していない必要があります。DOM、JDOM、dom4jなど、様々なXML処理およびメカニズム表現のためのAPIの実装を作成できる必要があります。
拡張可能なプロバイダ・ベースのAPI。サード・パーティが、暗号化アルゴリズムや変換アルゴリズムの管理や作成、URIの間接参照、およびXMLのオブジェクトの整列化を行う実装を作成およびプラグインすることができる必要があります。
デフォルトのXMLメカニズム・タイプDOMのサポート。実装は、デフォルトのメカニズム・タイプDOMを最低限サポートする必要があります。これにより、JSR 105のすべての実装で最低レベルの機能性が保証されます。実装は、ほかのメカニズム・タイプもサポートできます。
デフォルトのXMLメカニズム・タイプDOMの相互運用性。APIは、DOM実装を使用するアプリケーションで移植性および相互運用性を保証する必要があります。
J2SEの要件。このテクノロジの実装はJ2SE 1.2以降をサポートできますが、最低限J2SEのバージョン1.4をサポートする必要があります。
非DOM実装のサポート。APIでは非DOM実装を作成できる必要がありますが、DOM以外の実装間での相互運用性を保証することは最初のバージョンの範囲外です。追加の標準サービス・プロバイダ・タイプが今後追加されたり、JSR 105の保守バージョンで必要なAPIの拡張機能が検討される可能性があります。
高レベルのAPIのサポート。プログラマは、JSR 105 APIに基づいて構築される高レベルのAPIを設計して、低レベルの詳細を非表示にしたり、共通のユース・ケースに対処したり、プロファイリングの制限を適用できます。ただし、これらの要件をサポートすることは最初のバージョンの範囲外です。高レベルのAPIは、JSR 105の保守リリースで考慮される可能性があります。
ユーザープラガブル・アルゴリズム(javax.xml.crypto.dsig.TransformServiceクラスでサポートされている変換アルゴリズムおよび正規化アルゴリズム以外)のサポート。開発者が完全なJSR 105実装を作成せずに独自のXML署名アルゴリズム実装をプラグインできるようにすることは、有用なことだと考えられますが、JSR 105のこのリリースでは必要ありません。Java SEの後続リリースでは、基礎となるJCA/JCEを拡張して、XMLセキュリティ・アルゴリズム、パラメータ、鍵情報の登録、解析および処理のサポートを追加するソリューションを検討しています。
JSR 105 APIは、次の6つのパッケージで構成されています。
javax.xml.cryptoパッケージには、XML署名の生成やXMLデータの暗号化など、XML暗号化操作を行う場合に使用する一般的なクラスが含まれています。このパッケージ内の主要なクラスには次の2つがあります。KeySelectorクラスを使用すると、開発者は、KeyInfoオブジェクトに含まれている情報を使用して鍵を検出して任意に検証する実装を提供できます。URIDereferencerクラスを使用すると、開発者は、実装を間接参照する独自のURIを作成および指定できます。
javax.xml.crypto.dsigパッケージには、W3C XMLデジタル署名仕様で定義されているコア要素を表すインタフェースが含まれています。最も重要なのはXMLSignatureクラスです。このクラスを使用すると、XMLデジタル署名の署名および検証を行うことができます。ほとんどのXML署名構造または要素は、対応するインタフェースによって表されます(KeyInfo構造を除きます。これは独自のパッケージに含まれており、次の段落で説明します)。これらのインタフェースには、SignedInfo、CanonicalizationMethod、SignatureMethod、Reference、Transform、DigestMethod、XMLObject、Manifest、SignaturePropertyおよびSignaturePropertiesなどがあります。XMLSignatureFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。
javax.xml.crypto.dsig.keyinfoパッケージには、KeyInfo、KeyName、KeyValue、X509Data、X509IssuerSerial、RetrievalMethodおよびPGPDataなど、W3C XMLデジタル署名勧告で定義されているほとんどのKeyInfo構造を表すインタフェースが含まれています。KeyInfoFactoryクラスは、これらのインタフェースを実装するオブジェクトを作成する場合に使用する抽象ファクトリです。
javax.xml.crypto.dsig.specパッケージには、ダイジェスト、署名、変換、またはXML署名の処理で使用される正規化アルゴリズム用の入力パラメータを表すインタフェースおよびクラスが含まれています。
最後に、javax.xml.crypto.domおよびjavax.xml.crypto.dsig.domパッケージには、javax.xml.cryptoおよびjavax.xml.crypto.dsigパッケージ用のDOM固有のクラスがそれぞれ含まれています。DOMベースのXMLSignatureFactoryまたはKeyInfoFactory実装を作成または使用する開発者およびユーザーのみが、これらのパッケージを直接使用する必要があります。
JSR 105暗号化サービスは、XMLSignatureFactoryおよびKeyInfoFactory抽象クラスの固定実装であり、XML署名やKeyInfo構造を解析、生成および検証するオブジェクトやアルゴリズムを作成します。XMLSignatureFactoryの固定実装は、XML署名に関するW3C勧告に規定されているように、必要なそれぞれのアルゴリズムをサポートしている必要があります。この固定実装は、W3C勧告やほかの仕様に規定されているように、ほかのアルゴリズムをサポートできます。
JSR 105は、JCAプロバイダ・モデルを使用して(Providerクラスを参照)、XMLSignatureFactoryおよびKeyInfoFactory実装を登録およびロードします。
固定実装XMLSignatureFactoryまたはKeyInfoFactoryはそれぞれ、XML署名やKeyInfo構造を解析および生成するときに実装によって内部で使用されるXML処理メカニズムを識別する、特定のXMLメカニズム・タイプをサポートしています。このJSRは標準タイプDOMをサポートしています。新しい標準タイプ(JDOMなど)のサポートは、今後追加される可能性があります。
JSR 105実装は、java.security.Signatureやjava.security.MessageDigestなど、ベースとなるJCAエンジン・クラスを使用して暗号化操作を行う必要があります。
XMLSignatureFactoryおよびKeyInfoFactoryクラス以外に、JSR 105は、変換および正規化アルゴリズム用のサービス・プロバイダ・インタフェースもサポートしています。TransformServiceクラスを使用すると、特定のXMLメカニズム・タイプ用の固有の変換または正規化アルゴリズムの実装を開発およびプラグインできます。TransformServiceクラスは、実装を登録およびロードするときに標準JCAプロバイダ・モデルを使用します。各JSR 105実装はTransformServiceクラスを使用して、生成または検証しているXML署名で変換アルゴリズムおよび正規化アルゴリズムをサポートするプロバイダを見つける必要があります。
相互運用性の問題を最小限にするために、DOMベースのXMLSignatureFactory、KeyInfoFactory、またはTransformServiceを実装する場合は、次の要件に従う必要があります。
XMLSignatureFactoryのunmarshalXMLSignatureメソッドは、DOMValidateContext型をサポートする必要があります。型がDOMValidateContextの場合は、Signature型の要素を含む必要があります。また、unmarshalXMLSignatureメソッドは渡されるDOMValidateContextのID/要素マッピングを生成できます。
XMLSignatureFactoryによって生成されるXMLSignatureのsignメソッドはDOMSignContext型をサポートし、validateメソッドはDOMValidateContext型をサポートする必要があります。この要件は、SignatureValueのvalidateメソッド、およびReferenceのvalidateメソッドにも当てはまります。
実装は、アプリケーションが拡張可能なコンテンツ(任意の要素または混合コンテンツ)を指定するためのメカニズムとして、DOMStructureをサポートする必要があります。
ユーザー指定のURIDereferencerのdereferenceメソッドがNodeSetDataオブジェクトを返す場合、iteratorメソッドは型org.w3c.dom.Nodeのオブジェクトの繰返しを返す必要があります。
ユーザー指定のURIDereferencersのdereferenceメソッドに渡されたURIReferenceオブジェクトは型DOMURIReferenceである必要があり、XMLCryptoContextオブジェクトはDOMCryptoContextを実装する必要があります。
直前の2つの要件は、XMLSignatureFactoryおよびKeyInfoFactoryのgetURIDereferencerメソッドによって返されるURIDereferencersにも当てはまります。
KeyInfoFactoryのunmarshalKeyInfoメソッドはDOMStructure型をサポートする必要があります。型がDOMStructureの場合は、KeyInfo型の要素を含む必要があります。
TransformのtransformメソッドはDOMCryptoContextコンテキスト・パラメータ型をサポートする必要があります。
XMLSignatureFactoryのnewtransformおよびnewCanonicalizationMethodメソッドはDOMStructureパラメータ型をサポートする必要があります。
TransformServiceのinitおよびmarshalParamsメソッドはDOMStructureおよびDOMCryptoContext型をサポートする必要があります。
XMLSignatureFactoryのunmarshalXMLSignatureメソッドは、DOMStructure型をサポートする必要があります。型がDOMStructureの場合は、Signature型の要素を含む必要があります。
KeyInfoのmarshalメソッドはDOMStructureおよびDOMCryptoContextパラメータ型をサポートする必要があります。
DOM実装は、相互運用性に影響しない場合、DOM以外のXML解析APIを内部的に使用できます。たとえば、XMLSignatureFactoryのDOM実装は、内部的にSAXパーサーを使用してデータを正規化することができます。
オープンAPIの問題は、次のとおりです。
外部XMLドキュメント参照のID属性の登録がサポートされていません。次の参照について検討してみます。
外部ドキュメントの間接参照によってオクテット・ストリームが生成され、それがJSR 105実装によってNodeSetに変換されています。ただし、APIでは外部ドキュメントのID属性を登録するためのメカニズムが提供されていないため、XPath変換実装は「foo」IDを識別できない可能性があります。
<Reference URI="document.xml"> <Transforms> <Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <XPath>id("foo")</XPath> </Transform> </Transforms> </Reference>
次の例1 - 3では、JSR 105 APIを使用してさまざまなタイプの単純なXMLデジタル署名を生成する方法について説明します。例1では、DSA署名アルゴリズムを使用して分離署名を生成する方法について説明します。例2では、エンベロープされた署名を生成する方法について説明します。例3では、エンベロープされた署名を生成する方法について説明します。例4では、XML署名を検証する方法について説明します。
例11-1 1.分離されたXMLデジタル署名の生成
import javax.xml.crypto.*; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dom.*; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.*; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import java.io.FileOutputStream; import java.io.OutputStream; import java.security.*; import java.util.Collections; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; /** * This is a simple example of generating a Detached XML * Signature using the JSR 105 API. The resulting signature will look * like (key and signature values will be different): * * <pre><code> * <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> * <SignedInfo> * <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> * <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/> * <Reference URI="http://www.w3.org/TR/xml-stylesheet"> * <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> * <DigestValue>60NvZvtdTB+7UnlLp/H24p7h4bs=</DigestValue> * </Reference> * </SignedInfo> * <SignatureValue> * DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ== * </SignatureValue> * <KeyInfo> * <KeyValue> * <DSAKeyValue> * <P> * rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL * FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q * UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8= * </P> * <Q> * kEjAFpCe4lcUOdwphpzf+tBaUds= * </Q> * <G> * oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2 * SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4 * BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw= * </G> * <Y> * pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv * AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg * jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k= * </Y> * </DSAKeyValue> * </KeyValue> * </KeyInfo> * </Signature> * </code></pre> */ public class GenDetached { // // Synopsis: java GenDetached [output] // // where output is the name of the file that will contain the detached // signature. If not specified, standard output is used. // public static void main(String[] args) throws Exception { // First, create a DOM XMLSignatureFactory that will be used to // generate the XMLSignature and marshal it to DOM. XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); // Create a Reference to an external URI that will be digested // using the SHA256 digest algorithm Reference ref = fac.newReference("http://www.w3.org/TR/xml-stylesheet", fac.newDigestMethod(DigestMethod.SHA256, null)); // Create the SignedInfo SignedInfo si = fac.newSignedInfo( fac.newCanonicalizationMethod (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null), fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null), Collections.singletonList(ref)); // Create a DSA KeyPair KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(2048); KeyPair kp = kpg.generateKeyPair(); // Create a KeyValue containing the DSA PublicKey that was generated KeyInfoFactory kif = fac.getKeyInfoFactory(); KeyValue kv = kif.newKeyValue(kp.getPublic()); // Create a KeyInfo and add the KeyValue to it KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); // Create the XMLSignature (but don't sign it yet) XMLSignature signature = fac.newXMLSignature(si, ki); // Create the Document that will hold the resulting XMLSignature DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); // must be set Document doc = dbf.newDocumentBuilder().newDocument(); // Create a DOMSignContext and set the signing Key to the DSA // PrivateKey and specify where the XMLSignature should be inserted // in the target document (in this case, the document root) DOMSignContext signContext = new DOMSignContext(kp.getPrivate(), doc); // Marshal, generate (and sign) the detached XMLSignature. The DOM // Document will contain the XML Signature if this method returns // successfully. signature.sign(signContext); // output the resulting document OutputStream os; if (args.length > 0) { os = new FileOutputStream(args[0]); } else { os = System.out; } TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); trans.transform(new DOMSource(doc), new StreamResult(os)); } }
例11-2 2.エンベロープされたXMLデジタル署名の生成
import javax.xml.crypto.*; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dom.*; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.*; import javax.xml.crypto.dsig.spec.*; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStream; import java.security.*; import java.util.Collections; import java.util.Iterator; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; /** * This is a simple example of generating an Enveloped XML * Signature using the JSR 105 API. The resulting signature will look * like (key and signature values will be different): * * <pre><code> *<Envelope xmlns="urn:envelope"> * <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> * <SignedInfo> * <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> * <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/> * <Reference URI=""> * <Transforms> * <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> * </Transforms> * <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> * <DigestValue>K8M/lPbKnuMDsO0Uzuj75lQtzQI=<DigestValue> * </Reference> * </SignedInfo> * <SignatureValue> * DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ== * </SignatureValue> * <KeyInfo> * <KeyValue> * <DSAKeyValue> * <P> * rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL * FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q * UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8= * </P> * <Q> * kEjAFpCe4lcUOdwphpzf+tBaUds= * </Q> * <G> * oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2 * SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4 * BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw= * </G> * <Y> * pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv * AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg * jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k= * </Y> * </DSAKeyValue> * </KeyValue> * </KeyInfo> * </Signature> *</Envelope> * </code></pre> */ public class GenEnveloped { // // Synopsis: java GenEnveloped [document] [output] // // where "document" is the name of a file containing the XML document // to be signed, and "output" is the name of the file to store the // signed document. The 2nd argument is optional - if not specified, // standard output will be used. // public static void main(String[] args) throws Exception { // Create a DOM XMLSignatureFactory that will be used to generate the // enveloped signature XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); // Create a Reference to the enveloped document (in this case we are // signing the whole document, so a URI of "" signifies that) and // also specify the SHA256 digest algorithm and the ENVELOPED Transform. Reference ref = fac.newReference ("", fac.newDigestMethod(DigestMethod.SHA256, null), Collections.singletonList (fac.newTransform (Transform.ENVELOPED, (TransformParameterSpec) null)), null, null); // Create the SignedInfo SignedInfo si = fac.newSignedInfo (fac.newCanonicalizationMethod (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null), fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null), Collections.singletonList(ref)); // Create a DSA KeyPair KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(2048); KeyPair kp = kpg.generateKeyPair(); // Create a KeyValue containing the DSA PublicKey that was generated KeyInfoFactory kif = fac.getKeyInfoFactory(); KeyValue kv = kif.newKeyValue(kp.getPublic()); // Create a KeyInfo and add the KeyValue to it KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); // Instantiate the document to be signed DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(args[0])); // Create a DOMSignContext and specify the DSA PrivateKey and // location of the resulting XMLSignature's parent element DOMSignContext dsc = new DOMSignContext (kp.getPrivate(), doc.getDocumentElement()); // Create the XMLSignature (but don't sign it yet) XMLSignature signature = fac.newXMLSignature(si, ki); // Marshal, generate (and sign) the enveloped signature signature.sign(dsc); // output the resulting document OutputStream os; if (args.length > 1) { os = new FileOutputStream(args[1]); } else { os = System.out; } TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); trans.transform(new DOMSource(doc), new StreamResult(os)); } }
例11-3 3.エンベロープするXMLデジタル署名の生成
import javax.xml.crypto.*; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dom.*; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.*; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStream; import java.security.*; import java.util.Arrays; import java.util.Collections; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * This is a simple example of generating an Enveloping XML * Signature using the JSR 105 API. The signature in this case references a * local URI that points to an Object element. * The resulting signature will look like (certificate and * signature values will be different): * * <pre><code> * <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> * <SignedInfo> * <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/> * <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha256"/> * <Reference URI="#object"> * <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> * <DigestValue>7/XTsHaBSOnJ/jXD5v0zL6VKYsk=</DigestValue> * </Reference> * </SignedInfo> * <SignatureValue> * RpMRbtMHLa0siSS+BwUpLIEmTfh/0fsld2JYQWZzCzfa5kBTz25+XA== * </SignatureValue> * <KeyInfo> * <KeyValue> * <DSAKeyValue> * <P> * /KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0Imbz * RMqzVDZkVG9xD7nN1kuFw== * </P> * <Q> * li7dzDacuo67Jg7mtqEm2TRuOMU= * </Q> * <G> * Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOH * CBiNU0NogpsQW5QvnlMpA== * </G> * <Y> * wbEUaCgHZXqK4qLvbdYrAc6+Do0XVcsziCJqxzn4cJJRxwc3E1xnEXHscVgr1Cql9 * i5fanOKQbFXzmb+bChqig== * </Y> * </DSAKeyValue> * </KeyValue> * </KeyInfo> * <Object Id="object">some text</Object> * </Signature> * * </code></pre> */ public class GenEnveloping { // // Synopis: java GenEnveloping [output] // // where "output" is the name of a file that will contain the // generated signature. If not specified, standard ouput will be used. // public static void main(String[] args) throws Exception { // First, create the DOM XMLSignatureFactory that will be used to // generate the XMLSignature XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); // Next, create a Reference to a same-document URI that is an Object // element and specify the SHA256 digest algorithm Reference ref = fac.newReference("#object", fac.newDigestMethod(DigestMethod.SHA256, null)); // Next, create the referenced Object DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().newDocument(); Node text = doc.createTextNode("some text"); XMLStructure content = new DOMStructure(text); XMLObject obj = fac.newXMLObject (Collections.singletonList(content), "object", null, null); // Create the SignedInfo SignedInfo si = fac.newSignedInfo( fac.newCanonicalizationMethod (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null), fac.newSignatureMethod("http://www.w3.org/2000/09/xmldsig#dsa-sha256", null), Collections.singletonList(ref)); // Create a DSA KeyPair KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(2048); KeyPair kp = kpg.generateKeyPair(); // Create a KeyValue containing the DSA PublicKey that was generated KeyInfoFactory kif = fac.getKeyInfoFactory(); KeyValue kv = kif.newKeyValue(kp.getPublic()); // Create a KeyInfo and add the KeyValue to it KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); // Create the XMLSignature (but don't sign it yet) XMLSignature signature = fac.newXMLSignature(si, ki, Collections.singletonList(obj), null, null); // Create a DOMSignContext and specify the DSA PrivateKey for signing // and the document location of the XMLSignature DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc); // Lastly, generate the enveloping signature using the PrivateKey signature.sign(dsc); // output the resulting document OutputStream os; if (args.length > 0) { os = new FileOutputStream(args[0]); } else { os = System.out; } TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); trans.transform(new DOMSource(doc), new StreamResult(os)); } }
例11-4 4.XMLデジタル署名の検証
import javax.xml.crypto.*; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dom.*; import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.keyinfo.*; import java.io.FileInputStream; import java.security.*; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * This is a simple example of validating an XML * Signature using the JSR 105 API. It assumes the key needed to * validate the signature is contained in a KeyValue KeyInfo. */ public class Validate { // // Synopsis: java Validate [document] // // where "document" is the name of a file containing the XML document // to be validated. // public static void main(String[] args) throws Exception { // Instantiate the document to be validated DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(args[0])); // Find Signature element NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); if (nl.getLength() == 0) { throw new Exception("Cannot find Signature element"); } // Create a DOM XMLSignatureFactory that will be used to unmarshal the // document containing the XMLSignature XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); // Create a DOMValidateContext and specify a KeyValue KeySelector // and document context DOMValidateContext valContext = new DOMValidateContext (new KeyValueKeySelector(), nl.item(0)); // unmarshal the XMLSignature XMLSignature signature = fac.unmarshalXMLSignature(valContext); // Validate the XMLSignature (generated above) boolean coreValidity = signature.validate(valContext); // Check core validation status if (coreValidity == false) { System.err.println("Signature failed core validation"); boolean sv = signature.getSignatureValue().validate(valContext); System.out.println("signature validation status: " + sv); // check the validation status of each Reference Iterator i = signature.getSignedInfo().getReferences().iterator(); for (int j=0; i.hasNext(); j++) { boolean refValid = ((Reference) i.next()).validate(valContext); System.out.println("ref["+j+"] validity status: " + refValid); } } else { System.out.println("Signature passed core validation"); } } /** * KeySelector which retrieves the public key out of the * KeyValue element and returns it. * NOTE: If the key algorithm doesn't match signature algorithm, * then the public key will be ignored. */ private static class KeyValueKeySelector extends KeySelector { public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException { if (keyInfo == null) { throw new KeySelectorException("Null KeyInfo object!"); } SignatureMethod sm = (SignatureMethod) method; List list = keyInfo.getContent(); for (int i = 0; i < list.size(); i++) { XMLStructure xmlStructure = (XMLStructure) list.get(i); if (xmlStructure instanceof KeyValue) { PublicKey pk = null; try { pk = ((KeyValue)xmlStructure).getPublicKey(); } catch (KeyException ke) { throw new KeySelectorException(ke); } // make sure algorithm is compatible with method if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) { return new SimpleKeySelectorResult(pk); } } } throw new KeySelectorException("No KeyValue element found!"); } //@@@FIXME: this should also work for key types other than DSA/RSA static boolean algEquals(String algURI, String algName) { if (algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase("http://www.w3.org/2000/09/xmldsig#dsa-sha256")) { return true; } else if (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase("http://www.w3.org/2000/09/xmldsig#dsa-sha256")) { return true; } else { return false; } } } private static class SimpleKeySelectorResult implements KeySelectorResult { private PublicKey pk; SimpleKeySelectorResult(PublicKey pk) { this.pk = pk; } public Key getKey() { return pk; } } }