シーザー暗号ってなに?
C言語で実装したシーザー暗号プログラム
Rubyで実装したシーザー暗号プログラム
Windowsを対象としてますが、MacやLinuxでも問題なく読めます
こんにちは。ナガオカ(@boot_kt)です。
最近、暗号化の本を読んでいます
僕が読んでるのはコレ↓
僕が読んでる版は一つ古くて、現在では最新版が出ています
この本めちゃくちゃ面白いです!!
いや、この本だけじゃない、結城 浩さんの本は全部面白い!!
結城さんの本は分かりやすいのは当たり前すぎて、「分かりやすい」なんて表現するのは恥ずかしいぐらい(笑)
結城さんの本は読んでて楽しいワクワクする!
興味ない分野でも興味出て来る
文章も巧い!!!
・・・とまぁ、それは置いといて
この記事は、シーザー暗号を自分でプログラミングしてみませんか?という内容です
Contents
シーザー暗号を自分でプログラミングしてみよう!
この本の最初の方にシーザー暗号が出て来ます
聞いたことあるような無いような・・・?
いや、無いでしょう(笑)
で、このシーザー暗号、簡単そうなので実装してみました
しかも、C言語とRubyの2本立て!!!
興味ある方はご覧ください
シーザー暗号ってなんじゃらほい?
単一換字式暗号の一種らしいですけど、そんな難しい言葉は置いといて・・・
超簡単に言いますと、文字をズラして違う文字にするだけです
以下の図は3文字ズラした場合の図です
↓こんな感じになります
「A」の文字 → 「D」に置き換える
「B」の文字 → 「E」に置き換える
「C」の文字 → 「F」に置き換える
・
・
・
・
・
「X」の文字 → 「A」に置き換える
「Y」の文字 → 「B」に置き換える
「Z」の文字 → 「C」に置き換える
一例を出せば、
This is a Pen.
を3文字ズラして、
Wklv lv d Shq.
となります
超かんたんな暗号化アルゴリズムですな
シーザー暗号のアルゴリズムをプログラミングしてみよう
で、これをプログラミングしてみましょう
プログラミング初心者の方でもできると思います
※但し、16進数とか文字コードの知識は必要です
難易度は高く無いです
以下のポイントを意識してプログラミングやってみてください
- まずは絵を書きましょう
設計と言うほどじゃないですけど、どうしたいかを絵にしてみましょう
絵にできないということは、何を作ればいいか分かっていないということ
何を作ればいいか分からないということは、どう作れば分からないということ - アスキーコード表(キャラクターコード表)を見ましょう
「アスキーコード表」とか「キャラクターコード表」とかでググってみてください - ズラすべき文字はどれ?
- ズラす必要のの無い文字はどれ?
- 文字をズラすのはいいが、ズラした結果の文字が
A
~Z
(a
~z
)を超える場合はどうするのか? - 文字をズラすってどういうこと?
この記事では、僕が書いたソースコードを置いてます
興味ある方はご覧ください
(もうちょっと下に読み進めばあります)
ソースコードの解説
コメント書いてますし、処理は複雑ではないので読めば分かると思います
ですので解説は省略しています
概要だけ書いておきますと、こんな感じです
※C言語版もRuby版も同じ
-k
オプションで与えられた数字の分だけ英数字(小文字・大文字・数字)をズラしています
-k 3
の場合:「a」は3文字ズラして「d」に、「y」は3文字ズラして「b」になる感じです- ズラすだけじゃ面白くないので、小文字の場合は大文字に、大文字の場合は小文字に変換しています
- 暗号化も復号化もコマンドは同じですが、
-k
オプションに与える数字の符号を変えます
-k 3
で暗号化した場合 →-k -3
で復号化
-k -4
で暗号化した場合 →-k 4
で復号化 - キー(
-k
オプションで与えた数字)が解らないと復号化できません
今回実装したものはネットや本で見た物ではなく自力で考えて書いたものになります
本当はもっとエレガント & シンプル & 簡潔 & 処理の速いコードになるのかも知れません
いや、なるでしょう
テストもそんなにやってませんので、バグは残っていると思います
悪い点や間違っている点、改善ポイントとかあればぜひ教えてください
あと、細かい入力チェックとかはやってません
メンドくさかったので・・・
プログラミングのレッスンサービスをご提供しています。
お試し用として、無料体験レッスンがあります。
今回のプログラムをどうしても解説して欲しいとおっしゃる方は無料体験レッスンを利用してください。
C言語版とRuby版の違い
処理そのものは全く同じです
が、C言語の方がスッキリして分かりやすいですね
Rubyは文字と数値の扱いが全く別物になりますので、.ord
や.chr
を使ってイチイチ変換するのが面倒です
あと、参照渡しだか、参照の値渡しだか、共有渡しだか分かりませんけど、OUTの引数の作り方も面倒です
まぁ、そういう書き方はRubyっぽく無いんでしょうけど
この辺のことも聞いてみたい方は無料体験レッスンを受講頂ければお話しします
情報を出し惜しみして無料体験レッスンに誘導したいのではなく、単に書くのが面倒なだけですwww
どうせなら、Skypeで会話した方が早いし楽なんです
それにウチのプログラミングスクールの宣伝にもなるかなと(笑)
広告
C言語でシーザー暗号
じゃ、ソースコードを見てみましょう
コメント書いてあるので、何やってるか分かると思います
プログラムが長いと思うかも知れませんが、コメントを頼りに一行一行読んでみてください
別に難しい内容じゃないので、(その気があればですが)絶対に理解できます!!!
「C言語なんて知らないし・・・」なんて言い訳してないで、読んでみてください
プログラム言語なんてどれもほぼ同じようなもんです
特にC言語はシンプル中のシンプルなので、C言語知らなくても他のプログラム言語を知っているなら読めます
どうしても分からない部分があれば、ググりまくりましょう
僕も分からないことがあればググりまくってます
理解できるまでググりまくったり、本屋さんで立ち読みしたり、本買ったりしてますよ
ソースコード
#include#include #include /* usage :caci -f filename [-k keynumber] */ /******************************************************************* for DEBUG *******************************************************************/ #define _SWITCH_DEBUG_ #ifdef _SWITCH_DEBUG_ #define DEBUG_PRINT(...) printf(__VA_ARGS__) #define DEBUG_RETURN return #else #define DEBUG_PRINT(...) #define DEBUG_RETURN #endif /******************************************************************* 型定義 *******************************************************************/ typedef unsigned char uchar; typedef signed char schar; typedef unsigned short int ushort; typedef unsigned long ulong; /******************************************************************* 定数 *******************************************************************/ #define MEMCMP_MATCH (0) #define KEYNUMBER_DEFAULT (3) /* -K オプションを入れていない場合にこの値を使う */ #define KEYNUMBER_SIZE (2) /* 2桁、0~99 */ #define FILENAME_SIZE (256) #define OUTPUTFILE_EXT ".cc" /* 出力ファイルに付与する拡張子 */ /******************************************************************* プロトタイプ *******************************************************************/ uchar getCaesarCipher(uchar src, schar key); uchar chgCase(uchar src); /******************************************************************* main *******************************************************************/ int main(int argc, char *argv[]) { /* 暗号化の際のキー */ ushort keynumber = KEYNUMBER_DEFAULT; uchar szKeyNumber[KEYNUMBER_SIZE + 1] = { 0 }; /* ファイル関係 */ FILE *fpIn = NULL; uchar szInFileName[FILENAME_SIZE + 1] = { 0 }; FILE *fpOut = NULL; uchar szOutFileName[FILENAME_SIZE + 1] = { 0 }; ushort intOutFileNameLen = 0; ushort intOutFileExtLen = 0; /* READした文字 */ int intC = '\0'; uchar ucC = '\0'; /* その他 */ int i; /*==============================================================*/ /* コマンドライン解析 */ /*==============================================================*/ /* 0番目は実行ファイル名なので飛ばす */ DEBUG_PRINT("argc:%d \n", argc); DEBUG_PRINT("argv[0]:%s \n", argv[0]); for(i=1; i ucLastC) { ucC -= (ucLastC - ucFirstC + 1); } else if(ucC < ucFirstC) { ucC += (ucLastC - ucFirstC + 1); } else { /* nop */ } } DEBUG_PRINT("ucC:%c \n", ucC); /*--------------------------------------------------------------*/ /* 大文字小文字変換 */ /*--------------------------------------------------------------*/ ucC = chgCase(ucC); /*--------------------------------------------------------------*/ /* ファイル出力 */ /*--------------------------------------------------------------*/ DEBUG_PRINT("output:%c \n", ucC); fputc( ucC, fpOut ); } /*--------------------------------------------------------------*/ /* CLOSE */ /*--------------------------------------------------------------*/ fclose(fpIn); /*--------------------------------------------------------------*/ /* CLOSE */ /*--------------------------------------------------------------*/ fclose(fpOut); return 0; } /********************************************************************/ /* name : 文字ずらし処理 */ /* content : 対象文字にキーを足したものを返す */ /* parameter : (I) uchar 対象文字 */ /* (I) schar キー */ /* return : 対象文字にキーを足したもの */ /* remarks : */ /********************************************************************/ uchar getCaesarCipher(uchar src, schar key) { DEBUG_PRINT("src:%d(%Xh) \n", src, src); DEBUG_PRINT("key:%d(%Xh) \n", key, key); DEBUG_PRINT("src + key:%d(%Xh) \n", src + key, src + key); return src + key; } /********************************************************************/ /* name : 大文字小文字処理 */ /* content : 大文字と小文字を変換する、英字以外はそのまま返す */ /* parameter : (I) uchar 対象文字 */ /* return : 対象文字を大文字or小文字に変換したもの */ /* remarks : */ /********************************************************************/ uchar chgCase(uchar src) { uchar diff = 'a' - 'A'; /* 小文字の場合 */ if( ('a' <= src) && (src <= 'z') ) { return src - diff; } /* 大文字の場合 */ else if( ('A' <= src) && (src <= 'Z') ) { return src + diff; } /* その他 */ else { return src; } /* ここに到達する事は無い */ return src; }
ビルドコマンド
gccを使っているなら、WindowsでもMacでもLinuxでも同じです
※コマンドプロンプト or ターミナルウィンドウでコマンド打ちます
> gcc -o caci CaesarCipher.c
実行方法(暗号化)
WindowsでもMacでもLinuxでも同じです
※コマンドプロンプト or ターミナルウィンドウでコマンド打ちます
-k
の後の数字は何文字ズラすかを示しています
-25
~ 25
を入力してください
※ソースコードでは入力チェックしてないので、変な数字とか小数点とか文字とか、やたら大きな数字とかは入れないでね
何文字ズラしても暗号の強度は変わらないですよ
対象ファイル(以下のコマンドではtarget.txt
)と同じ場所に同じファイル名で、拡張子.cc
を付与した暗号化ファイルを出力します
> caci -f target.txt -k 3
実行方法(復号化)
> caci -f target.txt.cc -k -3
※もし、暗号化時に-k 5
としていたのであれば、復号化時には符号(+/-)-k -5
としてください
広告
Rubyでシーザー暗号
C言語版と流れは同じです
考え方も同じです
ただ、Ruby版の方がクセがあると思います
binding
、.ord
、.chr
、local_variable_set
が見慣れないだろうと思います
初心者向けのRubyの本には載っていないかも知れません
ググってみてください
A
~Z
の範囲チェックは.include?
を使えるのでC言語版よりも読みやすいかな
ソースコード
# # usage : ruby caci.rb -f filename [-k keynumber] # # ******************************************************************** # 定数 # ******************************************************************** OUTPUTFILE_EXT = ".cc" # 出力ファイルに付与する拡張子 # ******************************************************************** # * name : キー&ファイル名取得処理 * # * content : コマンドライン配列からキーとファイル名を取得 * # * parameter : (I) arrayCommand コマンドライン配列 * # * (I) bind out用引数を使うためのbinding * # * (O) outIntKeyNumber キーを入れる変数 * # * (O) outStrFileName ファイル名を入れる変数 * # * return : 無し * # * remarks : 処理結果を返すべきだが、面倒なので省略 * # ******************************************************************** def getKeyAndFilename(arrayCommand, bind, outIntKeyNumber, outStrFileName) next_cancel = false #-------------------------------------------------------------- # 個数分回す #-------------------------------------------------------------- arrayCommand.size.times { |i| if next_cancel == true next_cancel = false next end #-------------------------------------------------------------- # 解析 #-------------------------------------------------------------- case arrayCommand[i].upcase # ファイル名 when "-F" next_cancel = true puts "#{arrayCommand[i]} #{arrayCommand[i+1]}" bind.local_variable_set(outStrFileName, arrayCommand[i+1]) # キー when "-K" next_cancel = true puts "#{arrayCommand[i]} #{arrayCommand[i+1]}" bind.local_variable_set(outIntKeyNumber, arrayCommand[i+1].to_i) # その他 else next_cancel = false puts arrayCommand[i] end } end # ******************************************************************** # * name : 文字ずらし処理 * # * content : 対象文字にキーを足したものを返す * # * parameter : (I) uchar 対象文字 * # * (I) schar キー * # * return : 対象文字にキーを足したもの * # * remarks : * # ******************************************************************** def getCaesarCipher(src, key) return src + key end # ******************************************************************** # * name : 大文字小文字処理 * # * content : 大文字と小文字を変換する、英字以外はそのまま返す * # * parameter : (I) uchar 対象文字 * # * return : 対象文字を大文字or小文字に変換したもの * # * remarks : * # ******************************************************************** def chgCase(src) diff = 'a'.ord - 'A'.ord # 小文字の場合 if ("a".."z").include?(src.chr) return src - diff # 大文字の場合 elsif ("A".."Z").include?(src.chr) return src + diff # その他 else return src end end # ******************************************************************** # * name : 処理メイン * # ******************************************************************** keynumber = 0 filename = "" getKeyAndFilename(ARGV, binding, :keynumber, :filename) puts "--------" puts keynumber puts filename # ファイルの存在確認 if File.exist?(filename) == false puts("error : input file open error.(#{filename})"); exit end #============================================================== # 1文字ずつ読み込んでファイル出力 #============================================================== #-------------------------------------------------------------- # OPEN(入力ファイル) #-------------------------------------------------------------- inFile = File.open(filename, "rb") inFile.binmode #-------------------------------------------------------------- # OPEN(出力ファイル) #-------------------------------------------------------------- outFile = File.open(filename + OUTPUTFILE_EXT, "wb") outFile.binmode #-------------------------------------------------------------- # READ & 暗号化 #-------------------------------------------------------------- # キーナンバーから暗号用に使う値を算出する while( binC = inFile.getc ) puts("0x{binC.ord.to_s(16)} (#{binC})") ordBinC = binC.ord ordFirstC = "\0" ordLastC = "\0" #-------------------------------------------------------------- # 文字の種類の判定 #-------------------------------------------------------------- if ("a".."z").include?(binC) puts "小文字" ordFirstC = 'a'.ord ordLastC = 'z'.ord elsif ("A".."Z").include?(binC) puts "大文字" ordFirstC = 'A'.ord ordLastC = 'Z'.ord elsif ("0".."9").include?(binC) puts "数字" ordFirstC = '0'.ord ordLastC = '9'.ord else puts "default" end #-------------------------------------------------------------- # (必要があれば)変換範囲内の文字に収める #-------------------------------------------------------------- if ordFirstC != "\0" #-------------------------------------------------------------- # 暗号化実施 #-------------------------------------------------------------- ordBinC = getCaesarCipher(ordBinC, keynumber) if(ordBinC > ordLastC) ordBinC -= (ordLastC - ordFirstC + 1) elsif(ordBinC < ordFirstC) ordBinC += (ordLastC - ordFirstC + 1) else # nop end end #-------------------------------------------------------------- # 大文字小文字変換 #-------------------------------------------------------------- ordBinC = chgCase(ordBinC) #-------------------------------------------------------------- # ファイル出力 #-------------------------------------------------------------- outFile.putc(ordBinC.chr) end #-------------------------------------------------------------- # CLOSE #-------------------------------------------------------------- inFile.close #-------------------------------------------------------------- # CLOSE #-------------------------------------------------------------- outFile.close
ビルドコマンド
無し
実行方法(暗号化)
WindowsでもMacでもLinuxでも同じです
※コマンドプロンプト or ターミナルウィンドウでコマンド打ちます
-k
の後の数字は何文字ズラすかを示しています
-25
~ 25
を入力してください
※ソースコードでは入力チェックしてないので、変な数字とか小数点とか文字とか、やたら大きな数字とかは入れないでね
何文字ズラしても暗号の強度は変わらないですよ
対象ファイル(以下のコマンドではtarget.txt
)と同じ場所に同じファイル名で、拡張子.cc
を付与した暗号化ファイルを出力します
> caci -f target.txt -k 3
実行方法(復号化)
> caci -f target.txt.cc -k -3
※もし、暗号化時に-k 5
としていたのであれば、復号化時には符号(+/-)-k -5
としてください
シーザー暗号って、暗号の強度としては安全???
んなワケ無いでしょ!!
ブルートフォースアタック(考えられる全てのパターンを力技で試してみること)したらすぐに解読されます
未来少年じゃない方の名探偵さんならブルートフォースアタックするまでもなく一瞬で解読するんじゃね?(笑)
小中学生が授業中にコソッと回す手紙に使う程度なら安全じゃないですかね?
先生に見つかっても中身を読み取られる可能性は低いかも知れません
あ、でも、数学の先生ならシーザー暗号とバレるかも知れませんので気を付けてね
広告
さいごに、
参考
調べてみたら、暗号化の本はもちろんですけどエニグマについての本も沢山でています
DVDもBlu-rayもありました
僕は歴史的経緯とか背景とかを知ると一気に興味が湧き上がってもっと知りたいと思うタイプなんですよ(笑)
でもなかなか高価なんだよなぁ・・・
早速見ましたけど、面白かった!!!
とってもオススメです
ただ、この映画は解読作業にはさほどスポットを与えていません
僕的には解読作業やその苦労をもっともっと見たかった
でもすんげー面白かったですよ!!!
あと、エニグマの解読はアラン・チューリングさんだけの功績ではなく、アラン・チューリングさんが解読するに至るまで多くの人の功績があります
そういう面も興味が湧きっぱなしです
↓僕と同じように、あなたもこんな風になるかも知れませんよ(笑)
- エニグマや暗号技術をちょっと知る
- エニグマすげーーー!ってなる
- この映画見る
- エニグマを解読したヤツすげーーー!!ってなる
- エニグマや暗号技術勉強したくなる
- エニグマ開発したヤツ頭おかしいーーー!!!ってなる
- またこの映画見る
- エニグマ解読したヤツ頭おかしいーーー!!!ってなる
- エニグマや暗号技術をもっと勉強したくなる
- またこの映画見る
- 数学者・暗号技術者カッケー!!ってなる
暗号は面白い!! 楽しい!
かなり以前に購入していた『新版 暗号技術入門 秘密の国のアリス』をやっと読み始める事ができました
暗号って難しそうなイメージがありますが、かなり大昔から使われている技術です
ということは、「情報」が人間世界では如何に重要かという事を示しています
この双方が今現在はもちろん大昔も存在していたという事は、技術は進歩して変わったけど人間自体は全く変わっていないっていうことなのかなと思います
暗号技術は人類の歴史とともに発達してきました
その背景や歴史的な経緯、人間の行動を含めながら暗号技術を勉強するととてつもなく面白い!!
暗号おもしれーーーー!!!
エニグマ(Enigma)、ヤバいっ!
この本にはエニグマ(Enigma)の事も少しですが書いてあります
エニグマは名前だけは知っていたけど、
暗号方法を知ると、、、
これ考えた人、頭おかしくね????
で・・・・
そのエニグマを解読した人はもっと頭おかしくね??????
エニグマそのものにはもちろん、開発した人、解読した人、運用、なぜ破られたのか?等々を考えると、ワクワクとドキドキが止められない!
(もっと勉強してエニグマについての記事を書いてみたいし、エニグマを模したプログラミングもやってみたい)
ってことで、この本は超絶にマジでオススメです!!!