Windows C++關於AES-CBC-PKCS7Padding加密解密

首先,需要了解的是,不管什麼語言,只要是aes加密解密,所有的參數(包括密鑰/向量/基數)都對了,加密和解密結果一定是一樣的。

在閱讀的過程中,可能會有些詞,看不懂是啥意思,比如 加密塊大小BlockSize,加密向量IV,密鑰Key,不着急,之後會有解釋 ~

C++使用的是三方庫OpenSSL,文章最後,會提供openssl編譯過的庫告誡下試圖根據某些語言中的Aes-CBC代碼過程,翻譯成C++版本過程的程序員,先把所有流程看完,看看是否某些接口,是用匯編實現,如果你是彙編高手,當我沒說~

把aes-cbc-pkcs7padding拆成幾部分分析:

1.aes,沒什麼好說的,就是一類加密技術算法,包括若干種算法模式,cbc,ecb,ctr,ocf,cfb等;

2.cbc,是aes的其中一種算法模式;

3.pkcs7padding,只是aes對需要加密的原數據,進行相應的字節補齊,一般是根據加密塊大小方式補齊,比如塊大小BlockSize=16,則需要16字節補齊,當然也有unpkcs7padding去掉補齊字節

比如原數據長度爲7,則需要補齊9字節,湊成16字節;

比如原數據長度爲12,則需要補齊4字節,湊成16字節;

比如原數據長度爲24,則需要補齊8字節,湊成32字節;

總之,把原數據長度補上幾個字節,使數據長度必須是BlockSize的倍數;

但有一種特殊情況,就是原數據長度本來就是BlockSize的倍數,此時算法要求,再給補BlockSize字節

那麼,問題來了,的數據是啥呢??

就是插入的自己值就是補的字節個數,比如補5個字節,則5個字節的字符值都是5,比如補7個字節,則7個字節的字符值都是7;

以C++爲例,希望認真看下,也許有人會跟我踩一樣的低級坑呢( ̄▽ ̄)"

//簡單的補齊demo

std::string s="15100000000" //長度11,需要補齊5字節
s.append(5,5)
s="15100000000\5\5\5\5\5"  //正確結果

//親們,我在這裏踩過坑,當然,我相信偶爾也會出現跟我一樣踩坑的
//我在調試的時候,直接手動給字符串補齊,得到以下結果,當然,以下得到的結果是錯誤示範!!!!
//s="1510000000055555"  //錯誤

pkcs7padding和unpkcs7padding實現如下:

bool Pkcs7padding(std::string& sInOut, int nBlockSize)
{
    if(nBlockSize <= 0)
        return false;
    int nPadding = nBlockSize - sInOut.length() % nBlockSize;//補齊個數
    sInOut.append(5,5);
    return true;
}

bool UnPkcs7padding(std::string& sIn)
{
    if(sIn.length() <= 0)
        return false;
    int nLen = static_cast<int>(sIn.back());
    if (sIn.length() < nLen)
        return false;

    sIn.erase(sIn.end() - nLen,sIn.end());
    return true;
}

-------------------------------華麗分割線---------------------------

現在來說下AES-CBC加密是怎樣的過程。先套用網上其他人做好的圖,希望不會被告侵權,如果原作者不樂意被引用,請留言,我會第一時間刪掉的,不愛畫圖,懶~

先看以下圖,有個直觀的瞭解,再配合之後的講解,可能會讓大腦“舒服”一點。

首先應該瞭解,CBC加密,是把原數據切割成若干個塊,也就是上面說的加密塊大小BlockSize,通常塊大小爲16;這裏還涉及到IV,也就是加密向量,大小一般也是BlockSize個字節,密鑰Key和加密向量。。。不太好解釋,只要知道是參加加密過程的基礎條件,或者理解爲密鑰是“鑰匙”,向量是“進入下一關卡的咒語”。

比如需要加密的原數據:"123456789123456789123456789"

補齊之後數據爲:"123456789123456789123456789\5\5\5\5\5"   (並非都用\5補齊,只是例子恰巧需要補5字節)

加密塊0:"1234567891234567"

加密塊1:"89123456789\5\5\5\5\5"

看圖理解,使用初始向量IV和密鑰,經過某些加密過程,對每個加密塊進行加密,且是按順序對加密塊進行加密,因爲下一個加密塊所使用的向量,是從前一塊的密文塊中得到的,比如圖中指示,密文塊0被當作加密塊1的向量。

最後,得到的密文塊拼接起來,就是完整的加密數據啦!!!!

由於我們是調用三方庫openssl接口,就不去糾結加密器中做了啥事,會用即可!

以下代碼會引導讀者完成加密和解密過程

//早期寫的demo,把很多過程簡化了,讀者自行改造
//但是!!
//但是!!!
//但是!!!!
//認真看註釋,也許能幫助你解決問題呢!
//注:key一般是 16 24 32 字節的字符串
//當然,還需要引用頭文件 aes.h
bool AesCBC()
{
    char* str = "123456789";//原數據
    size_t nLen = strlen(str);
    int nBlockSize = 16;//塊大小
    char key[] = "adf5e4fa1d3f24sd5f4das46fa4a4sd3"; //我這裏是隨便給的key,共32字節
    char iv[] = "adf5e4fa1d3f24sd";//爲了省事這裏的向量使用key的前16字節,當然也可以是其他的

    //重點來了
    //第二個參數是怎麼得出來的?
    //我之前也是從網上拷貝一份代碼,對某個數據進行加密,但死活和golang加密結果不一致,糾結兩天!
    //因爲網上的代碼,第二參數給的是128,我就習慣性認爲是固定數值,或者認爲是nBlockSize*8=128,
    //最後去翻看golang的源碼,發現是key的長度*8,也就是32*8=256,
    //怎麼發現的呢,線索是128:AES_KEY.round=10  256:AES_KEY.round=14,
    //糾結兩天時間,我恨吶!
    //沒一篇文章告訴我,這個坑爹的128和256是怎麼算出來的。。。
    //自己技術水平不行,不怪誰,碎碎念。。。

    AES_KEY aes;
    if (AES_set_encrypt_key((unsigned char*)key, 256, &aes) < 0)
        return false;

    StringA sIn = str;
    int nPadd = nBlockSize - sIn.length() % nBlockSize;
    sIn.append(size_t(nPadd), char(nPadd));//補齊,本來應該用獨立接口進行補齊,但懶。。。

    //這裏的out大小,是跟補齊後的sIn.length()一樣的大小
    //由於我給的原數據不超過nBlockSize,所以直接給16字節
    //不知道加密的彙編過程做了啥,out數據塊太大的話,也會引起異常
    BYTE out[16] = { 0 };

    AES_cbc_encrypt((unsigned char*)sIn.c_str(), (unsigned char*)out, sIn.length(), &aes, (unsigned char *)iv, AES_ENCRYPT);

    char aBuf[128] = { 0 };
    int len = 128;

    //這個步驟跟加密沒必然關係,看具體需求,是否進行轉碼
    Base64Encode(out, 16, aBuf, &len);

    printf("out:%s", aBuf);
    return true;

    //=================================================//

    //本來到這裏應該結束
    //但,有加密肯定得有解密吧?
    //另外開一個接口?
    //懶。。。。
    if (AES_set_decrypt_key((unsigned char*)key, 256, &aes) < 0)
        return false;

    memcpy((char*)in, (char*)out, 16);
    len = 16;
    memcpy((char*)iv, (char*)key, 16);
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    
    //是的,改幾個接口和參數,就是解密過程,
    //最後還有一步,就是去除補齊字節,
    //就不詳細寫了,懶。。。。


}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章