XML Encryption in .Net

XML Encryption in .Net

One of the new features being introduced with the Whidbey version of the .Net framework is XML encryption.  XML Encryption allows you to encrypt arbitrary data, and have the result be an XML element.  Much as XML digital signatures are driven through the SignedXml class, this feature is driven through the new EncryptedXml class.  In order to allow this feature to work well with XML digital signatures, there is a special transform included with the framework, that allows the digital signature engine to decrypt the encryption document, and compute the signature over only that portion.

In this posting, I'll build upon the code from my earlier posting on signing an XML document.  In that post, I showed how to use XML digital signatures to verify that nobody had tampered with a CD order from a website.  However, this signature did nothing to hide the purchaser's credit card information from prying eyes.

Encrypting the Document

The first step is to setup an EncryptedXml object, and get a key that will be used for encryption.  This example generates a random RSA key (that is then written to a file, so that it can be used again to verify the signature and decrypt the document), but in real life, a well known key would be used here.  There are two choices for an encryption key -- you could use a symmetric key that both parties know, but this can be problematic for key management purposes.  Instead, I have chosen to use an RSA key.  I will then generate a random symmetric session key to do the actual encryption with, and embed this key within the encrypted document itself.

XmlDocument doc = new XmlDocument();
doc.Load("order.xml"); 
EncryptedXml exml = new EncryptedXml(doc);
    
// setup the key for encryption 
RSA sharedKey = new RSACryptoServiceProvider();
using(StreamWriter writer = new StreamWriter("shared-key.xml"))
    writer.WriteLine(sharedKey.ToXmlString(true)); 

// create a random session key to do the actual work with 
RijndaelManaged sessionKey = new RijndaelManaged();
sessionKey.KeySize = 256;
  
// encrypt the session key 
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, sharedKey, false);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA1_5Url); 
The last step in setting up the keys, is to assign a name to the key that I'm using to encrypt the document with, which will be later placed into the encrypted xml, allowing the reciepient of my encrypted document to determine which key to use to decrypt it with. 

// set up a key info clause for the key that was used to encrypt the session key 
KeyInfoName keyName = new KeyInfoName(); 
keyName.Value = "shared-key";
ek.KeyInfo.AddClause(keyName);
exml.AddKeyNameMapping("shared-key", sharedKey); 
In this example, I am only going to encrypt the payment tag, leaving the rest of the less-sensitive content in plain text. 

// encrypt the payment tag 
XmlElement paymentElem = doc.SelectSingleNode("/order/payment") as XmlElement;
byte[] encryptedPayment = exml.EncryptData(paymentElem, sessionKey, false);
  
// create the encrypted data 
EncryptedData ed = new EncryptedData();
ed.CipherData = new CipherData(encryptedPayment);
ed.Type = EncryptedXml.XmlEncElementUrl;
ed.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
ed.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

After computing the encrypted value, it is placed in an EncryptedData object, along with some information about how the encryption was done, including the name of the key necessary to decrypt the data, the algorithm used for the encryption, and the type of data that was encrypted.  Note that the encryption method is AES-256 since the session key was used for encryption, not the RSA key.  The last step is simply to replace the unencrypted data in the document with the encrypted version.

// replace the original XML with this version EncryptedXml.ReplaceElement(paymentElem, ed, false);

Modifications to the Signature

In order for the XML digital signature to be able to sign the unencrypted form of the payment element, it must have an XmlDecryptionTransform applied to it.  This transform needs to be setup with an EncryptedXml object that contains the key name mapping for any keys necessary to decrypt the document.  In this case, we can simply pass the EncryptedXml object that we already used to perform the encryption.  Here is the modified code that creates the reference to the content that is to be signed.

// create a reference to the root of the document  Reference orderRef = new Reference(""); orderRef.AddTransform(new XmlDsigEnvelopedSignatureTransform()); XmlDecryptionTransform decXform = new XmlDecryptionTransform(); decXform.EncryptedXml = exml; orderRef.AddTransform(decXform); signer.AddReference(orderRef);

The result

After running this modified code over order.xml, the result is:

<order>
  <purchase>
    <item quantity="1">Def Leppard: Pyromania</item>
    <item quantity="1">Ozzy Osbourne: Goodbye to Romance</item>
  </purchase>
  <shipping>
    <to>Shawn Farkas</to>
    <street>5 21st Street</street>
    <city>Seattle</city>
    <state>WA</state>
    <zip>98000</zip>
  </shipping>
  <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
    xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <KeyName>shared-key</KeyName>
        </KeyInfo>
        <CipherData>
          <CipherValue>GIYVu9Hr  Gqj/9t0=</CipherValue>
        </CipherData>
      </EncryptedKey>
    </KeyInfo>
    <CipherData>
      <CipherValue>SFJKVdDT bGOYQw==</CipherValue>
    </CipherData>
  </EncryptedData>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
          <Transform Algorithm="http://www.w3.org/2002/07/decrypt#XML" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>BPoz+CmKZyTATOhskqke3iOXmvA=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>QVBMvFic YEO31+o=</SignatureValue>
    <KeyInfo>
      <KeyValue>
        <RSAKeyValue>
          <Modulus>rh3AyE99 MdSOAo0=</Modulus>
          <Exponent>AQAB</Exponent>
        </RSAKeyValue>
      </KeyValue>
    </KeyInfo>
  </Signature>
