見出し画像

[Drogon]実験!OpenSSLでSHA-2 512bit ハッシュ値を得る

皆様こんにちはもしくはこんばんは、みじんこきなこです。

今日はまた次回の記事につなげるために、実験をしていきたいと思います。

タイトルからわかる通り、今回はOpenSSLの機能を使って、SHA-2 512bit 暗号化ハッシュを生成する実験を行います。

暗号化ハッシュというのは、あるデータに単純な計算を行い生成される、ほんの少しの差異しかないデータでも全く異なる値となる数値の羅列です。
ネットワーク越しにやり取りをする仕組みを実装する際に暗号化ハッシュは、安全性の担保のためにちょこちょこと使いたいところが出てきます。

Drogon には drogon::utils というユーティリティクラスの中に、md5という暗号化ハッシュを得るユーティリティが実装されています。
しかしこのmd5というアルゴリズムは開発から30年以上も経っており、現在のPCであれば簡単に破れてしまうものなので、せいぜい通信のチェックサム程度にしか使用できませんし、実際にそのような目的でutilsに含まれています。
それ以上に強度の高い暗号化ハッシュを利用したい場合には、独自に実装する必要があるのです。

そこで今回は、近年利用される暗号化ハッシュアルゴリズムの中でも最強の強度を誇るSHA-2 を用いた 512bit 暗号化ハッシュの取得を実験していきます。

Opensslを使用する

今回は、Drogonをインストールする過程でインストールされているということで、OpenSSL、厳密にはOpenSSLの一部として提供されるcryptoというライブラリを使用します。

OpenSSL自体はC言語で実装されていますので、C++からの利用も簡単で便利です。
ただマニュアルがねぇ……下手するとソース呼んだ方が早いくらいひたすら読みにくいんですけどね……

ソースコード

今回のソースコードはDrogonのプロジェクトなどは使用しません。
sample.ccとして実装しています。

 #include  <iostream> #include  <memory> #include  <iomanip> #include  <openssl/sha.h>


std::string createDigest(std::string data_)
{
    std::array<u_char, SHA512_DIGEST_LENGTH> hash;
    std::stringstream ss_digest;
    std::unique_ptr< SHA512_CTX > context 
        = std::make_unique<SHA512_CTX>();

    SHA512_Init(context.get());

    SHA512_Update(
        context.get(), 
        static_cast<const void *>(data_.c_str()), 
        data_.length()
        );

    SHA512_Final(hash.data(), context.get());

    ss_digest << std::setfill('0') << std::hex;
    for(auto it : hash) {
        ss_digest << std::setw(2) << static_cast<int>(it);
    }

    return ss_digest.str();
}

int main(void)
{
    std::string data1 = "test.";
    std::string data2 = "test";
    std::string data3 = "test";

    auto result1  = createDigest(data1);
    auto result2  = createDigest(data2);
    auto result3  = createDigest(data3);

    std::cout << result1 << std::endl;
    std::cout << result2 << std::endl;
    std::cout << (result1 == result2) << std::endl;

    std::cout << result1 << std::endl;
    std::cout << result3 << std::endl;
    std::cout << (result1 == result3) << std::endl;

    std::cout << result2 << std::endl;
    std::cout << result3 << std::endl;
    std::cout << (result2 == result3) << std::endl;
    
    return 0;

}

ポイント

使用するヘッダファイル

OpenSSLに含まれるSHA系のアルゴリズムを利用するには、openssl/sha.h というヘッダフェイルをインクルードします。

インターフェース

SHA系のアルゴリズムのインターフェースはどのハッシュ長でも4種類あります。

  • アルゴリズムそのものずばりの名前のもの

 unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md);

呼び出すときには、

  1. 第一引数にデータ

  2. 第二引数にデータ長

  3. 第三引数に出来上がったハッシュを受ける配列

