· 

128bitAESをゼロから書、、、かない

128bitのAES暗号化復号化を、、、windows使いならこっちでしょって方法でやったのをメモとして残す。ちなみに、C#からDLL関数を実行の回でつかったDLLはこの方法で作ったものです。特に自分の勉強にもならないんだけど、きっとIT業界では常識すぎるのか、あまり情報がないので。

参考サイト

トラスト・ソフトウェア・システム

 Microsoft Windowsアプリ開発

#include <windows.h>
#include <stdint.h>
#include <bcrypt.h>

//#include <stdio.h>

#pragma comment(lib,"Bcrypt.lib")

#define NT_SUCCESS(Status) (((NTSTATUS)(Status))>=0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)

int __stdcall AES128Encrypt(PBYTE plain,PBYTE key,PBYTE ciphered){
    BCRYPT_ALG_HANDLE hAesAlg=NULL;
    BCRYPT_KEY_HANDLE hKey=NULL;
    NTSTATUS status=STATUS_UNSUCCESSFUL;
    DWORD cbCipherText=0;
    DWORD cbPlainText=0;
    DWORD cbData=0;
    DWORD cbKeyObject=0;
    PBYTE pbCipherText=NULL;
    PBYTE pbPlainText=NULL;
    PBYTE pbKeyObject=NULL;
    size_t size_of_plain=16;
    size_t size_of_key=16;
    int ret;
    
    status=BCryptOpenAlgorithmProvider(&hAesAlg,BCRYPT_AES_ALGORITHM,NULL,0);
    if(!NT_SUCCESS(status)){ret=1;goto Cleanup;}

    status=BCryptGetProperty(hAesAlg,BCRYPT_OBJECT_LENGTH,(PBYTE)&cbKeyObject,sizeof(DWORD),&cbData,0);
    if(!NT_SUCCESS(status)){ret=2;goto Cleanup;}

    pbKeyObject=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbKeyObject);
    if(NULL==pbKeyObject){ret=3;goto Cleanup;}

    status=BCryptSetProperty(hAesAlg,BCRYPT_CHAINING_MODE,(PBYTE)BCRYPT_CHAIN_MODE_ECB,sizeof(BCRYPT_CHAIN_MODE_ECB),0);
    if(!NT_SUCCESS(status)){ret=4;goto Cleanup;}

    status=BCryptGenerateSymmetricKey(hAesAlg,&hKey,pbKeyObject,cbKeyObject,key,size_of_key,0);
    if(!NT_SUCCESS(status)){ret=5;goto Cleanup;}

    cbPlainText=size_of_plain;
    pbPlainText=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbPlainText);
    if(NULL==pbPlainText){ret=6;goto Cleanup;}

    memcpy(pbPlainText,plain,size_of_plain);

    status=BCryptEncrypt(hKey,pbPlainText,cbPlainText,NULL,NULL,0,NULL,0,&cbCipherText,BCRYPT_BLOCK_PADDING);
    if(!NT_SUCCESS(status)){ret=6;goto Cleanup;}

    pbCipherText=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbCipherText);
    if(NULL==pbCipherText){ret=8;goto Cleanup;}

    status=BCryptEncrypt(hKey,pbPlainText,cbPlainText,NULL,NULL,0,pbCipherText,cbCipherText,&cbData,BCRYPT_BLOCK_PADDING);
    if(!NT_SUCCESS(status)){ret=9;goto Cleanup;}

    memcpy(ciphered,pbCipherText,size_of_plain);

    ret=0;

Cleanup:
    if(hAesAlg){BCryptCloseAlgorithmProvider(hAesAlg,0);}
    if(hKey){BCryptDestroyKey(hKey);}
    if(pbCipherText){HeapFree(GetProcessHeap(),0,pbCipherText);}
    if(pbPlainText){HeapFree(GetProcessHeap(),0,pbPlainText);}
    if(pbKeyObject){HeapFree(GetProcessHeap(),0,pbKeyObject);}

    hAesAlg=NULL;
    hKey=NULL;
    pbCipherText=NULL;
    pbPlainText=NULL;
    pbKeyObject=NULL;

    //for(int i=0;i<16;i++){printf("%02X",plain[i]);}printf("\n");
    //for(int i=0;i<16;i++){printf("%02X",key[i]);}printf("\n");
    ///for(int i=0;i<16;i++){printf("%02X",ciphered[i]);}printf("\n");

    return ret;
}