</order>

Modifying the Verification Code

The modifications to the code that verify the signature are very minor.  All that needs to be done is to set the SignedXml object up with an EncryptedXml object that contains the necessary key name mappings.  In order to do this, I will first read the key in from the file created by the encryption process, add its name to an EncryptedXml object, and set this property on the SignedXml object.

// read in the encryption key  RSA sharedKey = new RSACryptoServiceProvider(); using(StreamReader reader = new StreamReader("shared-key.xml"))     sharedKey.FromXmlString(reader.ReadLine());  // setup the keyname mapping  EncryptedXml exml = new EncryptedXml(doc); exml.AddKeyNameMapping("shared-key", sharedKey);  SignedXml verifier = new SignedXml(doc); verifier.EncryptedXml = exml; Decrypting the Encrypted Data

Decrypting the encrypted payment value is also trivial.  This can be done simply by calling one method on the EncryptedXml object, which will decrypt any encrypted data in the document using the keys that it has in its key name mapping table, and replace the encrypted version with the decrypted one:

// decrypt the encrypted document exml.DecryptDocument(); Console.WriteLine("Decrypted payment info: "); Console.WriteLine(doc.SelectSingleNode("/order/payment").OuterXml);

Published Friday, November 14, 2003 11:10 PM by shawnfa

Filed under: Security, Cryptography, XML

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逸鹏说道

Birdge.NET:将C#代码转换为JavaScript

Birdge.NET 是一个可以将C#代码转换为JavaScript的开源编译器,由 Object.NET于2015年5月推出。它允许开发者使用C#编写平台独立...

33940
来自专栏逸鹏说道

【.NET】UnpackMe!Shielden+DNGuard,双层变异壳 - 脱壳详解

前言:自从脱壳神器de4dot横空出世以来,我们可以看到几乎所有的.net破文中的第一部分就是不管三七二十一把程序丢进去脱壳以及反混淆。可是你真的明白de4do...

2K40
来自专栏逸鹏说道

memcached安装及.NET中的Memcached.ClientLibrary使用详解

序言 吹吹牛逼先,借我你的20分钟,保证你在.net中使用memcached缓存数据,畅通无阻,提升数据读取效率,分担数据库压力,便不在话下。 本篇主要说下:m...

31370
来自专栏逸鹏说道

Toxy新手教程

Toxy新手教程 官方网站:http://toxy.codeplex.com Toxy是干嘛用的?它是.NET平台上的文件抽取框架,主要解决各种格式的内容抽取问...

32260
来自专栏逸鹏说道

2014年国内最热门的.NET开源项目TOP25

如果知道.NET项目在开源中国的git上所占的比重只有5%的话,为什么这个《2014年国人开发的最热门的开源软件TOP 100》榜中.NET项目那么少就是情理之...

48670
来自专栏逸鹏说道

大型.NET ERP系统的20条数据库设计规范

数据库设计规范是个技术含量相对低的话题,只需要对标准和规范的坚持即可做到。当系统越来越庞大,严格控制数据库的设计人员,并且有一份规范书供执行参考。在程序框架中,...

39260
来自专栏逸鹏说道

.NET技术+25台服务器怎样支撑世界第54大网站

英文原文:StackOverflow Update: 560M Pageviews A Month, 25 Servers, And It's All Abou...

43070
来自专栏逸鹏说道

基础才是重中之重~多线程的代价~我的内存都被吃了!

异步操作是.net4.5推出的新名词,事实上,这东西早就有了,它归根结底是通过线程池来实现的,即将一个大任务分成多个小任何块,每个线程并行处理其中的一个,完成后...

29370
来自专栏逸鹏说道

Red Hat与微软合作,将致力于构建企业级Linux版.NET

微软和红帽声明将在红帽企业版Linux运行的.NET纳入官方支持。经两家公司透露,“红帽企业级Linux将成为Linux下的.NET Core主要参考操作系统...

28180
来自专栏逸鹏说道

关于是否在C#中加入不可空引用类型的争论

来自微软的Mads Togersen在近期所提出的一条提议,即在C#语言中加入对不可空引用类型的支持在.NET社区中引起了热烈的争论。人们对此提议的反应大相径庭...

27650

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励