Windowsで暗号プログラムを書いてみる(RSA編 その2) | reverse-eg-mal-memoのブログ

reverse-eg-mal-memoのブログ

サイバーセキュリティに関して、あれこれとメモするという、チラシの裏的存在。
medium(英語):https://sachiel-archangel.medium.com/

RSA暗号化のサンプルコードです。

エラー処理が甘め&エラーデータ等を使ったエラールートのテストまではしていないので、あくまで正常系の流れの参考程度に。

 

おまけでつけたPEMの出力は、出力したprivate keyを openssl を使って読み込めること、この関数で出力した public key と openssl rsa -RSAPublicKey_out で出力した結果が合致する程度の確認はしています。

PEMの形式は正直詳しくないので、他の形式で出したい場合はパラメータ変更等で対応してください。

 

 

CryptRSA.h

#pragma once
#include <windows.h>
#include <wincrypt.h>
#include "DataContainer.h"

#define CRYPTRSA_SUCCESS 0
#define CRYPTRSA_ER_FATAL -1
#define CRYPTRSA_ER_PARAM -2
#define CRYPTRSA_ER_ACQUIRECONTEXT -3
#define CRYPTRSA_ER_GENKEY -4
#define CRYPTRSA_ER_EXPORTKEY -5
#define CRYPTRSA_ER_IMPORTKEY -6
#define CRYPTRSA_ER_ENCRYPT -7
#define CRYPTRSA_ER_DECRYPT -8

#define CRYPTRSA_FLAG_PUBLIC 0
#define CRYPTRSA_FLAG_PRIVATE 1

#define CRYPTRSA_STR_BIGIN_PRV "-----BEGIN RSA PRIVATE KEY-----\r\n"
#define CRYPTRSA_STR_END_PRV "-----END RSA PRIVATE KEY-----\r\n"
#define CRYPTRSA_STR_BIGIN_PUB "-----BEGIN RSA PUBLIC KEY-----\r\n"
#define CRYPTRSA_STR_END_PUB "-----END RSA PUBLIC KEY-----\r\n"

class CryptRSA
{
public:
    CryptRSA();
    ~CryptRSA();

    static int CreateRSAKey(DataContainer *pobjPrivateKey, DataContainer *pobjPublicKey);
    static int ConvertBinToString(DataContainer *pobjBin, DataContainer *pobjStr, int iPrivatePublicFlag);
    static int ConvertStringToBin(DataContainer *pobjStr, DataContainer *pobjBin, int iPrivatePublicFlag);
    static int Encrypt(DataContainer *pobjPublicKey, DataContainer *pobjData);
    static int Decrypt(DataContainer *pobjPrivateKey, DataContainer *pobjData);
};

 

CryptRSA.cpp

#include "pch.h"
#include "CryptRSA.h"


CryptRSA::CryptRSA()
{
}


CryptRSA::~CryptRSA()
{
}

int CryptRSA::CreateRSAKey(DataContainer *pobjPrivateKey, DataContainer *pobjPublicKey)
{
    int iRetCode = CRYPTRSA_ER_FATAL;
    HCRYPTPROV hProv = NULL;        // コンテキストハンドル
    HCRYPTKEY hKey = NULL;            // 鍵をインポートしたときのハンドル
    DWORD dwDataSize = 0;

    try {
        // コンテキストの取得
        if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) == false)
        {
            if (GetLastError() == NTE_BAD_KEYSET)
            {
                if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET) == false)
                {
                    throw CRYPTRSA_ER_ACQUIRECONTEXT;
                }
            }
            else
            {
                throw CRYPTRSA_ER_ACQUIRECONTEXT;
            }
        }

        // Keyの作成
        if (CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey) == false)
        {
            throw CRYPTRSA_ER_GENKEY;
        }

        // 秘密鍵の受領サイズの取得
        if(CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, NULL, NULL, &dwDataSize) == false)
        {
            throw CRYPTRSA_ER_EXPORTKEY;
        }

        // 秘密鍵の受領サイズの領域確保
        if(pobjPrivateKey->CreateDataObject(dwDataSize) != DATACONT_SUCCESS)
        {
            throw CRYPTRSA_ER_FATAL;
        }

        // 秘密鍵の取得
        if (CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, NULL, (BYTE *)pobjPrivateKey->GetDataPointer(), &dwDataSize) == false)
        {
            throw CRYPTRSA_ER_EXPORTKEY;
        }
        pobjPrivateKey->SetCurrentDataSize(dwDataSize);


        // 公開鍵の受領サイズの取得
        if (CryptExportKey(hKey, NULL, PUBLICKEYBLOB, NULL, NULL, &dwDataSize) == false)
        {
            throw CRYPTRSA_ER_EXPORTKEY;
        }

        // 公開鍵の受領サイズの領域確保
        if (pobjPublicKey->CreateDataObject(dwDataSize) != DATACONT_SUCCESS)
        {
            throw CRYPTRSA_ER_FATAL;
        }

        // 公開鍵の取得
        if (CryptExportKey(hKey, NULL, PUBLICKEYBLOB, NULL, (BYTE *)pobjPublicKey->GetDataPointer(), &dwDataSize) == false)
        {
            throw CRYPTRSA_ER_EXPORTKEY;
        }
        pobjPublicKey->SetCurrentDataSize(dwDataSize);
        iRetCode = CRYPTRSA_SUCCESS;
    }

    catch (int iError)
    {
        iRetCode = iError;
    }

    // 後処理
    if (hKey)
    {
        CryptDestroyKey(hKey);
        hKey = NULL;
    }

    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
        hProv = NULL;
    }

    return iRetCode;
}


