WCF 进阶: 对称加密传输

  大家使用WCF的时候,会不会觉得使用SSL通道传输太麻烦,使用明文传输又觉得不安全呢? 特别是当传递的消息中带有比较敏感,机密的身份信息的时候更是如此呢?我们在上文实现了压缩编码传输,详见WCF进阶:将编码后的字节流压缩传输,本文照葫芦画瓢,实现一个可能大家更为需要的功能,将数据对称加密后传输,好处就是加密速度嗷嗷快,使用起来嗷嗷方便。

  工作原理和压缩传输一致所以本文不做赘述,详细的实现机理会单开一篇详细去谈,本文重点看看实现代码和实现效果。要实现对称机密传输的功能,我们主要要实现的有如下几个类:CryptEncodingBindingElement,CryptEncoderFactory,CryptEncoder,DESCryption,前面三项都是WCF扩展所必须的,后面是工具类,主要是用于DES加解密和生成密钥和IV。

CryptEncodingBindingElement

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.ServiceModel.Channels;

namespace RobinLib
{
    public class CryptEncodingBindingElement: MessageEncodingBindingElement
    { 
        private XmlDictionaryReaderQuotas readerQuotas;
        private MessageEncodingBindingElement innerMessageEncodingBindingElement;
        string key;
        string iv; 
        public MessageEncodingBindingElement InnerMessageEncodingBindingElement
        {
            get
            {
                return innerMessageEncodingBindingElement;
            }
        }

        public string Key
        {
            get
            {
                return key;
            }
        }
        public string IV
        {
            get
            {
                return iv;
            }
        }

        public CryptEncodingBindingElement(MessageEncodingBindingElement innerMessageEncodingBindingElement, string key,string iv)
        {
            this.readerQuotas = new XmlDictionaryReaderQuotas();
            this.key = key;
            this.iv = iv;
            this.innerMessageEncodingBindingElement = innerMessageEncodingBindingElement;
        }

        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelFactory<TChannel>();
        }
        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
        }
        public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.CanBuildInnerChannelFactory<TChannel>();
        }
        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.CanBuildInnerChannelListener<TChannel>();
        }
        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new CryptEncoderFactory(innerMessageEncodingBindingElement,key,iv);
        }
        public override T GetProperty<T>(BindingContext context)  
        {
            if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
            {
                return this.readerQuotas as T;
            }
            return base.GetProperty<T>(context);

        }
        public override MessageVersion MessageVersion
        {
            get
            {
                return innerMessageEncodingBindingElement.MessageVersion;
            }
            set
            {
                innerMessageEncodingBindingElement.MessageVersion = value;
            }
        }
        
        public override BindingElement Clone()
        {
            return new CryptEncodingBindingElement(innerMessageEncodingBindingElement,key,iv);
        } 
    }
}

CryptEncoderFactory

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;

namespace RobinLib
{
    public class CryptEncoderFactory : MessageEncoderFactory
    {
        private MessageEncodingBindingElement innerMessageEncodingBindingElement;
        CryptEncoder messageEncoder;
        string key;
        string iv; 
        public CryptEncoderFactory(MessageEncodingBindingElement innerMessageEncodingBindingElement, string key,string iv)
        {
            this.innerMessageEncodingBindingElement = innerMessageEncodingBindingElement;
            this.key = key;
            this.iv = iv;
            messageEncoder = new CryptEncoder(this,key, iv);
        }
        public override MessageEncoder CreateSessionEncoder()
        {
            return base.CreateSessionEncoder();
        }
        public override MessageEncoder Encoder
        {
            get { return messageEncoder; }
        }
        public override MessageVersion MessageVersion
        {
            get { return innerMessageEncodingBindingElement.MessageVersion; }
        }
        public MessageEncodingBindingElement InnerMessageEncodingBindingElement
        {
            get
            {
                return innerMessageEncodingBindingElement;
            }
        }
    }
}

CryptEncoder

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.IO;

