采用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=131string 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;}