注意:Advapi32のCryptAPIは非推奨になり、次世代暗号化API(CNG)が推奨されているようです。
今回はCryptAPIで作ってしまったので、そのまま掲載します。(そのうち直すかもしれません。)
RSA暗号化・復号化のCryptAPIのざっくりな流れ
大雑把なフローは以下の通り。
前提:
- "WinCrypt.h"をincludeすること。
- "Crypt32.lib"をリンクできるように設定すること。
概略フロー:
秘密鍵/公開鍵ペアの作成:
- Cryptコンテキストの取得(CryptAcquireContext)
- 鍵の作成(CryptGenKey)
- 秘密鍵/公開鍵の取得(CryptExportKey)
- キーオブジェクトの開放(CryptDestroyKey)
- Cryptコンテキストの開放(CryptReleaseContext)
暗号化・復号化:
- Cryptコンテキストの取得(CryptAcquireContext)
- 鍵のインポート(CryptImportKey)
- 暗号化・復号化の実行(CryptEncrypt、CryptDecrypt)
- キーオブジェクトの開放(CryptDestroyKey)
- Cryptコンテキストの開放(CryptReleaseContext)
(おまけ)PEM出力:
- X509/PKCS#7エンコード処理(CryptEncodeObjectEx)
- バイナリのBASE64変換(CryptBinaryToString)
- ヘッダー、フッターの付与
(おまけ)PEM入力:
- BASE64のバイナリ変換(CryptStringToBinary)
- X509/PKCS#7デコード処理(CryptDecodeObjectEx)
ポイント:
- 利用するCryptAPIの種類と順番、概ねのパラメータ。
- 鍵情報は秘密鍵・公開鍵のペアと公開鍵。
- 暗号化は公開鍵で行い、復号は秘密鍵で行っている。
サンプルソースのコード実装上の制限:
- TCHARを8バイトのASCII文字として取り扱うため、文字コードをマルチバイト文字セットを使用している。
CryptAPIを使ったRSA暗号化・復号化サンプルソース
これも、VisualStudio2017で動作することが確認されたソースです。
もし流用するのであれば、パラメータ等については、用途に合わせて変えてください。
MicrosoftのMSDNを参考にするとよいでしょう。
ソースコードは次の記事に記載しています。また、このソースコードを用いたサンプルをおまけ記事2に掲載します。
必要な領域を動的に確保するため、データの格納はバッファのポインタではなく、データを格納するオリジナルのデータオブジェクトを用いています。
データオブジェクトのソースはおまけ記事に掲載します。
データクラスで文字列操作をTCHARで実装してありますが、文字列をASCIIで使いたいため、マルチバイト文字セットでビルドする必要があります。
設定は右側のソリューションエクスプローラーでプロジェクトを選択したうえで、メニューの「プロジェクト」→「プロパティ」を選択し、プロパティページを表示します。左側ペインの「構成プロパティ」→「全般」を選び、右側ペインの「プロジェクトの規定値」内の文字セットを「マルチバイト文字セットを使用する」を選択します(VisualStudio 2017の場合)。
また、秘密鍵・公開鍵の生成や暗号化・復号化とは直接関係はないですが、PEMでのデータ出力のためのエンコード/BASE64変換およびPEMデータ入力のためのバイナリ変換/デコードの関数も入れています。
こちらを使う場合、「Crypt32.lib」をライブラリとして参照しています。
使用したVisualStudio 2017のデフォルト設定ではこのライブラリが含まれていないため、リンカの設定に追加する必要があります。新しいバージョンなら、デフォルトで参照するようになっているかもしれません。
(ライブラリを追加しないと、名前解決でコケます。)
設定は同様に開いたプロパティページの左側ペインの「構成プロパティ」→「全般」を選び、右側ペインの「構成プロパティ」→「リンカ」→「入力」を選び、右側ペインの「追加の依存ファイル」に「Crypt32.lib」を追加します。
関数の仕様:
static int CreateRSAKey(DataContainer *pobjPrivateKey, DataContainer *pobjPublicKey);
RSA鍵を生成し、第1引数に秘密鍵、第2引数に公開鍵を格納して返します。
引数:
pobjPrivateKey:秘密鍵Blob受け取り用データオブジェクトのポインタ
pobjPublicKey:公開鍵Blob受け取り用データオブジェクトのポインタ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
領域のサイズを計算して確保する必要があり、それも含めてラッピングしたため、データクラスを使う必要がある。
static int Encrypt(DataContainer *pobjPublicKey, DataContainer *pobjData);
第1引数で渡した鍵で第2引数のデータを暗号化する。
暗号化結果は第2引数のオブジェクトに格納される。
引数:
pobjPublicKey:公開鍵Blobデータオブジェクトのポインタ
pobjData:暗号化するデータが格納されたデータオブジェクトのポインタ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
大きなサイズのデータには未対応。
領域のサイズを計算して確保する必要があり、それも含めてラッピングしたため、データクラスを使う必要がある。
static int Decrypt(DataContainer *pobjPrivateKey, DataContainer *pobjData);
第1引数で渡した鍵で第2引数のデータを復号化する。
復号化結果は第2引数のオブジェクトに格納される。
引数:
pobjPrivateKey:秘密鍵Blobデータオブジェクトのポインタ
pobjData:復号化するデータが格納されたデータオブジェクトのポインタ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
大きなサイズのデータには未対応。
領域のサイズを計算して確保する必要があり、それも含めてラッピングしたため、データクラスを使う必要がある。
static int ConvertBinToString(DataContainer *pobjBin, DataContainer *pobjStr, int iPrivatePublicFlag);
第1引数で渡したBlobの鍵をPEMに変換し、第2引数のデータデータオブジェクトに格納する。
第3引数にBlobの鍵が秘密鍵か公開鍵かを識別するフラグ(CRYPTRSA_FLAG_PRIVATEまたはCRYPTRSA_FLAG_PUBLIC)を指定する。
引数:
pobjBin:エンコード/BASE64変換するBlobの鍵を格納するデータオブジェクトのポインタ
pobjStr:エンコード/BASE64変換結果を格納するデータオブジェクトのポインタ
iPrivatePublicFlag:pobjBinが秘密鍵か公開鍵かを示すフラグ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
エンコードは「X509_ASN_ENCODING | PKCS_7_ASN_ENCODING」で固定。
公開鍵のPEMはopensslのコマンド 「openssl rsa -RSAPublicKey_out」で出力した結果と同等となる。
static int ConvertStringToBin(DataContainer *pobjStr, DataContainer *pobjBin, int iPrivatePublicFlag);
第1引数で渡したPEMの鍵をBlobに変換し、第2引数のデータデータオブジェクトに格納する。
第3引数にPEMの鍵が秘密鍵か公開鍵かを識別するフラグ(CRYPTRSA_FLAG_PRIVATEまたはCRYPTRSA_FLAG_PUBLIC)を指定する。
引数:
pobjStr:Blob変換/デコードするPEMの鍵を格納するデータオブジェクトのポインタ
pobjBin:Blob変換/デコード結果を格納するデータオブジェクトのポインタ
iPrivatePublicFlag:pobjBinが秘密鍵か公開鍵かを示すフラグ
返り値:
0:正常終了
負値:異常終了(詳細はヘッダのdefine参照)
制限:
デコードは「X509_ASN_ENCODING | PKCS_7_ASN_ENCODING」で固定。
ソースコードは、ブログの1記事の字数制限を超えてしまったので、次の記事に掲載します。