采用vs2010 + Crypto++(C++)
//0->0x30(48) 1->0x31(49)
//将字符串转换为对应16进制数(一般是两位)
string strToHex(string str)
{
stringstream ss;
ss << hex << setfill('0');
for(int i=0;i
//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加密
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 加密
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;
}