を、それぞれを設定します。
第三引数がNullだとスレッドセーフでなくなるそうなので、頻繁にスレッドを生み殺しするDrogonで使用するには基本的には与えるのが無難です。
一発で全データをハッシュ化したいときに使用するインターフェースです。

  • アルゴリズム名 + init

int SHA512_Init(SHA512_CTX *c);

コンテキスト(アルゴリズムが使う定数とか途中の値を保持する構造体)の初期化をするインターフェースです。

  • アルゴリズム名 + update

int SHA512_Update(SHA512_CTX *c, const void *data, size_t len);

initしたコンテキストを利用して、複数のデータを与えたいときはupdateを複数回呼びます。
そのためのインターフェースです。

  • アルゴリズム名 + final

int SHA512_Final(unsigned char *md, SHA512_CTX *c);

updateで与えられたデータ全てを基にハッシュを作成するインターフェースです。

コンパイルしてみる

単一のサンプルコードですが、コンパイルするときにはただソースコードを与えてやればいいわけではなく、-lcryptoオプションでOpenSSLのCryptoライブラリをリンクしてやる必要があります。

つまり以下のような感じ

$ g++ -o sample sample.cc -lcrypto

実行してみる

実行結果はこんな感じです。

bbc1df86d1994a5c762fab9e815bbe28c98789b8320348c8e4a8ac0f270b2260edd72712cc4fb75231ae8d0257900a86cc71541b13c2364e18639016fad126c6
ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff
0
bbc1df86d1994a5c762fab9e815bbe28c98789b8320348c8e4a8ac0f270b2260edd72712cc4fb75231ae8d0257900a86cc71541b13c2364e18639016fad126c6
ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff
0
ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff
ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff
1

横に長い文字列が、「test」という文字列、または「test.」という文字列それぞれに対して生成されたハッシュ値です。
もはやこの文字列を見ても全く元の文字列が判らないめちゃくちゃな文字列ですね。
SHA2-512は結果の長さが512bit = 64Byte ですので、16進数128桁ほどになります。

ここで注目すべきは1行目と2行目、元のデータがピリオド一文字しか違わないのに、全く異なるハッシュが生成されているのがわかると思います。

そして7行目と8行目は共に異なる変数で「test」という文字列を与えましたが、これだけめちゃくちゃな数字なのにぴたりと一致しています。

参考

今回のサンプルでは、解りやすさのために文字列に変えたりstd::stringの等価演算子で比較していましたが、実用する上ではいちいち string 型にして目視する必要はありません。

また、ハッシュを得る対象が文字列である必要も特にないので、こんな感じになるでしょう。

 #include  <iostream> #include  <memory> #include  <iomanip> #include  <openssl/sha.h>

template < class T >
void createDigestArray(
    const T data_, 
    const size_t data_size_,
    std::array<u_char, SHA512_DIGEST_LENGTH>& hash_
)
{
    std::unique_ptr< SHA512_CTX > context 
        = std::make_unique<SHA512_CTX>();

    SHA512_Init(context.get());

    SHA512_Update(
        context.get(), 
        static_cast<const void *>(&data_), 
        data_size_
        );

    SHA512_Final(hash_.data(), context.get());
    return;
}

int main(void)
{
    int int_data1 = 1;
    int int_data2 = 1;
    createDigestArray<int>(int_data1, static_cast<size_t>(sizeof(int_data1)), hash1);
    createDigestArray<int>(int_data2, static_cast<size_t>(sizeof(int_data2)), hash2);

    std::cout << std::equal(hash1.begin(), hash1.end(),hash2.begin()) << std::endl; 

    return 0;
}

さいごに

今回は次回の記事に向けて、OpenSSLを利用してデータから暗号化ハッシュを作る方法の実験をしました。

次回のよちよち★Drogonフレームワーク、略してよちどらは、ユーザーからIDとパスワードを受け付けてログインするサンプルを作っていきます。

それでは皆様、次回もご購読よろしくお願いいたします。

この記事が気に入ったらサポートをしてみませんか?