int __stdcall AES128Decrypt(PBYTE ciphered,PBYTE key,PBYTE plain){
    BCRYPT_ALG_HANDLE hAesAlg=NULL;
    BCRYPT_KEY_HANDLE hKey=NULL;
    NTSTATUS status=STATUS_UNSUCCESSFUL;
    DWORD cbCipherText=0;
    DWORD cbPlainText=0;
    DWORD cbData=0;
    DWORD cbKeyObject=0;
    PBYTE pbCipherText=NULL;
    PBYTE pbPlainText=NULL;
    PBYTE pbKeyObject=NULL;
    size_t size_of_ciphered=16;
    size_t size_of_key=16;
    int ret;
    
    status=BCryptOpenAlgorithmProvider(&hAesAlg,BCRYPT_AES_ALGORITHM,NULL,0);
    if(!NT_SUCCESS(status)){ret=1;goto Cleanup;}

    status=BCryptGetProperty(hAesAlg,BCRYPT_OBJECT_LENGTH,(PBYTE)&cbKeyObject,sizeof(DWORD),&cbData,0);
    if(!NT_SUCCESS(status)){ret=2;goto Cleanup;}

    pbKeyObject=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbKeyObject);
    if(NULL==pbKeyObject){ret=3;goto Cleanup;}

    status=BCryptSetProperty(hAesAlg,BCRYPT_CHAINING_MODE,(PBYTE)BCRYPT_CHAIN_MODE_ECB,sizeof(BCRYPT_CHAIN_MODE_ECB),0);
    if(!NT_SUCCESS(status)){ret=4;goto Cleanup;}

    status=BCryptGenerateSymmetricKey(hAesAlg,&hKey,pbKeyObject,cbKeyObject,key,size_of_key,0);
    if(!NT_SUCCESS(status)){ret=5;goto Cleanup;}

    cbCipherText=size_of_ciphered;
    pbCipherText=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbCipherText);
    if(NULL==pbCipherText){ret=6;goto Cleanup;}

    memcpy(pbCipherText,ciphered,size_of_ciphered);

    status=BCryptDecrypt(hKey,pbCipherText,cbCipherText,NULL,NULL,0,NULL,0,&cbPlainText,0);
    if(!NT_SUCCESS(status)){ret=6;goto Cleanup;}

    pbPlainText=(PBYTE)HeapAlloc(GetProcessHeap(),0,cbPlainText);
    if(NULL==pbPlainText){ret=8;goto Cleanup;}

    status=BCryptDecrypt(hKey,pbCipherText,cbCipherText,NULL,NULL,0,pbPlainText,cbPlainText,&cbData,0);
    if(!NT_SUCCESS(status)){ret=9;goto Cleanup;}

    memcpy(plain,pbPlainText,size_of_ciphered);

    ret=0;

Cleanup:
    if(hAesAlg){BCryptCloseAlgorithmProvider(hAesAlg,0);}
    if(hKey){BCryptDestroyKey(hKey);}
    if(pbCipherText){HeapFree(GetProcessHeap(),0,pbCipherText);}
    if(pbPlainText){HeapFree(GetProcessHeap(),0,pbPlainText);}
    if(pbKeyObject){HeapFree(GetProcessHeap(),0,pbKeyObject);}

    hAesAlg=NULL;
    hKey=NULL;
    pbCipherText=NULL;
    pbPlainText=NULL;
    pbKeyObject=NULL;

    return ret;
}

#include <stdio.h>
int main(int argc,char** argv){
    uint8_t key[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
    uint8_t data[]={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff};
    uint8_t ciphered[16];
    uint8_t invciphered[16];

    AES128Encrypt(data,key,ciphered);
    for(int i=0;i<16;i++){
        printf("%02X",ciphered[i]);
    }
    printf("\n");

    AES128Decrypt(ciphered,key,invciphered);
    for(int i=0;i<16;i++){
        printf("%02X",invciphered[i]);
    }
    printf("\n");
}

でけた(๑•̀д•́๑)

相変わらずコメントなし。コードの行数は自力で書いたのと大して変わらないかな、、、

Decryptの時に、dwFlagsをBCRYPT_BLOCK_PADDINGでなく0にしないといけなかったのがはまりどころ。オプション設定とかが色々あって面倒だけど、参考ページをじっくり読めばまぁそう難しくない。自力で実装するよりは全然ラク。

 

処理時間を計測してみる。

#include <stdio.h>
#include <time.h>
int main(int argc,char** argv){
    uint8_t key[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
    uint8_t data[]={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff};
    uint8_t ciphered[16];
    uint8_t invciphered[16];
    clock_t start_clock, end_clock;

    start_clock = clock();
    for(int i=0;i<1000000;i++){
        AES128Encrypt(data,key,ciphered);
    }
    end_clock = clock();
    printf("clock:%f\n",(double)(end_clock-start_clock)/CLOCKS_PER_SEC);
    for(int i=0;i<16;i++){
        printf("%02X",ciphered[i]);
    }
    printf("\n");

    AES128Decrypt(ciphered,key,invciphered);
    for(int i=0;i<16;i++){
        printf("%02X",invciphered[i]);
    }
    printf("\n");
}

main関数をこんな感じに変更して実行すると、

1M回の計算に1.222sかかっている。すなわち、1回あたり1.222usかかっている。

では自作した方は、、、同じように測定してみると、1回あたり8.746us!!実用上はMicrosoftに乗っかるのが正解TT