利用XML Signature, 消息的完整性(Integrity)得到了保證。回顧之前提到的安全的三個基本概念--- Integrity, Confidentiality, Authentication, 現在該考慮消息的機密性的問題,雖然簽名可以保證消息在傳送的途中沒有被篡改,但是並不能避免它被偷取。如果消息沒有經過加密,那麼某個敏感的信息就會被泄漏。與XML Signature類似,結合了XML技術和傳統加密技術而產生的XML Encryption,也並不僅僅是加密XML文件那麼簡單,它提供了以下的功能:
1. 加密整個XML文件。
2. 加密XML文件中的某個元素。
3. 加密XML文件中某個元素的內容。
4. 加密非XML格式的資源。(例如一張JPEG圖片)。
5. 加密已經過加密的內容。
XML Encryption的結構如下所示:
<EncryptedData (Id)? (Type)? (MimeType)? (Encoding)?>
(<EncryptionMethod/>)?
(<ds:KeyInfo>
(<ds:KeyName>)?
(<ds:RetrievalMethod>)?
(<ds:*>)?
(<EncryptedKey>)?
(<AgreementMethod>)?
</ds:KeyInfo>)?
<CipherData>
(<CipherValue>)?
(<CipherReference (URI)?>)?
</CipherData>
(<EncryptionProperties>)?
</EncryptedData>
(x)? 代表x出現0-1次 (x)+ 代表x出現1-n次 (x)* 代表x出現0-n次
與XML Signature不同,XML Encryption更加體現了自包含的性質,它不象XML Signature通過引用對某個資源簽名,而是在原資源的位置上創建一個新的EncryptedData元素完全的替代原資源(使用CipherReference除外)。也是因爲這個原因,你不可能象XML Signature那樣在一個Signature元素中對多個資源簽名,有幾個需要加密的資源就有幾個EncryptedData元素替代它們。
EncryptedData元素是原資源經過XML Encryption作用後的結果,將替代原資源。Type屬性有兩個合法值: element, content. 它們用於區別是否加密tag(標籤)。如果Type設爲element將加密整個元素包括tag在內, 而設爲content時只對元素中的內容加密。
EncryptionMethod元素指定加密將使用的算法。
CipherData元素中的內容爲原資源加密後的結果,可以用兩種形式表示,通常使用CipherValue,而CipherReference類似於XML Signature的Reference元素,往往用於對外部資源(jpeg文件)的加密。
EncryptionProperties元素用於爲加密的數據添加一些額外的信息,比如加密發生的時間。
KeyInfo元素描述加密所使用的密鑰。這裏的KeyInfo是借用的XML Signature下的KeyInfo元素。在簽名的時候,大多使用非對稱密鑰,即利用私鑰產生簽名,然後將公鑰信息放在KeyInfo元素中,這樣消息的接受方就可以直接使用公鑰來驗證簽名。但是在加密的時候,通常使用的是對稱密鑰,如果此時把密鑰的信息直接放在KeyInfo中顯然是不安全的。此時有以下幾種方法:
1. 不使用KeyInfo元素,假定消息交換已經約定好了加密使用的密鑰。
2. 在KeyInfo中指定一個標識(Identity),假定消息接受方已經擁有了解密所需的密鑰,通過這個標識,消息接受方接可以定位到此次解密所需要的密鑰。
3. 使用消息接受方的公鑰加密此次加密消息所使用的對稱密鑰,消息接受方利用自己唯一擁有的私鑰解密出加密消息使用的密鑰。
4. 通過key agreement protocol獲得密鑰(較少使用)。
基於以上幾種方法,下面對KeyInfo元素做具體介紹:
KeyName: 方法2中的一種形式,通過指定一個標識來獲得解密所需的密鑰。
RetrievalMethod: 方法2中的另一種形式,通過一個URI指向解密所需的密鑰, 比如指向信息中另一段被加密的內容,而那段內容可以方便的被解密。
EncryptedKey: 爲了使用第三種方法,XML Encryption爲KeyInfo加入的擴展元素。通過非對稱密鑰技術來傳遞對稱密鑰, 綜合了兩種的優點,前提是消息加密方需擁有消息接受方的公鑰。下面是使用該方法的一個例子。
AgreementMethod: 該元素使用key agreement protocol來獲得密鑰,極少使用故不做介紹。
<EncryptedData>
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmlsig#" />
<EncryptedKey>
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmlsig#"/>
<ds:X509Data>
<ds:X509SubjectName>
o=MyCompany,ou=Engineering,cn=Dave Remy
</ds:X509SubjectName>
</ds:X509Data>
</ds:KeyInfo>
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
</EncryptedKey>
</ds:KeyInfo>
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
</EncryptedData>
從上面的例子中可以看出EncryptedKey 和EncryptedData 具有類似的結構,其實它們本來就是同一類型(EncryptedType),一個用於加密數據,一個用於加密密鑰(密鑰就是一種特殊的數據)。它們之間的關係就象面向對象中的sub class和abstract class。EncryptedKey 和EncryptedData是EncryptedType的子類,而EncryptedType是不能具體存在的。因此EncryptedKey可以獨立於EncryptedData存在,甚至EncryptedKey還可以象EncryptedData那樣在子元素中嵌套EncryptedKey或EncryptedData。
瞭解了這個特性,就可以避免在兩段使用相同密鑰加密的消息中重複包含一個複雜的EncryptedKey。利用ReferenceList元素可以在密鑰中引用到使用該密鑰的不同地方,避免重複。
<Employee>
<Name>Dave Remy</Name>
<SocialSecurityNumber>
<EncryptedData id="socsecnum" Type="http://www.w3.org/2000/09/xmldsig#content">
<EncryptionMethod Algorithm=". . ." />
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
</EncryptedData>
</SocialSecurityNumber>
<Salary>
<EncryptedData id="salary" Type="http://www.w3.org/2000/09/xmldsig#content">
<EncryptionMethod Algorithm=". . .">
<CipherData><CipherValue>. . .</CipherValue></CipherData>
</EncryptedData>
</Salary>
<EncryptedKey>
<EncryptionMethod Algorithm=". . ." />
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
<ReferenceList>
<DataReference URI="#socsecnum" />
<DataReference URI="#salary" />
</ReferenceList>
</EncryptedKey>
</Employee>
通過以上方法,可以通過EncryptedKey中的ReferenceList定位到使用該密鑰的不同信息段,但是這是一個單向引用,爲了增加可讀性,以及方便XML Encryption Processor處理還可以通過CarriedKeyName元素來實現雙向引用,在WS-Security中也有類似的實現,示例如下:
<Employee>
<Name>Dave Remy</Name>
<SocialSecurityNumber>
<EncryptedData id="socsecnum" Type="http://www.w3.org/2000/09/xmldsig#content">
<KeyInfo>
<KeyName>Jothy Rosenberg</KeyName>
</KeyInfo>
<EncryptionMethod Algorithm=". . ." />
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
</EncryptedData>
</SocialSecurityNumber>
<Salary>
<EncryptedData id="salary" Type="http://www.w3.org/2000/09/xmldsig#content">
<EncryptionMethod Algorithm=". . .">
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
</EncryptedData>
</Salary>
<EncryptedKey>
<EncryptionMethod Algorithm=". . ." />
<CipherData>
<CipherValue>. . .</CipherValue>
</CipherData>
<ReferenceList>
<DataReference URI="#socsecnum" />
<DataReference URI="#salary" />
</ReferenceList>
<CarriedKeyName>Jothy Rosenberg</CarriedKeyName>
</EncryptedKey>
</Employee>
與XML Signature相比而言,XML Encryption的創建(加密)和驗證(解密)過程要簡單的多。簡要介紹如下:
加密過程:
1. 選擇一個加密算法
2. 選擇一個加密用的密鑰,如果需要將密鑰的有關信息展示給消息接受方。
3. 在加密前,將待加密的資源轉換爲字符流的格式。
4. 使用選擇的密鑰和算法加密經過串行化的原始消息。
5. 設置加密的類型(Content還是Element?)。
6. 根據結果和以上的選項創建出EncryptedData元素,替代原來的資源。
解密過程:
1. 將CipherValue元素的內容抽取出來。
2. 從EncryptionMethod的Algorithm中獲得加密所用的算法。
3. 獲得加密的類型(Content還是Element?)。
4. 通過KeyInfo中的信息取得密鑰。
5. 根據以上信息將密文解密獲得原始信息。