共计 5838 个字符,预计需要花费 15 分钟才能阅读完成。
采用 vs2010 + Crypto++(C++)
配置 Crypto++
//0->0x30(48) 1->0x31(49)
// 将字符串转换为对应 16 进制数(一般是两位)
string strToHex(string str)
{
stringstream ss;
ss << hex << setfill('0');
for(int i=0;i<str.size();i++){// setw(2) 占两位,setfill('0') 空位填充 0
ss << setw(2) << (int)(unsigned char)str[i];
}
return ss.str();}
//0x30->0 0x31->1
// 将 16 进制数 (一般两位为位为一个分割) 转换为对应字符串
string hexToStr(string hex)
{
string str;
for(string::size_type i = 0; i < hex.length(); i += 2) {string tempStr = hex.substr(i, 2);
// string to int
unsigned char ch = (unsigned char)stoi(tempStr, nullptr, 16);
str.push_back(ch);
}
return str;
}
PKCS5 是按 8 字节分组对数据进行填充的
AES 实际按 16 字节大小分组(PKCS5)
PKCS5 为 PKCS7 的一个子集
h<0x07><0x07><0x07><0x07><0x07><0x07><0x07> 7
he<0x06><0x06><0x06><0x06><0x06><0x06> 6
hel<0x05><0x05><0x05><0x05><0x05> 5
hell<0x04><0x04><0x04><0x04> 4
hello<0x03><0x03><0x03> 3
hello <0x02><0x02> 2
hello w<0x01> 1
hello wo<0x08><0x08><0x08><0x08><0x08><0x08><0x08><0x08> 8
// 分组后填充
string padding(string plaintext)
{
string lastBlock;
int len = plaintext.length();
//16 - 最后一组长度
int paddingNum = AES::BLOCKSIZE - len % AES::BLOCKSIZE;
// 完整组长度
int quotient = len / AES::BLOCKSIZE;
// 获取最后一组长度
lastBlock = plaintext.substr(AES::BLOCKSIZE * quotient, len % AES::BLOCKSIZE);
//arr 形如 hello <0x02><0x02>
for(int i = 0; i < AES::BLOCKSIZE - len % AES::BLOCKSIZE; i++) {lastBlock.push_back((unsigned char)paddingNum);
}
return plaintext.substr(0, AES::BLOCKSIZE * quotient) + lastBlock;
}
// 计数器自增 256 进制 "123"+254=131
string counterIncrement(string counter, int n)
{
string res = counter;
int addend = n;
for(int i = counter.length() - 1; i >= 0; i--) {unsigned char tempChar = counter[i];
if((int)tempChar + addend > 255) {
tempChar = tempChar + addend;
addend = 1;
} else {
tempChar = tempChar + addend;
addend = 0;
}
res[i] = tempChar;
}
return res;
}
CBC
CBC 测试参数
CBC 加密
string CBCencrypt(string plaintext, string key, string vi, string ciphertext)
{plaintext = padding(plaintext);
//ciphertext 为空 此时初始化为 vi(用户设定为 16 位随机字符串)
ciphertext += vi;
key = hexToStr(key);
vi = hexToStr(vi);
// 获取多少个组
int multiple = plaintext.length() / AES::BLOCKSIZE;
AESEncryption aesEncryptor;
aesEncryptor.SetKey((byte*)key.c_str(), key.length());
for(int i = 0; i < multiple; i++) {string plaintextBlock = plaintext.substr(i * AES::BLOCKSIZE, AES::BLOCKSIZE);
string xorBlock;
//outblock 存放一组的中间过程
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
for(int j = 0; j < AES::BLOCKSIZE; j++) {xorBlock.push_back(plaintextBlock[j] ^ (unsigned char)vi[j]);
}
aesEncryptor.ProcessBlock((byte*)xorBlock.c_str(), outBlock);
vi = "";
// unsigned char[] 转 string 不要通过这个方式:vi = (char *) outBlock
//outblock 现在存放的是一组密文传递给 vi
for(int j = 0; j < AES::BLOCKSIZE; j++) {vi.push_back(outBlock[j]);
}
vi = vi.substr(0, AES::BLOCKSIZE);
ciphertext += strToHex(vi);
}
return ciphertext;
}
CBC 解密
string CBCdecrypt(string ciphertext, string key, string vi,string plaintext)
{
// 原始 key 为 16 进制形式,需按字节转换为 char
key = hexToStr(key);
ciphertext = hexToStr(ciphertext);
// 随机生成 16 位 vi
//string vi = ciphertext.substr(0, AES::BLOCKSIZE);
ciphertext = ciphertext.substr(AES::BLOCKSIZE, ciphertext.length() - AES::BLOCKSIZE);
int multiple = ciphertext.length() / AES::BLOCKSIZE;
AESDecryption aesDecryptor;
aesDecryptor.SetKey((byte*)key.c_str(), key.length());
for(int i = 0; i < multiple; i++) {
// 分组密文 截取一组放入数组
string ciphertextBlock = ciphertext.substr(i * AES::BLOCKSIZE, AES::BLOCKSIZE);
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
aesDecryptor.ProcessBlock((byte*)ciphertextBlock.c_str(), outBlock);
// AES 输出结果与上组密文或 vi 异或,得到明文
for(int j = 0; j < AES::BLOCKSIZE; j++) {plaintext.push_back(outBlock[j] ^ (unsigned char)vi[j]);
}
vi = ciphertextBlock;
}
// 解密后,最后一组明文单独处理
string lastBlock = plaintext.substr((multiple - 1) * AES::BLOCKSIZE, AES::BLOCKSIZE);
// 从字符串最后一个字符获取填充字符
int paddingNum = (unsigned char)lastBlock[AES::BLOCKSIZE - 1];
// 把填充字符从明文中去掉
for(int i = 0; i < paddingNum; i++) {
// 若填充字符出现不同,则说明给定密文有误
if(plaintext.back() != paddingNum) {return "Ciphertext is invalid!";}
plaintext.pop_back();}
return plaintext;
}
CTR
CTR 测试参数
CTR 加密
string CTRencrypt(string plaintext, string key, string counter, string ciphertext)
{
ciphertext += counter;
key = hexToStr(key);
counter = hexToStr(counter);
int multiple = plaintext.length() / AES::BLOCKSIZE;
AESEncryption aesEncryptor;
aesEncryptor.SetKey((byte*)key.c_str(), key.length());
for(int i = 0; i < multiple; i++) {string plaintextBlock = plaintext.substr(i * AES::BLOCKSIZE, AES::BLOCKSIZE);
string xorBlock;
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
aesEncryptor.ProcessBlock((byte*)counter.c_str(), outBlock);
for(int j = 0; j < AES::BLOCKSIZE; j++) {xorBlock.push_back(outBlock[j] ^ (unsigned char)plaintextBlock[j]);
}
ciphertext += strToHex(xorBlock);
counter = counterIncrement(counter, 1);
}
int residueLen = plaintext.length() - multiple * AES::BLOCKSIZE;
string residuePlaintext = plaintext.substr(multiple * AES::BLOCKSIZE, residueLen);
string xorBlock;
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
aesEncryptor.ProcessBlock((byte*)counter.c_str(), outBlock);
for(int j = 0; j < residueLen; j++) {xorBlock.push_back(outBlock[j] ^ (unsigned char)residuePlaintext[j]);
}
ciphertext += strToHex(xorBlock);
return ciphertext;
}
CTR 解密
string CTRdecrypt(string ciphertext,string counter, string key, string plaintext)
{key = hexToStr(key);
ciphertext = hexToStr(ciphertext);
int multiple = ciphertext.length() / AES::BLOCKSIZE;
AESEncryption aesEncryptor;
aesEncryptor.SetKey((byte*)key.c_str(), key.length());
for(int i = 0; i < multiple; i++) {string ciphertextBlock = ciphertext.substr(i * AES::BLOCKSIZE, AES::BLOCKSIZE);
string xorBlock;
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
aesEncryptor.ProcessBlock((byte*)counter.c_str(), outBlock);
// 密文和 AES 加密结果异或,得到明文
for(int j = 0; j < AES::BLOCKSIZE; j++) {xorBlock.push_back(outBlock[j] ^ (unsigned char)ciphertextBlock[j]);
}
plaintext += xorBlock;
// 计数器自增
counter = counterIncrement(counter, 1);
}
// 最后的分组可能不完整,单独输出 这里没有 padding
int residueLen = ciphertext.length() - multiple * AES::BLOCKSIZE;
string residueCiphertext = ciphertext.substr(multiple * AES::BLOCKSIZE, residueLen);
string xorBlock;
unsigned char outBlock[AES::BLOCKSIZE];
memset(outBlock, 0, AES::BLOCKSIZE);
aesEncryptor.ProcessBlock((byte*)counter.c_str(), outBlock);
for(int j = 0; j < residueLen; j++) {xorBlock.push_back(outBlock[j] ^ (unsigned char)residueCiphertext[j]);
}
plaintext += xorBlock;
return plaintext;
}
正文完