原xml文件清单1:
<?xml version="1.0"?>
<PurchaseOrderRequest>
<Order>
<Item>
<Code>Screw001</Code>
<Description>Screw with half centimeter thread</Description>
</Item>
<Quantity>2</Quantity>
</Order>
<Payment>
<CreditCard>
<Type>MasterCard</Type>
<Number>1234567891234567</Number>
<ExpiryDate>20050501</ExpiryDate>
</CreditCard>
<PurchaseAmount>
<Amount>30000</Amount>
<Currency>INR</Currency>
<Exponent>-3</Exponent>
</PurchaseAmount>
</Payment>
</PurchaseOrderRequest>
清单 2 示范了如何对清单 1 中的部分 XML 进行加密。在这个清单之后,我解释了加密过程中的每一个步骤。
清单 2. XML 加密
//Get the DOM document object for the XML that you
// want to encrypt.
// getDocument method that takes XML file name as input
// and returns DOM document provided in Listing 3 (Step 1)
Document doc = XmlUtil.getDocument(xmlFileName);
String xpath = "/PurchaseOrderRequest/Payment";
// Step 2. Get the shared secret. This key is used to encrypt the
// XML content
Key dataEncryptionKey = getKey();
// Algorithm type used to generate shared secret
// i.e. content encryption key
AlgorithmType dataEncryptionAlgoType = AlgorithmType.TRIPLEDES;
// Get the key pair. You are interested in the public key
// as that is the one you will use for encrypting the
// XML content
KeyPair keyPair = getKeyPair();
// Step 3. Get the public key of the key pair
Key keyEncryptionKey = keyPair.getPublic();
// Algorithm type used to generate the public
// private key pair
AlgorithmType keyEncryptionAlgoType = AlgorithmType.RSA1_5;
KeyInfo keyInfo = new KeyInfo();
// Step 4
try {
Encryptor enc =
new Encryptor(
doc,
dataEncryptionKey,
dataEncryptionAlgoType,
keyEncryptionKey,
keyEncryptionAlgoType,
keyInfo);
XPath xpath = new XPath(xPath);
// Step 5
try {
enc.encryptInPlace(xpath);
} catch (XPathException e1) {
System.out.println("XPAth is not correct");
e1.printStackTrace();
}
XmlUtil.XmlOnStdOut(doc);
} catch (Exception e) {
System.out.println("Some exception");
e.printStackTrace();
}
步骤 1:将 XML 转换成 DOM 对象,如清单 3 所示:
清单 3. 根据 XML 创建 DOM 对象
public static Document getDocument(String fileName) {
Document doc = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
File f = new File(fileName);
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
System.out.println("Parse configuration exception");
e.printStackTrace();
}
try {
doc = builder.parse(f);
} catch (Exception e1) {
System.out.println("Some exception");
e1.printStackTrace();
}
return doc;
}
步骤 2:获得共享密钥(shared secret)。您要用这个密钥来加密 XML 内容。本文附带的源代码使用的 XML 加密方法只能识别三重 DES(Triple-DES)加密算法,因此我就用这种算法创建密钥。
步骤 3:参照本系列文章第 1 部分所述,获得公-私密钥对中的公钥;您需要用这个公钥给共享密钥加密。您从第 1 部分中可以看到,这个公钥是基于 RSA 算法生成的。
步骤 4:利用一个数据加密密钥、一个密钥加密密钥、与这两个密钥相关联的算法、以及将来包含在输出信息中的密钥信息,根据它们来创建一个 Encryptor 对象。创建 Encryptor 对象时指定的算法必须与密钥相符。 Encryptor 是加密过程中的主要对象。它的类在 com.verisign.xmlenc 这个包中。Encryptor 根据 W3C XML Encryption 规范进行加密。您可以指定想要使用哪种加密类型,是 Element 还是 Content。在清单 2 中,加密类型是 Element,这也是默认的类型。 Encryptor 要理解 XPath 表达式,这样才能识别出需要加密的 XML 元素。
步骤 5:最后一步,调用 Encryptor 对象的 encrypt 或者 encryptInPlace 方法,并将 XPath 作为输入参数传入。XPath 定义了 XML 内部需要进行加密的元素。这个元素的所有子元素,以及 XPath 所指向的属性也都要进行加密。在本例中,您加密的是 XML 中的 /PurchaseOrderRequest/Payment 元素。encrypt 和 encryptInPlace 两个方法都用传入的共享密钥对 XPath 指定的 XML 元素进行加密,两种方法也都用公钥对共享密钥进行加密,并将加密结果嵌入到 XML 加密后的内容之中。这两种方法的唯一区别在于,encrypt 返回一个全新的 DOM 文档,其中包含加密后的数据,而 encryptInPlace 方法对原有的文档本身进行修改,使其中包含加密后的数据。加密过的 XML 如清单 4 所示。
清单 4. 加密后的 XML
<?xml version="1.0" encoding="UTF-8"?>
<PurchaseOrderRequest>
<Order>
<Item>
<Code>Screw001</Code>
<Description>Screw with half centimeter thread</Description>
</Item>
<Quantity>2</Quantity>
</Order>
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
<xenc:EncryptionMethod Algorithm=
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey>
<xenc:EncryptionMethod Algorithm=
"http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
<xenc:CipherData>
<xenc:Ciphervalue>
F1aIpdp3axm8nFofx/xX62VlsxildddHcxaevd7sbr+lv/fzZ7e8ovmKGQopAjclxPTybpkW
YG8GVcOIbD4UGR24CNxeB7eZCws5/RKBTqKp+76FkVxf+G+EqgMmueRqoaF4oYOrTKquWLnR
kiSOFmplRaJ8G7bR2j0eTFdiFRk=
</xenc:Ciphervalue>
</xenc:CipherData>
</xenc:EncryptedKey>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:Ciphervalue>
KMkufRUY7rs0i+4jX6VhviiUIbYWay1KbwhTQxH9SaqJ6HA+Qc2Ce7TVZUQuH0GGD4xTR8hB
hOls+hgHA16EfmmxLd3E+YqO4sXQ+GkX9O9EcO4ULha/q1KmP2yNGNy/tavdj9a7JuZnnNGV
/M4gxdt5fCJXT0A9bw9HwKR/Pc81rZYWa7fOrmvDvC7Q+//OCzkqcAaCmAHEySWbv2vK3T+a
GlQOI2Wooxa9hm7Dx70BuLI8ihhSAV3moK+JAPdn1vdCpoFKdzzq2HSh/yOisYZvQOh+jIks
MW8oUzWnVUe/DFztPtvvDKbPE/xoAasixlbDLa42gFFe9uzEeIG89XBMSkZtTio0zn9xppSf
Dc0WFMy+UoLnCA==
</xenc:Ciphervalue>
</xenc:CipherData>
</xenc:EncryptedData>
</PurchaseOrderRequest>
清单 4 是部分加密的 XML 代码片断。只有当接收者具有与加密数据时使用的公钥相对应的私钥时,才能阅读这部分被加密的数据。
最后说一下,清单 5 中的代码可以对加密过的 XML 进行解密。
清单 5. XML 解密
// Get the DOM document object for the XML that you
// want to decrypt (The one shown in Listing 4)
// The getDocument method that takes an XML file name as input
// and returns a DOM document is provided in Listing 3 (Step 1)
Document doc = XmlUtil.getDocument(encryptedXmlFileName);
// Step 2. Get the private key of the pair whose public key was
// used to encrypt the XML
Key privateKey = keyPair.getPrivate();
// Specifying the XPath at which encrypted data is lying
// in the XML
// Step 3. Specify XPath expression
String xpath = "//xenc:EncryptedData";
// Specify the namespace that relates to the XPath
// expression
String[] ns =
{ "xenc", "http://www.w3.org/2001/04/xmlenc#" };
// Create the XPath helper with the XPath expression and a map
// of namespaces that relate to the XPath expression
XPath xpath = new XPath(xPath, ns);
// Step 4. Create the Decryptor object with decryption key and
// location of the encrypted data to be decrypted
Decryptor decrypt = null;
try {
decrypt = new Decryptor(doc, privateKey, xpath);
} catch (Exception e) {
System.out.println("Some exception");
e.printStackTrace();
}
// Step 5. Method decryptInPlace is called to decrypt the
// encrypted contents of the XML
try {
decrypt.decryptInPlace();
} catch (Exception e1) {
System.out.println("Some exception");
e1.printStackTrace();
}
清单 5 示范了当您有正确的私钥时,如何对加密的数据进行解密。下面的步骤解释了解密的过程。
步骤 1:将加密过的 XML 转换成 DOM 对象,这一步与加密过程相同。
步骤 2:根据用于加密 XML 的公钥,获取密钥对中对应的私钥。请注意,解密过程使用的是加密 XML 时使用的公钥所对应的私钥。.
步骤 3:创建 XPath 以及相关名称空间,用于表示加密过的数据在加密过的 XML 中的位置。在本例中,XPath 的值是 //xenc:EncryptedData。加密过的数据总是在加密过的 XML 中的 xenc:EncryptedData 元素下面,而与哪个元素被加密无关。XPath 为 //xenc:EncryptedData 则表示,从 XML 中可能出现加密数据的任何地方查找 EncryptedData 元素。
步骤 4:用解密密钥和需要解密的加密数据所在的位置创建 Decryptor 对象。Decryptor 是解密过程中的主要对象。它的类在 com.verisign.xmlenc 包中。 Decryptor 根据 W3C XML Encryption 规范进行解密(参阅参考资料)。解密过程支持 Element 和 Content 两种类型。为了识别需要解密的 XML 元素, Decryptor 要能理解 XPath 表达式。
步骤 5:解密过程的最后一个步骤是在 Decryptor 对象中调用 decryptInPlace 或者 decrypt 方法。这两种方法调用都使用提供的私钥来解密共享密钥(共享密钥是已加密消息中的一部分),然后用这个共享密钥来解密消息的其余部分。两种调用之间的唯一区别在于, decrypt 对 XML 解密之后创建一个新的 DOM 对象,而 decryptInPlace 在作为输入接收的同一 DOM 对象中解密消息