/// summary
/// DES数据加密
/// /summary
/// param name="targetValue"目标值/param
/// param name="key"密钥/param
/// returns加密值/returns
public static string Encrypt(string targetValue, string key)
{
if (string.IsNullOrEmpty(targetValue))
{
return string.Empty;
}
var returnValue = new StringBuilder();
var des = new DESCryptoServiceProvider();
byte[] inputByteArray = Encoding.Default.GetBytes(targetValue);
// 通过两次哈希密码设置对称算法的初始化向量
des.Key = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile
(FormsAuthentication.HashPasswordForStoringInConfigFile(key, "md5").
Substring(0, 8), "sha1").Substring(0, 8));
// 通过两次哈希密码设置算法的机密密钥
des.IV = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile
(FormsAuthentication.HashPasswordForStoringInConfigFile(key, "md5")
.Substring(0, 8), "md5").Substring(0, 8));
var ms = new MemoryStream();
var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
foreach (byte b in ms.ToArray())
{
returnValue.AppendFormat("{0:X2}", b);
}
return returnValue.ToString();
}
/// summary
/// DES数据解密
/// /summary
/// param name="targetValue"/param
/// param name="key"/param
/// returns/returns
public static string Decrypt(string targetValue, string key)
{
if (string.IsNullOrEmpty(targetValue))
{
return string.Empty;
}
// 定义DES加密对象
var des = new DESCryptoServiceProvider();
int len = targetValue.Length / 2;
var inputByteArray = new byte[len];
int x, i;
for (x = 0; x len; x++)
{
i = Convert.ToInt32(targetValue.Substring(x * 2, 2), 16);
inputByteArray[x] = (byte)i;
}
// 通过两次哈希密码设置对称算法的初始化向量
des.Key = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile
(FormsAuthentication.HashPasswordForStoringInConfigFile(key, "md5").
Substring(0, 8), "sha1").Substring(0, 8));
// 通过两次哈希密码设置算法的机密密钥
des.IV = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile
(FormsAuthentication.HashPasswordForStoringInConfigFile(key, "md5")
.Substring(0, 8), "md5").Substring(0, 8));
// 定义内存流
var ms = new MemoryStream();
// 定义加密流
var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
return Encoding.Default.GetString(ms.ToArray());
}
一、优点不同:
ECB模式
1、简单;
2、有利于并行计算;
3、误差不会被传送;
CBC模式:
1、不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
二、缺点不同:
ECB模式
1、不能隐藏明文的模式;
2、可能对明文进行主动攻击;
CBC模式:
1、不利于并行计算;
2、误差传递;
3、需要初始化向量IV
三、概念不同
1、ECB模式又称电子密码本模式:Electronic codebook,是最简单的块密码加密模式,加密前根据加密块大小(如AES为128位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。
2、密码分组链接(CBC,Cipher-block chaining)模式,由IBM于1976年发明,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量IV。
扩展资料:
1976年,IBM发明了密码分组链接(CBC,Cipher-block chaining)模式。在CBC模式中,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。
若第一个块的下标为1,则CBC模式的加密过程为:
Ci = Ek (P _ Ci-1), C0 = IV.
而其解密过程则为:
Pi = Dk (Ci) _Ci-1, C0 = IV.
CBC是最为常用的工作模式。它的主要缺点在于加密过程是串行的,无法被并行化,而且消息必须被填充到块大小的整数倍。解决后一个问题的一种方法是利用密文窃取。
注意在加密时,明文中的微小改变会导致其后的全部密文块发生改变,而在解密时,从两个邻接的密文块中即可得到一个明文块。因此,解密过程可以被并行化,而解密时,密文中一位的改变只会导致其对应的明文块完全改变和下一个明文块中对应位发生改变,不会影响到其它明文的内容。
参考资料来源:百度百科-ECB模式
参考资料来源:百度百科-CBC
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace Sens.Security
{
#region class - hDES
public class HDES
{
#region private field
/// summary
/// 密钥加密算法,默认为SHA1
/// /summary
private HashAlgorithm KeyHash;
private Listint supportedKeySize;
private Encoding _encoding = Encoding.Default;
private DES des;
#endregion
#region constructor
public HDES(string key, DES des)
{
if (des == null)
throw new Exception("des不能为null");
else
this.des = des;
if (string.IsNullOrEmpty(key))
throw new Exception("密钥不能为空");
//获取支持的密钥长度
this.supportedKeySize = new Listint(SupportedKeySize);
// 初始化默认的key的加密方式
this.KeyHash = SHA1.Create();
this.StringKey = key;
}
#endregion
#region public properties
/// summary
/// 获取或设置文本编码的方式
/// /summary
public Encoding encoding
{
set { _encoding = value == null ? Encoding.Default : value; }
get { return _encoding; }
}
/// summary
/// 通过字符串设置密钥
/// /summary
public string StringKey
{
set
{
if (string.IsNullOrEmpty(value))
throw new Exception("密钥不能为空");
byte[] keyHash = KeyHash.ComputeHash(encoding.GetBytes(value));
byte[] tmp = new byte[8];
for (int i = 0; i 8; i++)
{
tmp[i] = keyHash[i];
}
this.Key = tmp;
for (int i = 8; i 16; i++)
{
tmp[i - 8] = keyHash[i];
}
this.IV = tmp;
}
}
/// summary
///
/// /summary
public byte[] Key
{
set
{
if (!supportedKeySize.Contains(value.Length * 8))
throw new Exception("密钥长度不对");
this.des.Key = value;
}
get { return this.Key; }
}
/// summary
/// 设置对称加密算法的初始化向量
/// /summary
public byte[] IV
{
set
{
if (!supportedKeySize.Contains(value.Length * 8))
throw new Exception("向量长度不对");
this.des.IV = value;
}
get { return this.IV; }
}
/// summary
/// 获取密钥大小
/// /summary
public int KeySize
{
get { return des.KeySize; }
}
/// summary
/// 获取支持的密钥大小
/// /summary
public KeySizes[] LegalKeySizes
{
get { return des.LegalKeySizes; }
}
/// summary
/// 获取支持的块大小
/// /summary
public KeySizes[] LegalBlockSizes
{
get { return des.LegalBlockSizes; }
}
/// summary
/// 获取支持的密钥大小
/// /summary
public int[] SupportedKeySize
{
get
{
Listint tmp = new Listint();
int step = 0;
foreach (KeySizes item in des.LegalKeySizes)
{
if (item.SkipSize == 0)
if (item.MaxSize == item.MinSize)
step = item.MaxSize;
else
step = item.MaxSize - item.MinSize;
else
step = item.SkipSize;
for (int i = item.MinSize; i = item.MaxSize; i += step)
{
if (!tmp.Contains(i))
tmp.Add(i);
}
}
return tmp.ToArray();
}
}
#endregion
#region public methods
#region 加解密字符串
/// summary
/// 加密字符串
/// /summary
/// param name="scr"/param
/// returns/returns
public string EncryptString(string scr)
{
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
byte[] inputByteArray = encoding.GetBytes(scr);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
StringBuilder ret = new StringBuilder();
foreach (byte b in ms.ToArray())
{
ret.AppendFormat("{0:X2}", b);
}
return ret.ToString();
}
/// summary
/// 解密字符串
/// /summary
/// param name="scr"/param
/// returns/returns
public string DecryptString(string scr)
{
byte[] inputByteArray = new byte[scr.Length / 2];
for (int x = 0; x scr.Length / 2; x++)
{
int i = (System.Convert.ToInt32(scr.Substring(x * 2, 2), 16));
inputByteArray[x] = (byte)i;
}
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
StringBuilder ret = new StringBuilder();
return encoding.GetString(ms.ToArray());
}
#endregion
#region 加解密文件
/// summary
/// 加密文件
/// /summary
/// param name="filePath"要加密的文件位置/param
/// param name="savePath"加密后文件保存到的位置/param
/// returns/returns
public bool EncryptFile(string filePath, string savePath)
{
FileStream fs = File.OpenRead(filePath);
byte[] inputByteArray = new byte[fs.Length];
fs.Read(inputByteArray, 0, (int)fs.Length);
fs.Close();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
fs = File.OpenWrite(savePath);
foreach (byte b in ms.ToArray())
{
fs.WriteByte(b);
}
fs.Close();
cs.Close();
ms.Close();
return true;
}
/// summary
/// 解密文件
/// /summary
/// param name="filePath"要解密的文件/param
/// param name="savePath"解密后保存到的位置/param
/// param name="keyStr"/param
/// returns/returns
public bool DecryptFile(string filePath, string savePath)
{
FileStream fs = File.OpenRead(filePath);
byte[] inputByteArray = new byte[fs.Length];
fs.Read(inputByteArray, 0, (int)fs.Length);
fs.Close();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
fs = File.OpenWrite(savePath);
foreach (byte b in ms.ToArray())
{
fs.WriteByte(b);
}
fs.Close();
cs.Close();
ms.Close();
return true;
}
#endregion
#endregion
}
#endregion
}
虽然这样的代码很垃圾,但毕竟你拿到了64bit的明文块了!你中间的方框中不正是64bit块组成的吗?这有什么疑问吗?
说到块,必须先说一下这些加密的方式就是块加密,把密文进行分组(块),然后依次进行加密,每块输出的长度也是固定的,最后把这些输出块依次排好也就是明文了。
不过,这里边有几个问题要考虑一下:
块的大小是固定的吗?
是预知的,并非固定。比如DES加密块就是64bit,而AES则是128~256之间变化,当然TDES则是128~192之间变化,同一种方式在执行时可以设置好块大小了然后执行。算法上块大小是被选择的,不固定,但运行时必须是设置为固定的值。DES只支持64bit。
密文若不是块的整数倍,也就是分组时最后一组不够块大小怎么办?
答案是使用填充(部分算法可以不填充),ModePadding属性可以控制填充方式。
对称加密中密钥指的是什么?
对称加密中我们学说的密钥指的是密钥串和向量串两部分,也就是Key和IV,Key被称为密钥串,这个概念其实与密钥是有差异的,但由于翻译上的习惯等多种原因,key也被称为密钥,但是对于对称加密中所指的密钥来说,指定的是Key和IV两部分,而不是专指的Key。虽然如此,称Key为对称加密中的密钥也不能算错,这与IV的功能有一定的关系。
对称加密中的IV的作用是什么?
IV我们称之为初始向量或向量,引用这个值的作用类似于MD5中的加盐(但不完全一样,这个留到下个问题中说),在加密过程中会存在同一内容相似密文块多的情况,这样情况下,不同的加密方式(C#默认电子本CBC加密)会有异同,若使用相同的块加密会导致加密结果相似,从而通过统计方式进行反解,所以有些加密方式是通过向量给以变化,然后上一块的结果会变成下一块的IV使用(CBC加密,这样相同块由于输入的IV不同,明文也不会相同,但这种情况下会需要IV,体现了初始向量概念中初始的概念),而有些由是每个块使用相同的IV(初始IV),更有甚者根本没有使用IV。这些具体的要求要看具体的加密方式(模式)。但由于DES对象会检查,所以在C#中无论是否使用都检查IV的值。DESCryptoServiceProvider只是CSP容器实现DES而已。
使用完全一样的Key和IV,相同的加密算法与填充模式,对相同的密文加密,结果是一样的吗?
不一样!加密算法了为对抗反向工程,使用同一类对象加密相同的内容,结果也不尽相同(IV只是保密加密过程中块重叠问题,而这个是为了保证不同的加密过程结果不同,所以这个更象是MD5的不同加盐方式)。虽然加密后的结果是不同的,但解密都会是正确的结果。
对称加密和解密使用的是相同的算法吗?
这个很多人有误解,算法概念本身被含混了。严格来说,算法是相同的!密码学中的算法包含加密算法和解密算法(广义上还是签名算法和验签算法也是算法)!所以可以说加解密算法是一个,比如DES算法,AES算法等;狭义上的算法往往指的是加密/解密的具体实施算法,比如DES中的CBC等,其实更多的时候为了区分我们称之为加/解密模式或块算法或块模式。而更多的程序员却把实现(类库)中的函数(成员方法)称为算法——比如EnCrypto和DesCrypto之类的,这种其实是算是狭义中的加解密算法,更多的时候我们还是称之为方法而不是算法,假定出现重载时,你难道还称其为有多个“算法”吗?所以算法到底是加密方法是否相同,并不一定——有些精巧点的算法可能会相同——对称算法只是说使用的是相同的密钥,加密和解密时间相差不多,并不是说一定是方法相同!不少人以为对称算法中使用了相同的密钥,所使用的方法必须是同一个——没这回事!比如我封装一个:Crypto(byte[] orgData, byte[] keyIv, bool enCrypto); 最后一个使用布称或枚举让你选择是加密还是解密,这不就算是所谓的一个方法了吗?所以说实现(类中的方法)与加密中理论完全是两码事,很多人喜欢往一块混!
加密学中的位指的是bit吗?
确实是!比如64bit,其实就是8Bytes(Byte[8])而已。很多人在问,为什么加密出来却是16byte?比如上文中的16个十六进制字符?其实是完全扯*蛋——byte[8]如果用16进制表示,每4个bit用0~F表示,正好是16个,很多人喜欢把十六进制与字符串乱整一通(基本知识的问题),把十六进制转成字符,然后再大言不惭地说,就是16个字符嘛——确实是,问题在于十六进制转成字符后,把每个4bit给转成了8bit表示出来了!所以自己都不知道多少bit了!十六进制的"FF"其实只是一个字节(1111 1111),转成字符串“FF”就是"0100 0110 0100 0110"了(0100 0110是字符F的编码)!
其实加/解密涉及的内容并不多,比如上文的几个问题就基本上全是对称加密的一些重点了,然后要有处理字节的能力就可以了!别动不动转成字符串看看,那对你没有什么好处!很多应用程序员都喜欢把字节处理成字符串,然后转来转去浪费计算机性能!举个例子来说,把一个文本文件加密,加密后的结果就使用二进制方式直接存储就可以了,解密时直接读二进制文件。为什么非要转成十六进制字符呢?我知道打开后是乱码——就算是你保存成不乱码的十六进制,除了文件大小增加一倍外,你还自己真能看懂?可能其他人会跟题主一样说,我不会犯这样的毛病——那么Key和IV为什么会是Encoding.GetBytes()?本身的设计是key和IV的容量都是非常的大的——现在就剩下一些可显示字符了!加上标点才85个而已(如果只是数字和字母只有36个了),按Key加密方法来算只有85^8了,事实设计了64bit,也就是2^64方个!为什么这个参数不是byte[8]?就算你想使用户接口,也应该是用户接口的成员方法处理。
所以,Key/IV输入的一定要是Byte[],就算是图形界面与方法不能直接交互,那么你可以设计一个密码的输入框,然后把用户输入的内容MD5,前64bit做Key后64bit做IV不是更好么?
如果我说没有区别你会信吗?
但答案还真是这样,两者没有任何区别的,只不过实现的语言代码不同而已。
那么java与dot net之间的DES是否可以通用?答案也是完全通用。无论是Java的DES加密还是dot net的DES回密,均可以使用另一种语言且不限于Java或dot net解密。够明白吗?
DES其实只是一个算法,加密与解密我们都知道算法与密码是分离的。算法是公开的,都可以用,而密码是独立于算法的。所以DES在不同的语言中实现的算法根本就是一样的——也正是因为如此不管何种语言都是通用的(除非伪DES,要知道DES算法网上本身能搜到而且是一个标准,最先是由美国安全部门公开的)
再说一下,为什么有人“通”用不起来的原因。DES其实有CBC之类的参数的,也就是针对加密块选用的不同的加密手段。正是这个参数的原因,不同的语言中使用不同的参数做为默认值,所以使用默认的方式进行让两个串进行加解密肯定是不同的。DES使用一种模式(方法)加密,用另一种模式(方法)进行解密能得到正确的结果吗?一些人不怪自己的学艺不精,反说是两种语言的DES不通用(这也就是为什么百度上会出现诸多说java和dot net的DES加密方法不通用的原因)。
即便是自己使用的DES加密的代码也是通用的(前提你要遵守DES分开算法),但不要“重复实现已经实现的东西(专业术语叫造轮子)”。
附:
DES.Model属性取值
CBC 密码块链 (CBC) 模式引入了反馈。每个纯文本块在加密前,通过按位“异或”操作与前一个块的密码文本结合。这样确保了即使纯文本包含许多相同的块,这些块中的每一个也会加密为不同的密码文本块。在加密块之前,初始化向量通过按位“异或”操作与第一个纯文本块结合。如果密码文本块中有一个位出错,相应的纯文本块也将出错。此外,后面的块中与原出错位的位置相同的位也将出错。
ECB 电子密码本 (ECB) 模式分别加密每个块。这意味着任何纯文本块只要相同并且在同一消息中,或者在用相同的密钥加密的不同消息中,都将被转换成同样的密码文本块。如果要加密的纯文本包含大量重复的块,则逐块破解密码文本是可行的。另外,随时准备攻击的对手可能在您没有察觉的情况下替代和交换个别的块。如果密码文本块中有一个位出错,相应的整个纯文本块也将出错。
OFB 输出反馈 (OFB) 模式将少量递增的纯文本处理成密码文本,而不是一次处理整个块。此模式与 CFB 相似;这两种模式的唯一差别是移位寄存器的填充方式不同。如果密码文本中有一个位出错,纯文本中相应的位也将出错。但是,如果密码文本中有多余或者缺少的位,则那个位之后的纯文本都将出错。
CFB 密码反馈 (CFB) 模式将少量递增的纯文本处理成密码文本,而不是一次处理整个块。该模式使用在长度上为一个块且被分为几部分的移位寄存器。例如,如果块大小为 8 个字节,并且每次处理一个字节,则移位寄存器被分为 8 个部分。如果密码文本中有一个位出错,则一个纯文本位出错,并且移位寄存器损坏。这将导致接下来若干次递增的纯文本出错,直到出错位从移位寄存器中移出为止。
CTS 密码文本窃用 (CTS) 模式处理任何长度的纯文本并产生长度与纯文本长度匹配的密码文本。除了最后两个纯文本块外,对于所有其他块,此模式与 CBC 模式的行为相同。
DES.Padding属性的取值
None 不填充。
PKCS7 PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
Zeros 填充字符串由设置为零的字节组成。
ANSIX923 ANSIX923 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节均填充数字零。
ISO10126 ISO10126 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节填充随机数据。
当Mode不同时,解密的内密内容能与相同吗?PaddingMode不同时,解密的内容的结尾部分能相同吗(填充结果只涉及到最后的一个块).所以当不管何种语言使用相同的Mode及PaddingMode时,加解密的结果是相同的(当然不排除部分语言不实现全部的Mode和PaddingMode)但,基本的都是实现了的,所以基本上任何两种语言之间的DES都可以实现相同的加解密结果!而java和dot net中的DES显然指的是算法,两者是相同的,可以随意使用(Java中dot net中的Mode默认值是不同的,一定要设置相同的Mode和PaddingMode才可以的,不要双方都采用默认值,那样真的通不起来)
DES加密算法是对密钥进行保密,而公开算法,即只有拥有相同密钥的人才能解密。DES加密算法对密钥有要求,必须是8个字符,如abcdefgh这样的。
java中的Des算法中要求向量是8个字符,但对方给的向量是32个字符。解密时会出现如下错误:
java.security.InvalidAlgorithmParameterException:WrongIVlength:mustbe8byteslong
找了好多解决方法都没有成功。第二天跟对方沟通,对方确定他们就是用上面给出的key和向量进行加密。后来对方把他们加密的代码发给我们,打开一看,竟然是c#语言实现的。查看他们的代码,确实传进去32个字符的向量。这边又没有c#的开发环境,无法跟进去看具体的实现。这个活本来是我帮前一个项目的同事解决,当时挺晚了,我同事就说要不明天再想办法了。我突然想试试,是不是c#传的是32位字符,但只用了前8个呢。立马在这边用前8个字符作为向量解密,结果非常让要高兴,解密成功。