先頭へ戻る

簡易的な暗号 解答ページ | Programming Place Plus アルゴリズムとデータ構造編【その他のアルゴリズム】 第3章

Programming Place Plus トップページアルゴリズムとデータ構造編

先頭へ戻る

問題①

問題① XOR暗号を、平文が整数でなく、文字列となるように実装してください。


1文字ずつ取り出し、1バイト単位で XOR演算を行えば、平文が文字列でも適用できます。

#include <stdio.h>
#include <string.h>

static void xor_encryption(char* str, size_t len, int key);
static void print(const char* str, size_t len);

int main(void)
{
    char data[256];
    char buf[32];
    int key;


    puts( "元の値を入力してください。" );
    fgets( data, sizeof(data), stdin );

    puts( "鍵となる値を入力してください。" );
    fgets( buf, sizeof(buf), stdin );
    sscanf( buf, "%d", &key );

    size_t len = strlen( data );

    xor_encryption( data, len, key );
    puts( "暗号結果は次のようになりました。" );
    print( data, len );

    xor_encryption( data, len, key );
    puts( "復号結果は次のようになりました。" );
    print( data, len );

    return 0;
}

/*
    XOR暗号

    引数:
        str:    暗号化(または復号化) する文字列。
        len:    引数str の文字列の長さ。
        key:    鍵。

    引数str の内容が直接変更される。
*/
void xor_encryption(char* str, size_t len, int key)
{
    for( size_t i = 0; i < len; ++i ){
        str[i] ^= key;
    }
}

/*
    文字列出力

    引数:
        str:    文字列。
        len:    引数str の文字列の長さ。
*/
void print(const char* str, size_t len)
{
    for( size_t i = 0; i < len; ++i ){
        printf( "%c", str[i] );
    }
    printf( "\n" );
}

実行結果:

元の値を入力してください。
abcde
鍵となる値を入力してください。
123
暗号結果は次のようになりました。
q
復号結果は次のようになりました。
abcde

本編でも触れたように、暗号化によって、暗号文の途中に ‘\0’ ができてしまう可能性があるので、結果を見るときにいは、print関数のように1文字ずつ出力しています。

問題②

問題② シーザ暗号の一種として ROT13 と呼ばれる暗号アルゴリズムがあります。これは、平文に含まれるアルファベットを、辞書順で 13文字ずらし、「Z (z)」を超えてしまったら「A (a)」に戻ってくるルールになっています。これを実装してください。


#include <stdio.h>
#include <string.h>

static void rot13_encryption(char* str, size_t len);
static void rot13_decryption(char* str, size_t len);

int main(void)
{
    char data[256];

    puts( "元の文字列を入力してください。" );
    fgets( data, sizeof(data), stdin );

    size_t len = strlen( data );

    rot13_encryption( data, len );
    puts( "暗号結果は次のようになりました。" );
    printf( "%s\n", data );

    rot13_decryption( data, len );
    puts( "復号結果は次のようになりました。" );
    printf( "%s\n", data );

    return 0;
}

/*
    ROT13 による暗号化

    引数:
        str:    暗号化する文字列。
        len:    引数str の文字列の長さ。

    引数str の内容が直接変更される。
*/
void rot13_encryption(char* str, size_t len)
{
    for( size_t i = 0; i < len; ++i ){
        int c = str[i];  // 0x7f でオーバーフローしないように int型変数へ代入

        // a~z, A~Z 以外は無視
        if( 'a' <= c && c <= 'z' ){
            c += 13;
            if( c > 'z' ){
                c = 'a' + (c - 'z' - 1);
            }
        }
        else if( 'A' <= c && c <= 'Z' ){
            c += 13;
            if( c > 'Z' ){
                c = 'A' + (c - 'Z' - 1);
            }
        }

        str[i] = c;
    }
}

/*
    ROT13 による復号化

    引数:
        str:    復号化する文字列。
        len:    引数str の文字列の長さ。

    引数str の内容が直接変更される。
*/
void rot13_decryption(char* str, size_t len)
{
    for( size_t i = 0; i < len; ++i ){
        int c = str[i];  // 0x7f でオーバーフローしないように int型変数へ代入

        // a~z, A~Z 以外は無視
        if( 'a' <= c && c <= 'z' ){
            c -= 13;
            if( c < 'a' ){
                c = 'z' - ('a' - c - 1);
            }
        }
        else if( 'A' <= c && c <= 'Z' ){
            c -= 13;
            if( c < 'A' ){
                c = 'Z' - ('A' - c - 1);
            }
        }

        str[i] = c;
    }
}

実行結果:

元の文字列を入力してください。
abyz012ABYZ
暗号結果は次のようになりました。
nolm012NOLM

復号結果は次のようになりました。
abyz012ABYZ

ASCIIコードを前提として、‘a’~‘z’ と ‘A’~‘Z’ だけを取り扱うように実装しています。char型のまま取り扱うと、+13 したときに上限値(127) を超えてしまうため注意が必要です。

さて、この例のように、暗号化と復号化の関数を分けて実装した人は多いと思いますが、実は暗号化の関数が、そのまま復号化にも使えます。main関数の復号化のところだけ修正します。

int main(void)
{
    char data[256];

    puts( "元の文字列を入力してください。" );
    fgets( data, sizeof(data), stdin );

    size_t len = strlen( data );

    rot13_encryption( data, len );
    puts( "暗号結果は次のようになりました。" );
    printf( "%s\n", data );

    rot13_encryption( data, len );
    puts( "復号結果は次のようになりました。" );
    printf( "%s\n", data );

    return 0;
}

今度は復号化のところでも、rot13_encryption関数を使っています。このときの実行結果は次のようになります。

実行結果:

元の文字列を入力してください。
abyz012ABYZ
暗号結果は次のようになりました。
nolm012NOLM

復号結果は次のようになりました。
abyz012ABYZ

このように、正しく復号化できました。

なぜこうなるかというと、ROT13 の以下の特性があるからです。

そして、アルファベットは全部で 26文字なのが決め手となります。13文字というのはちょうど半分なので、2回繰り返せば元の位置に戻ってくるわけです。


参考リンク



更新履歴

’2017/12/23 問題①の解答例の途中から、ページ内容が途切れて無くなっていたのを修復。

’2015/2/21 サンプルプログラムで、ファイルスコープでよい関数に static を付加。

’2012/5/25 新規作成。



第3章のメインページへ

アルゴリズムとデータ構造編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー