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
}
头文件
#import CommonCrypto/CommonCryptor.h
NSString *const kInitVector = @"ffGGtsdfzxCv5568";
NSString *const DESKey = @"gg356tt8g5h6j9jh";
+ (NSString *)encodeDesWithString:(NSString *)str{
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
size_t plainTextBufferSize = [data length];
const void *vplainText = (const void *)[data bytes];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSizeDES) ~(kCCBlockSizeDES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
const void *vkey = (const void *) [DESKey UTF8String];
const void *vinitVec = (const void *) [kInitVector UTF8String];
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
vinitVec,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
movedBytes);
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
NSString *result = [myData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return result;
}
+ (NSString *)decodeDesWithString:(NSString *)str{
NSData *encryptData = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
size_t plainTextBufferSize = [encryptData length];
const void *vplainText = [encryptData bytes];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSizeDES) ~(kCCBlockSizeDES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
const void *vkey = (const void *) [DESKey UTF8String];
const void *vinitVec = (const void *) [kInitVector UTF8String];
ccStatus = CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
vkey,
kCCKeySizeDES,
vinitVec,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
movedBytes);
NSString *result = [[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)bufferPtr
length:(NSUInteger)movedBytes] encoding:NSUTF8StringEncoding];
return result;
}
第一步是用密钥初始化des
初始化的过程主要是用传入的密钥生成16对长度为48的Kn 子密钥
生成48位子密钥Kn的函数主要是 __create_sub_keys , 主要设计两个换位表pc1和pc2
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) 开始先用换位表生成56位的初始key值(同pc1表的位数)
之后划分成两部分self.L和self.R各28位,然后是一个循环16此的左移操作,最后用pc2换位表生成第一个子密钥Kn[0]
我们传入数据调用encrypt函数即可, DES.encrypt('flag{isisisikey}') 我们先来看encrypt函数
encrypt函数主要调用了crypt函数,继续跟进crypt函数,开始一部分是cbc模式获取iv的过程,这里先暂时不考虑cbc,直接看关键部分
这里就设计到分组加密的核心了,为什么DES又叫分组加密,有一操作是 block = self.__String_to_BitList(data[i:i+8]) 把加密数据每八个字节分成一个block,然后调用 __String_to_BitList 会将八字节字符转换为64bit的二进制,每个block再调用 __des_crypt 函数加密
开始几步和子密钥生成函数类似,用一个ip换位表初始化block,然后划分成self,L和self.R 各32位。
之后又是一个16轮的计算,我们分析一下每轮操作
self.R = self.__permutate(des.__expansion_table, self.R) 利用一个扩展表将32bit扩展成48位,扩展表:
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] 将48位的self.R 分成6*8为,之后一个循环就是经典的是s-box的置换操作
s-box盒一个八个,m是前后2bit,n是中间6bit, v是s-box的(n,m)处的值
self.R = self.__permutate(des.__p, Bn) 是P-box置换盒。 最后返回64bit的processed_block, 经过BitList_to_String函数处理就变成8字节的字符流了,最后把每个block分组join一块就是最后的密文。
我们再来总结一下这个过程
子密钥生成算法
des 加密算法
附上完整版des加解密算法脚本
您好,一般来说在线加密工具都是网页版的,des加密需要加密前明文和加密密钥,在网页的控件上填写好对应信息之后,点击加密按钮一般就可以看到加密后的加密结果。
这个问题主要涉及编码、输入格式、输出格式、加密模式这几个方面的问题,还有一些细节问题比如空格与回车。 首先是编码问题,在线的编码格式一般默认是UTF-8,因此如果网页编码不是UTF-8,则会导致加密的结果不一样。因为DES算法本质上是对二进制内容进行加密,同样的文字经过不同的编码映射成的二进制内容并不相同。 其次,是输入格式问题。一般在网页的输入是文本格式(Plain Text),但是许多教程为了方便理解,写的输入格式是16进制,比如 DES算法实例讲解 这篇文章里面主要用的是16进制格式作为讲解,对于许多在线工具,明文和密钥输入用的是文本格式。因此,在输入的时候一定要注意区分。 然后,是输出格式的问题。有些在线加密工具输出会自动进行Base64编码,这样结果和直接加密的结果完全不同。DES加密的密文是16进制格式的,无法一一对应成ASCII码。密文要么以16进制输出,要么输出一堆乱码,而Base64能将一个较长的16进制数组编码为一个字符串,方便处理。 最后,是加密模式的问题。DES本身采用的是ECB(电子密码本)模式,即将加密的数据分成若干组,每组的大小跟加密密钥长度相同,这样密文输出完全由明文和密钥决定。为了进一步加强安全性,有许多安全性扩展,就诞生了别的加密模式,比如加密块链模式CBC、加密反馈模式CFB等等。不同的模式加密结果也会完全不同。 在附带一点细节问题,即空格与回车的问题。尤其是在字符串处理的时候,有些字符串会带回车换行(0x0D 0x0A),这会造成最后一个64位字符块加密有些许差别。还有一些文本框自动(trigger)去除空格,就导致文本中的空格没有被计算在内,导致加密不同。
/*
* @param arrB 需要转换的byte数组
* @return 转换后的字符串
* @throws Exception 本方法不处理任何异常,所有异常全部抛出
*/
public static String byteArr2HexStr(byte[] arrB) throws Exception {
int iLen = arrB.length;
// 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
StringBuffer sb = new StringBuffer(iLen * 2);
for (int i = 0; i iLen; i++) {
int intTmp = arrB[i];
// 把负数转换为正数
while (intTmp 0) {
intTmp = intTmp + 256;
}
// 小于0F的数需要在前面补0
if (intTmp 16) {
sb.append("0");
}
sb.append(Integer.toString(intTmp, 16));
}
return sb.toString();
}
/*
* @param strIn 需要转换的字符串
* @return 转换后的byte数组
* @throws Exception 本方法不处理任何异常,所有异常全部抛出
*/
public static byte[] hexStr2ByteArr(String strIn) throws Exception {
byte[] arrB = strIn.getBytes();
int iLen = arrB.length;
// 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
byte[] arrOut = new byte[iLen / 2];
for (int i = 0; i iLen; i = i + 2) {
String strTmp = new String(arrB, i, 2);
arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
}
return arrOut;
}
/**
* 加密字节数组
*
* @param arrB
* 需加密的字节数组
* @return 加密后的字节数组
* @throws Exception
*/
@SuppressWarnings("restriction")
private static byte[] encrypt(byte[] arrB,String keyParameter) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
Key key = getKey(keyParameter.getBytes());
Cipher encryptCipher = Cipher.getInstance("DES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
return encryptCipher.doFinal(arrB);
}
/**
* 加密字符串
*
* @param strIn
* 需加密的字符串
* @return 加密后的字符串
* @throws Exception
*/
public static String encrypt(String strIn,String keyParameter) throws Exception {
return HexStrByteArrUtils.byteArr2HexStr(encrypt(strIn.getBytes(PiccConfig.PICC_INPUT_CHARSET),keyParameter));
}
/**
* 解密字节数组
*
* @param arrB
* 需解密的字节数组
* @return 解密后的字节数组
* @throws Exception
*/
@SuppressWarnings("restriction")
private static byte[] decrypt(byte[] arrB,String keyParameter) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
Key key = getKey(keyParameter.getBytes());
Cipher decryptCipher = Cipher.getInstance("DES");
decryptCipher.init(Cipher.DECRYPT_MODE, key);
return decryptCipher.doFinal(arrB);
}
/**
* 解密字符串
*
* @param strIn
* 需解密的字符串
* @return 解密后的字符串
* @throws Exception
*/
public static String decrypt(String strIn,String keyParameter) throws Exception {
return new String(decrypt(HexStrByteArrUtils.hexStr2ByteArr(strIn),keyParameter),PiccConfig.PICC_INPUT_CHARSET);
}