int CryptRSA::ConvertBinToString(DataContainer *pobjBin, DataContainer *pobjStr, int iPrivatePublicFlag)
{
    DWORD dwDataSize = 0;
    DWORD dwEncodeSize = 0;
    DataContainer objTmp;
    DataContainer objTmpStr;
    int iBufSize = 0;

    // 引数チェック
    if (pobjBin == NULL || pobjStr == NULL)
        return CRYPTRSA_ER_FATAL;

    if (iPrivatePublicFlag != CRYPTRSA_FLAG_PRIVATE && iPrivatePublicFlag != CRYPTRSA_FLAG_PUBLIC)
    {
        return CRYPTRSA_ER_FATAL;
    }

    if (iPrivatePublicFlag == CRYPTRSA_FLAG_PRIVATE)
    {
        // X509/PKCS#7エンコード処理
        if (CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE *)pobjBin->GetDataPointer(), NULL, NULL, NULL, &dwEncodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (objTmp.CreateDataObject(dwEncodeSize) != DATACONT_SUCCESS)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE *)pobjBin->GetDataPointer(), NULL, NULL, objTmp.GetDataPointer(), &dwEncodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        // バイナリのBASE64変換
        if (CryptBinaryToString((const BYTE *)objTmp.GetDataPointer(), objTmp.GetDataSize(), CRYPT_STRING_BASE64, NULL, &dwDataSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (objTmpStr.CreateDataObject(dwDataSize) != DATACONT_SUCCESS)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (CryptBinaryToString((const BYTE *)objTmp.GetDataPointer(), objTmp.GetDataSize(), CRYPT_STRING_BASE64, (LPSTR)objTmpStr.GetDataPointer(), &dwDataSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }
        objTmpStr.SetCurrentDataSize(dwDataSize);

        // 文字列を付与する
        // -----BEGIN RSA PRIVATE KEY-----
        // (データ)
        // -----END RSA PRIVATE KEY-----

        iBufSize = sizeof(CRYPTRSA_STR_BIGIN_PRV) + sizeof(CRYPTRSA_STR_END_PRV) + dwDataSize;
        pobjStr->CreateDataObject(iBufSize);

        pobjStr->ImportString(CRYPTRSA_STR_BIGIN_PRV, lstrlen(CRYPTRSA_STR_BIGIN_PRV));
        pobjStr->AppendString((TCHAR*)objTmpStr.GetDataPointer(), objTmpStr.GetCurrentDataSize());
        pobjStr->AppendString(CRYPTRSA_STR_END_PRV, lstrlen(CRYPTRSA_STR_END_PRV));

        pobjStr->SetCurrentDataSize(lstrlen((TCHAR *)pobjStr->GetDataPointer()));
    }
    else
    {
        // X509/PKCS#7エンコード処理
        // 出力は openssl rsa -RSAPublicKey_out と同等。

        if (CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, (const BYTE *)pobjBin->GetDataPointer(), NULL, NULL, NULL, &dwEncodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (objTmp.CreateDataObject(dwEncodeSize) != DATACONT_SUCCESS)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, (const BYTE *)pobjBin->GetDataPointer(), NULL, NULL, objTmp.GetDataPointer(), &dwEncodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        // バイナリのBASE64変換
        if (CryptBinaryToString((const BYTE *)objTmp.GetDataPointer(), objTmp.GetDataSize(), CRYPT_STRING_BASE64, NULL, &dwDataSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (objTmpStr.CreateDataObject(dwDataSize) != DATACONT_SUCCESS)
        {
            return CRYPTRSA_ER_FATAL;
        }

        if (CryptBinaryToString((const BYTE *)objTmp.GetDataPointer(), objTmp.GetDataSize(), CRYPT_STRING_BASE64, (LPSTR)objTmpStr.GetDataPointer(), &dwDataSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }
        objTmpStr.SetCurrentDataSize(dwDataSize);

        // 文字列を付与する
        // -----BEGIN RSA PUBLIC KEY-----
        // (データ)
        // -----END RSA PUBLIC KEY-----

        iBufSize = sizeof(CRYPTRSA_STR_BIGIN_PUB) + sizeof(CRYPTRSA_STR_END_PUB) + dwDataSize;
        pobjStr->CreateDataObject(iBufSize);

        pobjStr->ImportString(CRYPTRSA_STR_BIGIN_PUB, lstrlen(CRYPTRSA_STR_BIGIN_PUB));
        pobjStr->AppendString((TCHAR*)objTmpStr.GetDataPointer(), objTmpStr.GetCurrentDataSize());
        pobjStr->AppendString(CRYPTRSA_STR_END_PUB, lstrlen(CRYPTRSA_STR_END_PUB));

        pobjStr->SetCurrentDataSize(lstrlen((TCHAR *)pobjStr->GetDataPointer()));
    }

    return CRYPTRSA_SUCCESS;
}

int CryptRSA::ConvertStringToBin(DataContainer *pobjStr, DataContainer *pobjBin, int iPrivatePublicFlag)
{
    DWORD dwDataSize = 0;
    DWORD dwDecodeSize = 0;
    DataContainer objTmp;

    // 引数チェック
    if (pobjBin == NULL || pobjStr == NULL)
        return CRYPTRSA_ER_FATAL;

    if (iPrivatePublicFlag != CRYPTRSA_FLAG_PRIVATE && iPrivatePublicFlag != CRYPTRSA_FLAG_PUBLIC)
    {
        return CRYPTRSA_ER_FATAL;
    }

    // BASE64のバイナリ変換
    if (CryptStringToBinary((LPSTR)pobjStr->GetDataPointer(), pobjStr->GetCurrentDataSize(), CRYPT_STRING_BASE64HEADER, NULL, &dwDataSize, NULL, NULL) == false)
    {
        return CRYPTRSA_ER_FATAL;
    }

    if (objTmp.CreateDataObject(dwDataSize) != DATACONT_SUCCESS)
    {
        return CRYPTRSA_ER_FATAL;
    }

    if (CryptStringToBinary((LPSTR)pobjStr->GetDataPointer(), pobjStr->GetCurrentDataSize(), CRYPT_STRING_BASE64HEADER, (BYTE *)objTmp.GetDataPointer(), &dwDataSize, NULL, NULL) == false)
    {
        return CRYPTRSA_ER_FATAL;
    }
    objTmp.SetCurrentDataSize(dwDataSize);

    if (iPrivatePublicFlag == CRYPTRSA_FLAG_PRIVATE)
    {
        // X509/PKCS#7デコード処理
        if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE *)objTmp.GetDataPointer(), dwDataSize, 0, NULL, NULL, &dwDecodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        pobjBin->CreateDataObject(dwDecodeSize);

        if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE *)objTmp.GetDataPointer(), dwDataSize, 0, NULL, pobjBin->GetDataPointer(), &dwDecodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }
    }
    else
    {
        // X509/PKCS#7デコード処理
        if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, (const BYTE *)objTmp.GetDataPointer(), dwDataSize, 0, NULL, NULL, &dwDecodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }

        pobjBin->CreateDataObject(dwDecodeSize);

        if (CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, (const BYTE *)objTmp.GetDataPointer(), dwDataSize, 0, NULL, pobjBin->GetDataPointer(), &dwDecodeSize) == false)
        {
            return CRYPTRSA_ER_FATAL;
        }
    }

    pobjBin->SetCurrentDataSize(dwDecodeSize);

    return CRYPTRSA_SUCCESS;
}

int CryptRSA::Encrypt(DataContainer *pobjPublicKey, DataContainer *pobjData)
{
    int iRetCode = CRYPTRSA_ER_FATAL;
    HCRYPTPROV hProv = NULL;        // コンテキストハンドル
    HCRYPTKEY hKey = NULL;            // 鍵をインポートしたときのハンドル
    DWORD dwEncryptSize = 0;


    try {
        // コンテキストの取得
        if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) == false)
        {
            if (GetLastError() == NTE_BAD_KEYSET)
            {
                if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET) == false)
                {
                    throw CRYPTRSA_ER_ACQUIRECONTEXT;
                }
            }
            else
            {
                throw CRYPTRSA_ER_ACQUIRECONTEXT;
            }
        }

        // 鍵のインポート
        if (CryptImportKey(hProv, (BYTE *)pobjPublicKey->GetDataPointer(), pobjPublicKey->GetCurrentDataSize(), NULL, 0x0, &hKey) == false)
        {
            throw CRYPTRSA_ER_IMPORTKEY;
        }

        // 出力バッファサイズの計算
        dwEncryptSize = (DWORD)pobjData->GetCurrentDataSize();
        if (CryptEncrypt(hKey, NULL, true, 0x0, NULL, &dwEncryptSize, pobjData->GetDataSize()) == false)
        {
            throw CRYPTRSA_ER_ENCRYPT;
        }

        if ((int)dwEncryptSize > pobjData->GetCurrentDataSize())
        {
            pobjData->ReallocDataObject((int)dwEncryptSize);
        }

        // 暗号化
        dwEncryptSize = (DWORD)pobjData->GetCurrentDataSize();
        if (CryptEncrypt(hKey, NULL, true, 0x0, (BYTE *)pobjData->GetDataPointer(), &dwEncryptSize, pobjData->GetDataSize()) == false)
        {
            throw CRYPTRSA_ER_ENCRYPT;
        }
        pobjData->SetCurrentDataSize(dwEncryptSize);

        iRetCode = CRYPTRSA_SUCCESS;
    }

    catch (int iError)
    {
        iRetCode = iError;
    }

    // 後処理
    if (hKey)
    {
        CryptDestroyKey(hKey);
        hKey = NULL;
    }

    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
        hProv = NULL;
    }

    return iRetCode;
}

