SECURITY / XML
XMLデジタル署名とは?3つの署名形式と改ざん検知の仕組みを図解
XML文書の「どの部分に、誰が署名したか」を明確にするための技術を、身近な例から理解します。
まずイメージ:オンラインの申請書に付ける「改ざん防止の印」
役所に送る申請書の「申請金額」だけを、提出者が秘密鍵で署名するとします。受信側は公開鍵で署名を検証し、その項目が署名後に変えられていないか確認できます。XMLデジタル署名なら、文書全体だけでなく特定の要素も対象にできます。
そもそもデジタル署名は何を保証する?
デジタル署名は、データから計算したハッシュ値などに秘密鍵による署名演算を行い、受信側が公開鍵で検証する仕組みです。主な役割は、内容が変わっていないこと(完全性)と、対応する秘密鍵で署名されたことの確認です。
ただし、公開鍵が本当に本人のものかを確認するには、証明書や鍵の配布経路などの信頼確認が必要です。また、デジタル署名そのものは暗号化ではないため、文書の内容を秘密にする機能はありません。
XMLデジタル署名(XMLDSig)とは
XMLDSigは、XML形式でデジタル署名を表現し、署名対象をReferenceで指定する標準技術です。たとえば、複数の項目がある注文書で、金額や振込先といった重要な要素を参照して署名できます。
署名対象はXML文書全体、文書内の特定要素、別のリソースなどに設定できます。XMLのどこに署名データを置くかと、何を署名対象とするかは分けて考えることが大切です。
3つの署名形式:違いは「入れ子の位置」
署名と対象が親子関係にならず、別々に存在する。同じXML文書内の別要素も参照できる。
署名対象の中にSignatureを置く。検証時には通常、署名要素自身を除外する変換を用いる。
署名データの中にObjectなどとして署名対象を格納する。
覚え方は「Detached=離れている」「Enveloped=文書が署名を包む」「Enveloping=署名が文書を包む」です。名前が似ている後者2つは、どちらが外側にあるかで区別します。
XMLDSigの主要要素を読む
| 要素 | 役割 |
|---|---|
| Signature | 署名データ全体の入れ物 |
| SignedInfo | 署名対象と検証に必要な情報をまとめた部分 |
| Reference URI | 署名対象を指す参照先(例:#payment) |
| DigestMethod / DigestValue | 参照先データのハッシュ方式と計算結果 |
| SignatureMethod / SignatureValue | SignedInfoの署名方式と署名値 |
| KeyInfo | 公開鍵や証明書の手がかりを任意で格納 |
例:金額の要素だけを署名する
<Order>
<Customer>山田</Customer>
<Amount Id="payment">5000</Amount>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
...
<Reference URI="#payment">
...
<DigestValue>...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...</SignatureValue>
</Signature>
</Order>構造の概念を示す省略例です。署名方式・正規化・変換などの必須要素は省略しており、そのまま検証可能なXMLではありません。
この例ではReference URI="#payment"が金額要素を示します。金額が5000から9000に変われば、通常は再計算したダイジェストが一致せず検証に失敗します。一方、署名対象外の顧客名を書き換えても、署名検証だけでは検出できない点に注意が必要です。
操作ラボ:何に署名し、どこに置くか
次のラボでは、3形式を切り替え、署名対象の選択やXML項目の変更によって、検証結果がどう変わるかを追えます。
INTERACTIVE LAB
XML署名の形と改ざん検知
署名形式を切り替え、注文書の値を変更してください。「金額だけを保護」と「注文書全体を保護」の違いを体験できます。
Detached:署名と対象が別々
署名と対象は親子関係にありません。同じXML文書内の要素をIDで参照することもできます。
電子注文書
編集して変化を確認署名の検証結果
署名対象に変更なし
保護対象は署名時の値と一致しています。実際の検証には公開鍵と暗号処理が必要です。
現在の署名対象:金額要素
申請者や商品名を変えても検知されません。
現在のXML構造(概念図)
<Document>
<Order Id="order">
<Customer>山田</Customer>
<Amount Id="amount">5000</Amount>
<Item>書籍</Item>
</Order>
<Signature>
<SignedInfo>
<Reference URI="#amount" />
<!-- 署名対象のハッシュ情報など -->
</SignedInfo>
<SignatureValue>署名値(模式表示)</SignatureValue>
</Signature>
</Document>※このラボは署名時点の値との差分を照合する教材です。暗号署名やハッシュ計算は実行せず、生成XMLもXMLDSigの必須要素を省略しています。実際の署名形式は正規化・Transform・鍵検証などが必要です。配置の切替え自体は署名値の再計算を行いません。
実装時に気をつけたいこと
① 署名対象の取り違え(XML Signature Wrapping)
署名の検証自体が成功しても、アプリが署名で保護された要素とは別の要素を業務処理に使うと攻撃につながることがあります。検証後は「実際に使用するデータが署名対象そのものか」を確認します。
② 正規化(Canonicalization / C14N)
XMLでは属性順や名前空間の表現などが異なっても、意味上は同等なことがあります。署名検証では、所定の正規化処理を通したバイト列を扱います。正規化方式やTransformの選択が不適切だと検証失敗や解釈差異の原因になります。
③ 公開鍵の信頼とアルゴリズム制限
KeyInfoに鍵や証明書が含まれていても、それだけで署名者を信頼できるわけではありません。信頼済みの鍵・証明書との照合、許可するアルゴリズムや参照URIの制限、外部リソース取得の禁止・制限などが重要です。
④ XMLDSigとCMS・ASN.1は別の概念
XMLDSigは署名をXML要素で表す方式です。一方、CMSはS/MIMEなどで利用される暗号メッセージの形式で、ASN.1は証明書などのデータ構造を表すためにも使われます。これらをXMLDSigの署名構造そのものと混同しないようにします。
まとめ
XMLDSigの本質は、XML文書のどの部分を署名対象にするかを明示し、その完全性と署名を検証できることです。Detached・Enveloped・Envelopingは、署名と署名対象をどこに配置するかの違い。実際の運用では署名の成否だけでなく、業務アプリが利用する要素まで確実に保護できているかが重要です。