30669


コード：

[code]

    #include <stdio.h>
    #include <string.h>
    #include <sys/file.h>
    
    #define key 1234
    #define SIZE 16
    
    int main()
    {
    
        int i;
        char str[SIZE];
        fpos_t ft;
        FILE *fp;
        fp = fopen("test.dat", "rb");
        FILE *file;
        file = fopen("test2.dat","wb");
    
        int j = 0;
        while(1){
            memset(str, 0, sizeof(str));
            fseek(fp, j, SEEK_SET);
            fread(str, SIZE-1, 1, fp);
    
            for(i=0; i<strlen(str); i++){
                fprintf(file, "%c", (char)(str[i] ^ key));
                printf("%c", str[i]);
            }
            if(str[i+1]=='\0'){puts("a"); break;}
            //if(feof(fp)){printf("aaa"); break;}
            j += 15;
        }
        fclose(fp);
        fclose(file);
    
        return 0;
    }
    
[/code]

小さめのテキストファイルでしか試してませんが、テキストファイルの場合はこのコードで暗号化復号化できます。  
画像ファイルや実行ファイルで試すとうまくいきません。  
このまま1.1MBくらいの画像ファイルで動かしてみると120バイトのファイルが出力されて、コメントアウトしてるif文に切り替えると1MBのファイルが出力されました。  
どうすればいいと思いますか？


CC BY-SA 3.0
2016-11-25T10:41:17.660
30667
2016-11-25T13:06:29.807
13313
question
c
XOR暗号を使ってファイルの暗号化と復号化をしたいのですが、テキストファイル以外でうまくいきません


`strlen(str)` がバイナリの場合期待するように働きません。  
`\0`の場所に依存しないように  
`fread`の返値を保存（`fread(str, 1, SIZE, fp);`
にしてバイト数を返すようにしておきます）して、読み込んだバイト数で処理するようにします。

ちなみに`fseek`は必要無いです。


CC BY-SA 3.0
2016-11-25T11:32:34.503
30668
2016-11-25T11:40:20.147
2016-11-25T11:40:20.147
5044
5044
30667
answer


最初の回答にあるとおり、まず `strlen`
がいけません。`fread`で読み込んだ長さを変数`n`で受け取り、読み込めなかった時はループを抜けるようにします。`n`の回数だけ`XOR`演算を行います。バイナリファイルに書き込むのに`fprintf`を使うのは、致命的に深刻な問題というほどではありませんが、あまり良くないやり方です。1文字出力するための関数`putc`がありますのでこれを使います。バイナリがコンソールに表示されると画面が乱れますので、`printf`や`puts`は消します。

特殊なコンピューターでなければ、`char`は8ビットです（この件に言及し始めると、`char`が8ビットであるとは限らないというつっこみが、たくさん飛んできます）ので、一般的なWindowsかMacかLinuxであると想定し、`char`の範囲は`-128`～`+127`の範囲であるものとします。ですので、`XOR`するための`key`の値が`1234`というのは適切ではありません。もし、`char`に`1234`を格納できるだけのサイズを持ったコンピューターなら間違いではないのですが、恐らくそうではないと思うので、`key`の値は`char`の範囲に合わせて、小さめの値にしました。

`fseek`は必要ないので、変数`j`も消します。

[code]

    #define key 123
    #define SIZE 16
    
    int main()
    {
    
        int i;
        char str[SIZE];
        FILE *fp;
        fp = fopen("neko1.jpg", "rb");
        FILE *file;
        file = fopen("neko2.jpg","wb");
    
        while (1) {
            int n = fread(str, 1, SIZE, fp);
            if (n < 1) break;
            for (i = 0; i < n; i++){
                putc(str[i] ^ key, file);
            }
        }
    
        fclose(fp);
        fclose(file);
    
        return 0;
    }
    
[/code]

とりあえずこれで動きますが、16バイト単位で処理するというのは、最近のコンピューターには控えめすぎるサイズです。1024や4096や65536などが好んで使われると思います。出力する際も、1バイトずつ`putc`でやるのもケチ臭いです。C言語の学習用サンプルプログラムか何かだと思いますが、実際に使えるプログラムにする場合、バッファのサイズ分まとめて暗号化の演算を行い、`putc`の代わりに`fwrite`で複数バイトまとめて書き込む、などの工夫ができますので、今後の課題として、挑戦してみるといいでしょう。


CC BY-SA 3.0
2016-11-25T13:06:29.807
30669
2016-11-25T13:06:29.807
3337
30667
answer