XML安全:实现安全层 第2部分

日期: 2009-03-12 作者:Manish Verma 来源:TechTarget中国 英文

  许多新兴的技术,比如Web服务,都将XML广泛应用于数据交换。因此,XML在传输和存储时的安全性成为非常重要的问题。本次系列文章介绍了保护XML的各种技术。第1部分介绍了XML安全性方面的基本支持技术。本文在前一部分的基础上,介绍了保障XML安全性的核心技术——XML加密和XML签名。本文还循序渐进地介绍了用这些技术保护XML消息的过程。

  有许多现有的加密技术,比如安全套接字层(Secure Sockets Layer,SSL),可以对XML文档进行加密,这和其他任何文档都一样,那么为什么还需要另一种加密标准呢?在本文中,我将从考察其目标和动机开始,引出另一种加密标准——XML加密(XML Encryption)。

  XML加密

  XML加密的首要目标是:

  ·支持对任意数字内容的加密,包括XML文档。
  ·确保经过加密的数据不管是在传输过程中还是在存储时,都不能被未经授权的人员访问到。
  ·即便在消息传送中的每一跳,都能维持数据的安全性——这句话的意思是,不仅数据正在传输的过程中要保证安全性(这就是SSL所作出的保证),当数据在某个特定的节点停留的时候,也要保证其安全性。
  ·以XML形式表现被加密的数据。
  ·可以从XML文档中选出一部分内容进行加密。

  我们拿这个目标与基于HTTP的SSL(又称HTTPS)必须提供的功能进行比较。如果使用基于HTTP的SSL,整条消息都会被加密;在第一个目的地,整条消息又被解密,在它被重新全部加密传输到第二跳的节点之前,可能受到嗅探器的威胁。基于HTTP的SSL提供的加密仅仅在传输的过程中存在,它是不持久的。

  XML加密概览

  设计目标之一清晰地表明,经过加密的XML数据应该用XML的形式表现。在得到的XML中,有两个重要的元素值得理解清楚:<EncryptedData>和<EncryptedKey>。<EncryptedData>中包含除加密密钥之外所有经过加密的内容。当对加密密钥进行加密的时候,得到的结果就放置在<EncryptedKey>元素中。

  除了加密过的内容以外,XML加密还允许您在上面讨论的两个元素中指定用于加密的算法,或者是加入用于加密的密钥。这意味着您即便不将它们分别保存,也可以在后续引用这些数据;或者通过其他传输机制发送给接收方。

  注意: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对象中解密消息。

  XML签名

  数字签名已经应用了相当长一段时间,任何数字化内容都可以用公钥密码标准(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

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • BEST:SOAP/XML和REST的替代方案

    虽然拥有大量的机架服务器,以及大量软件开发人员的组织,基于web和集成服务的SOAP和REST很适合他们,但也会出现问题。

  • Spring 烂!差!

    有些人可能对Spring的第一印象不太好,它真的很烂,很差吗,也许这只是你的一种偏见,它也有是自己的优点的。

  • 基于SOA架构的业务安全性研究

    SOA在提供价值链上企业之间信息共享和业务流程自动化的同时,也给业务信息安全带来了负面影响,且存在安全隐患,这些你知道吗?

  • Java读取配置文件的几种方法

    在现实工作中,我们常常需要保存一些系统配置信息,大家一般都会选择配置文件来完成,那么在Java怎样读取配置文件呢?