许多新兴的技术,比如Web服务,都将XML广泛应用于数据交换。因此,XML在传输和存储时的安全性成为非常重要的问题。本次系列文章介绍了保护XML的各种技术。第1部分介绍了XML安全性方面的基本支持技术。本文在前一部分的基础上,介绍了保障XML安全性的核心技术——XML加密和XML签名。本文还循序渐进地介绍了用这些技术保护XML消息的过程。
有许多现有的加密技术,比如安全套接字层(Secure Sockets Layer,SSL),可以对XML文档进行加密,这和其他任何文档都一样,那么为什么还需要另一种加密标准呢?在本文中,我将从考察其目标和动机开始,引出另一种加密标准——XML加密(XML Encryption)。
XML加密的首要目标是:
·支持对任意数字内容的加密,包括XML文档。
·确保经过加密的数据不管是在传输过程中还是在存储时,都不能被未经授权的人员访问到。
·即便在消息传送中的每一跳,都能维持数据的安全性——这句话的意思是,不仅数据正在传输的过程中要保证安全性(这就是SSL所作出的保证),当数据在某个特定的节点停留的时候,也要保证其安全性。
·以XML形式表现被加密的数据。
·可以从XML文档中选出一部分内容进行加密。
我们拿这个目标与基于HTTP的SSL(又称HTTPS)必须提供的功能进行比较。如果使用基于HTTP的SSL,整条消息都会被加密;在第一个目的地,整条消息又被解密,在它被重新全部加密传输到第二跳的节点之前,可能受到嗅探器的威胁。基于HTTP的SSL提供的加密仅仅在传输的过程中存在,它是不持久的。
设计目标之一清晰地表明,经过加密的XML数据应该用XML的形式表现。在得到的XML中,有两个重要的元素值得理解清楚:<EncryptedData>和<EncryptedKey>。<EncryptedData>中包含除加密密钥之外所有经过加密的内容。当对加密密钥进行加密的时候,得到的结果就放置在<EncryptedKey>元素中。
除了加密过的内容以外,XML加密还允许您在上面讨论的两个元素中指定用于加密的算法,或者是加入用于加密的密钥。这意味着您即便不将它们分别保存,也可以在后续引用这些数据;或者通过其他传输机制发送给接收方。
注意:XML加密没有定义任何新的加密算法,只使用已有的算法。
在本节中,我先向您展示一段普通的XML代码,然后介绍用XML Encryption标准对这段代码进行加密的各个步骤。我将从清单1中显示的XML开始。
清单1. XML示例
<?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”
>
<xenc:EncryptionMethod Algorithm=
“http://www.w3.org/2001/04/xmlenc#tripledes-cbc”/>
<ds:KeyInfo >
<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对象中解密消息。
数字签名已经应用了相当长一段时间,任何数字化内容都可以用公钥密码标准(Public Key Cryptography Standards,最常用的是PKCS#7签名)进行数字签名。安全多用途Internet邮件扩展(Secure Multipurpose Internet Mail Extensions,S/MIME)允许将数字签名附加到电子邮件消息上,这样接收方就可以验证签名者的身份。
XML签名是对现有数字签名基础设施的扩展。下面列出了创建XML签名的一些目标和动机:
·在数字签名周围建立一些结构,这样就可以用XML文档的形式来表现数字签名。
·实现对一部分XML进行签名,而剩余部分则不签名。
·实现对同一份XML文档的不同部分使用多于一种的数字签名。
·不仅仅在文档传送和通信的时候使用签名,还要使签名能够持久保留。
XML签名概览
XML签名可以用于对包括XML文档在内的任何数字内容进行签名。对数字内容进行签名的过程分为两个阶段。在第一阶段中,对数字内容进行整理,得到的结果放在一个XML元素中。第二阶段挑选出经过整理的值,并对其进行签名。这样做的原因非常简单:对原始内容进行整理之后,可以得到一个很小的但是唯一的加密结果(称为摘要),这样比对原始内容进行签名花费的时间少。
当XML(或其中的一部分)经过数字签名之后,得到的XML签名用一个XML元素表现出来,这个元素的标识是<Signature>,最初的内容与这个数字签名的关系基于下面几种XML签名类型:
·封外签名(Enveloping signature):<Signature>元素中包含了进行数字签名的元素。被签名的元素成为了<Signature>元素的子元素。
·封内签名(Enveloped signature):<Signature>元素成为被签名数据的子元素。<Signature>元素通过它其中的<Reference>元素提供的信息引用被签名的元素。
·分离签名(Detached signature ):<Signature>元素与被签名的元素各自独立存在。被签名的元素和<Signature>元素可以同属于一个文档,或者,<Signature>元素也可以在另一个完全不同的文档中。
除了引用被签名的数字内容之外,<Signature>元素还包括了有关如下方面的信息:
·用于使数字内容规范化的方法。
·为待签名的规范化元素生成签名的算法。
·指定在整理之前如何处理待签名元素的附加信息。
XML签名过程
接着清单1中的例子看,我现在将带你经历对XML元素进行数字签名的全过程。清单6示范了如何对部分XML进行数字签名;签名过程中的每一步骤都会在这个清单后面解释。
清单6. 数字签名的过程
// Get the DOM document object for the XML that you
// want to digitally sign.
// getDocument method that takes XML file name as input
// and returns DOM document is provided in Listing 3 (Step 1)
Document doc = XmlUtil.getDocument(xmlFileName);
// XPath expression that is to be digitally signed
String xpath = “/PurchaseOrderRequest/Payment”;
// Step 2. Get both the public and private keys. The private key is
// used to digitally sign the content, whereas the public key
// is sent along with the digitally signed content for the
// receiver to verify the digital signature.
contentKeyPair keyPair = getKeyPair();
PublicKey verificationKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// Step 3. Create a signer object passing the document whose part
// is to be signed, the private key to be used for
// signing, and the public key that is to be used
// for verification.
Signer signer = null;
// Step 4
try {
signer = new Signer(doc, privateKey,
verificationKey);
XPath location1 =
new XPath(“/PurchaseOrderRequest/Payment/CreditCard”);
signer.addReference(location1);
XPath location2 =
new XPath(“/PurchaseOrderRequest/Payment/PurchaseAmount”);
signer.addReference(location2);
} catch (Exception e) {
e.printStackTrace();
}
// Step 5. Use the signer object to sign the document passing the
// XPath of the element that is to be signed.
try {
d = signer.sign(new XPath(xPath));
} catch (Exception e1) {
e1.printStackTrace();
}
步骤1:同前面加密的步骤一样,将需进行数字签名的XML转换为DOM对象。
步骤2:获得您在本系列第1部分的“生成公-私密钥对”一节中生成的密钥对,其中同时包括公钥和私钥。您将用私钥对内容进行数字签名,然后将公钥随着经过数字签名的消息一起发送到接收方,以便用来验证其中的数字签名。前面讲过,公-私密钥对是基于RSA算法的。
步骤3:根据需要签名的DOM文档、用来签名的私钥以及用于验证的公钥,创建一个signer对象。验证用的公钥并不是一定要有。signer是数字签名过程中的主要对象。它的类在com.verisign.xmlsig包中。signer对象根据W3C XML Signature规范对XML文档签名。共支持全部三种签名模式:封外、封内以及分离模式。
步骤4:指定XML中的哪一部分需要签名。通过增加到Signer对象的引用,可以指定数字签名的XML位置。增加引用的方法是调用Signer对象中的addReference方法。需要签名的元素的XPath也作为一个参数提供给addReference。您可以多次调用addReference,来指定想要进行签名的不同元素。
步骤5:最后一步是对XML签名,您可以通过调用signer对象中的sign方法来完成。当调用sign方法时,如果不传递任何参数的话,生成的就是封外签名。调用带有XPath参数的sign方法,并且数字签名放置在文档中,则生成封内签名。
结束语
随着这个XML安全专题深入到这里,我已经定义了安全的含义,并讨论了XML规范化、PKI基础设施、XML加密和XML签名。通过介绍XML安全背后的理论,并竭力向您展示XML加密和数字签名有多么容易,希望这一切努力能为您揭开XML安全的神秘面纱。
在今后的文章中,我将要讨论安全性断言标记语言(Security Assertion Markup Language,SAML)和XML密钥管理系统( XML Key Management System,XKMS)。SAML可以用于在不同的协作实体之间传送凭证以及其他一些与主题、人等相关的信息,而不会损失信息的所属关系。通过将管理公钥基础设施(Public Key Infrastructure,PKI)的复杂性从客户机应用程序提取到一个可信任的第三方机构中,XKMS使得管理PKI变得非常容易。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
作者
相关推荐
-
BEST:SOAP/XML和REST的替代方案
虽然拥有大量的机架服务器,以及大量软件开发人员的组织,基于web和集成服务的SOAP和REST很适合他们,但也会出现问题。
-
Spring 烂!差!
有些人可能对Spring的第一印象不太好,它真的很烂,很差吗,也许这只是你的一种偏见,它也有是自己的优点的。
-
基于SOA架构的业务安全性研究
SOA在提供价值链上企业之间信息共享和业务流程自动化的同时,也给业务信息安全带来了负面影响,且存在安全隐患,这些你知道吗?
-
Java读取配置文件的几种方法
在现实工作中,我们常常需要保存一些系统配置信息,大家一般都会选择配置文件来完成,那么在Java怎样读取配置文件呢?