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();
}
}
}
运行效果图:
生成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);
}
}
项目文档:点击这里下载