namespace RobinLib
{
    public class CryptEncoder : MessageEncoder
    {
        CryptEncoderFactory factory;
        MessageEncoder innserEncoder;
        string key;
        string iv;
        public CryptEncoder(CryptEncoderFactory encoderFactory,string key,string iv)
        {
            factory = encoderFactory;
            this.key = key;
            this.iv = iv;
            innserEncoder = factory.InnerMessageEncodingBindingElement.CreateMessageEncoderFactory().Encoder;
        }
        public override string ContentType
        {
            get { return innserEncoder.ContentType; }
        }
        public override string MediaType
        {
            get { return innserEncoder.MediaType; }
        }
        public override MessageVersion MessageVersion
        {
            get { return innserEncoder.MessageVersion; }
        }
        public override bool IsContentTypeSupported(string contentType)
        {
            return innserEncoder.IsContentTypeSupported(contentType);
        }
        public override T GetProperty<T>()
        {
            return innserEncoder.GetProperty<T>();
        }
        public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
        {
            ArraySegment<byte> bytes = new DESCryption(key,iv).Decrypt(buffer);
            int totalLength = bytes.Count;
            byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
            Array.Copy(bytes.Array, 0, totalBytes, 0, bytes.Count);
            ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, 0, bytes.Count);
            bufferManager.ReturnBuffer(byteArray.Array); 
            Message msg = innserEncoder.ReadMessage(byteArray, bufferManager, contentType);
            return msg;

        }
        public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
        {
            //读取消息的时候,二进制流为加密的,需要解压
            Stream ms = new DESCryption(key,iv).Decrypt(stream); 
            Message msg = innserEncoder.ReadMessage(ms, maxSizeOfHeaders, contentType);
            return msg;
        }
        public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        { 
            ArraySegment<byte> bytes = innserEncoder.WriteMessage(message, maxMessageSize, bufferManager);
            ArraySegment<byte> buffer = new DESCryption(key,iv).Encrypt(bytes);
            int totalLength = buffer.Count + messageOffset;
            byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
            Array.Copy(buffer.Array, 0, totalBytes, messageOffset, buffer.Count);
            ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, buffer.Count);
            Console.WriteLine(",原来字节流大小:"+bytes.Count+",压缩后字节流大小:"+byteArray.Count);
            return byteArray;
        }
        public override void WriteMessage(Message message, System.IO.Stream stream)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            innserEncoder.WriteMessage(message, ms);
            stream = new DESCryption(key,iv).Encrypt(ms);
        }
    }
}

DESCryption

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace RobinLib
{
    public class DESCryption : IDisposable
    {
        DESCryptoServiceProvider des;
        Encoding encoding = new UnicodeEncoding();
        public DESCryption()
        {

        }
        public DESCryption(string key, string iv)
        {
            des = new DESCryptoServiceProvider();
            des.Key = Convert.FromBase64String(key);
            des.IV = Convert.FromBase64String(iv);
        }
        public void Dispose()
        {
            des.Clear();
        }
        public void GenerateKey(out string key, out string iv)
        {
            key = "";
            iv = "";
            using (DESCryptoServiceProvider des_o = new DESCryptoServiceProvider())
            {
                des_o.GenerateIV();
                des_o.GenerateKey();
                iv = Convert.ToBase64String(des_o.IV);
                key = Convert.ToBase64String(des_o.Key);
            }
        }
        #region ========加密========
        /// <summary> 
        /// 加密数据 
        /// </summary> 
        /// <param name="Text"></param> 
        /// <param name="sKey"></param> 
        /// <returns></returns> 
        public string Encrypt(string Text)
        {
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            StreamWriter sw = new StreamWriter(cs);
            sw.Write(Text);
            sw.Close();
            cs.Close();
            byte[] buffer = ms.ToArray();
            ms.Close();
            return Convert.ToBase64String(buffer);
        }

        public ArraySegment<byte> Encrypt(ArraySegment<byte> buffers)
        { 
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(buffers.Array, 0, buffers.Count); 
            cs.Close();
            byte[] buffer = ms.ToArray();
            ms.Close(); 
            ArraySegment<byte> bytes = new ArraySegment<byte>(buffer); 
            return bytes;
        }

        public Stream Encrypt(Stream stream)
        {
            MemoryStream ms = new MemoryStream(); 
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] buffer = new byte[stream.Length];
            stream.Read(buffer, 0, buffer.Length);
            cs.Write(buffer, 0, buffer.Length);
            cs.Close(); 
            return ms;
        }

        #endregion

        #region ========解密========
        /// <summary> 
        /// 解密数据 
        /// </summary> 
        /// <param name="Text"></param> 
        /// <param name="sKey"></param> 
        /// <returns></returns> 
        public string Decrypt(string Text)
        {
            byte[] inputByteArray = Convert.FromBase64String(Text);
            System.IO.MemoryStream ms = new System.IO.MemoryStream(inputByteArray);
            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read);
            StreamReader sr = new StreamReader(cs);
            string val = sr.ReadLine();
            cs.Close();
            ms.Close();
            des.Clear();
            return val;
        }
        public ArraySegment<byte> Decrypt(ArraySegment<byte> buffers)
        {
            MemoryStream ms = new MemoryStream();
            ms.Write(buffers.Array, 0, buffers.Count);
            ms.Seek(0, SeekOrigin.Begin);
            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read);
            byte[] buffer = RetrieveBytesFromStream(cs, 1024);
            ms.Close();
            ArraySegment<byte> bytes = new ArraySegment<byte>(buffer);
            return bytes;
        }
        public Stream Decrypt(Stream stream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            MemoryStream ms = new MemoryStream();
            Stream compressStream = new CryptoStream(stream, des.CreateDecryptor(), CryptoStreamMode.Read);
            byte[] newByteArray = RetrieveBytesFromStream(compressStream, 1);
            compressStream.Close();
            return new MemoryStream(newByteArray);
        }
        public static byte[] RetrieveBytesFromStream(Stream stream, int bytesblock)
        {

            List<byte> lst = new List<byte>();
            byte[] data = new byte[1024];
            int totalCount = 0;
            while (true)
            {
                int bytesRead = stream.Read(data, 0, data.Length);
                if (bytesRead == 0)
                {
                    break;
                }
                byte[] buffers = new byte[bytesRead];
                Array.Copy(data, buffers, bytesRead);
                lst.AddRange(buffers);
                totalCount += bytesRead;
            }
            return lst.ToArray();
        }
        #endregion

        #region IDisposable 成员

        void IDisposable.Dispose()
        {
            if (des != null)
            {
                des.Clear();
            }
        }

        #endregion
    }
}

