AES-cbc-ctr

48次阅读

共计 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;
}


正文完
 0