int CryptRSA::Decrypt(DataContainer *pobjPrivateKey, DataContainer *pobjData)
{
    int iRetCode = CRYPTRSA_ER_FATAL;
    HCRYPTPROV hProv = NULL;        // コンテキストハンドル
    HCRYPTKEY hKey = NULL;            // 鍵をインポートしたときのハンドル
    DWORD dwDecryptSize = 0;


    try {
        // コンテキストの取得
        if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) == false)
        {
            if (GetLastError() == NTE_BAD_KEYSET)
            {
                if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET) == false)
                {
                    throw CRYPTRSA_ER_ACQUIRECONTEXT;
                }
            }
            else
            {
                throw CRYPTRSA_ER_ACQUIRECONTEXT;
            }
        }

        // 鍵のインポート
        if (CryptImportKey(hProv, (BYTE *)pobjPrivateKey->GetDataPointer(), pobjPrivateKey->GetCurrentDataSize(), NULL, 0x0, &hKey) == false)
        {
            throw CRYPTRSA_ER_IMPORTKEY;
        }

        // 復号化
        dwDecryptSize = (DWORD)pobjData->GetCurrentDataSize();
        if (CryptDecrypt(hKey, NULL, true, 0x0, (BYTE *)pobjData->GetDataPointer(), &dwDecryptSize) == false)
        {
            throw CRYPTRSA_ER_ENCRYPT;
        }
        pobjData->SetCurrentDataSize(dwDecryptSize);

        iRetCode = CRYPTRSA_SUCCESS;
    }

    catch (int iError)
    {
        iRetCode = iError;
    }

    // 後処理
    if (hKey)
    {
        CryptDestroyKey(hKey);
        hKey = NULL;
    }

    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
        hProv = NULL;
    }

    return iRetCode;
}