宿主

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Robin_Wcf_CustomMessageEncoder_SvcLib;
using System.ServiceModel.Channels;
using RobinLib;

namespace Robin_Wcf_CustomMessageEncoder_Host
{
    class Program
    {
        static void Main(string[] args)
        {
            //服务地址
            Uri baseAddress = new Uri("http://127.0.0.1:8081/Robin_Wcf_Formatter");
            ServiceHost host = new ServiceHost(typeof(Service1), new Uri[] { baseAddress });
            //服务绑定
            ICollection<BindingElement> bindingElements = new List<BindingElement>();
            HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
            string key = "JggkieIw7JM=";
            string iv = "XdTkT85fZ0U=";
            CryptEncodingBindingElement textBindingElement = new CryptEncodingBindingElement(new BinaryMessageEncodingBindingElement(), key,iv);
            bindingElements.Add(textBindingElement);
            bindingElements.Add(httpBindingElement);
            CustomBinding bind = new CustomBinding(bindingElements);  
            host.AddServiceEndpoint(typeof(IService1), bind, "");
            if (host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
            {
                System.ServiceModel.Description.ServiceMetadataBehavior svcMetaBehavior = new System.ServiceModel.Description.ServiceMetadataBehavior();
                svcMetaBehavior.HttpGetEnabled = true;
                svcMetaBehavior.HttpGetUrl = new Uri("http://127.0.0.1:8001/Mex");
                host.Description.Behaviors.Add(svcMetaBehavior);
            }
            host.Opened += new EventHandler(delegate(object obj, EventArgs e)
            {
                Console.WriteLine("服务已经启动!");
            }); 
            host.Open();
            Console.Read();
        }
    }
}

客户端

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RobinLib;
using System.ServiceModel.Channels;
using Robin_Wcf_CustomMessageEncoder_ClientApp.ServiceReference1;

namespace Robin_Wcf_CustomMessageEncoder_ClientApp
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.Thread.Sleep(5300);
            ICollection<BindingElement> bindingElements = new List<BindingElement>();
            HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
            string key = "JggkieIw7JM=";
            string iv = "XdTkT85fZ0U=";
            CryptEncodingBindingElement textBindingElement = new CryptEncodingBindingElement(new BinaryMessageEncodingBindingElement(), key, iv);
            bindingElements.Add(textBindingElement);
            bindingElements.Add(httpBindingElement); 
            CustomBinding bind = new CustomBinding(bindingElements);  
            ServiceReference1.IService1 svc = new ServiceReference1.Service1Client(bind, new System.ServiceModel.EndpointAddress("http://127.0.0.1:8081/Robin_Wcf_Formatter"));
            string pres = svc.GetData(10);
            Console.WriteLine(pres);
            CompositeType ct = svc.GetDataUsingDataContract(new CompositeType());
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            for (int i = 0; i < 1000000; i++)
            {
                byte[] buffer = BitConverter.GetBytes(i);
                ms.Write(buffer, 0, buffer.Length);
            }
            System.IO.Stream stream = svc.GetStream(ms);
            Console.Read();
        }
    }
}

运行效果图:

image

image

 

生成key和iv的方法为:

public void GenerateKey(out string key, out string iv)
{
    key = "";
    iv = "";
    using (DESCryptoServiceProvider des_o = new DESCryptoServiceProvider())
    {
        des_o.GenerateIV();
        des_o.GenerateKey();
        iv = Convert.ToBase64String(des_o.IV);
        key = Convert.ToBase64String(des_o.Key);
    }
}

项目文档:点击这里下载

posted @ 2010-04-13 20:18 Robin Zhang 阅读(...) 评论(...) 编辑 收藏