はじめに
こんにちは、SANACHANです。
インターネットを利用するようになった昨今、ログインに使用するメールアドレス、
配送先を指定するための氏名、住所、カード番号まで、
今やありとあらゆる個人情報が多くのWebサイト上に保存されています。
一方で、某諸国によるハッキング事案、フィッシング詐欺など、
連日のように「個人情報流出」がニュースになっていますね。
個人情報を登録して便利になる反面、セキュリティ上のリスクは常に意識しておく必要があります。
今回は、セキュリティを高めるための技術「暗号化」に関して、XOR暗号方式についてご紹介します。
こんな方におすすめ
- 暗号技術についての基本を知りたい
- 暗号を使うプログラミングをしてみたいので、簡単な暗号技術を知りたい
語句説明
暗号とは
Wikipediaによると、以下のように記載されています。
暗号(あんごう)とは、セキュア通信の手法の種類で、第三者が通信文を見ても特別な知識なしでは読めないように変換する、というような手法をおおまかには指す。いわゆる「通信」(telecommunications)に限らず、記録媒体への保存などにも適用できる。
XOR(排他的論理和)とは
Wikipediaには、以下のように記載されています。
排他的論理和(はいたてきろんりわ、英: exclusive or / exclusive disjunction)とは、ブール論理や古典論理、ビット演算などにおいて、2つの入力のどちらか片方が真でもう片方が偽の時には結果が真となり、両方とも真あるいは両方とも偽の時は偽となる演算(論理演算)である。
XOR(排他的論理和)の特徴
排他的論理和と呼ばれる論理演算は、2つの特徴があります。
特徴①:2つの入力の真偽が異なる時は真、両方とも同じ時は偽となる
Wikipedia の記載内容にもありましたが、以下のような演算結果になります。
排他的論理和の演算結果
真 XOR 真 = 偽
真 XOR 偽 = 真
偽 XOR 真 = 真
偽 XOR 偽 = 偽
通常、プログラムで「真」は「1」、「偽」は「0」を使います。
プログラミングで a と b の排他的論理和を計算する時は、以下のように記述したりします。
プログラムでの記述
ans = a ^ b
ans = a xor b
スポンサーリンク
特徴②:同じ値でXORを2回演算すると元に戻る
実際に、2回XORを計算する場合を考えてみましょう。
以下のような式が成り立ちます。
同値に戻る
真 XOR 真 XOR 真 = 真
偽 XOR 真 XOR 真 = 偽
真 XOR 偽 XOR 偽 = 真
偽 XOR 偽 XOR 偽 = 偽
例えば一つ目の式では、最初に「真 XOR 真」を計算し、「偽」になります。
次に「偽」と「真」の XOR を計算すると「真」に戻りますね。
大切なポイントなので、もう一度いいます。
ポイント
同じ値でXORを2回演算すると元に戻る。
XORを利用した暗号の原理
先ほどのXORの特徴②を利用した暗号方式を「XOR暗号」とよびます。
ポイント
暗号技術の世界では、元となる値を「平文(PlainText)」、暗号化するための値を「鍵(Key)」、
暗号化された値を「暗号文(Cipher Text)」と呼びます。
先ほどの特徴②の式を暗号技術の用語を使って書くと、以下のようになります。
XOR暗号
平文 XOR 鍵 = 暗号文
暗号文 XOR 鍵 = 平文
現存する暗号化アルゴリズムは、ほぼほぼこのXOR暗号の原理をベースに考えられています。
このXOR暗号の原理において、鍵の長さ(ビット数)、鍵の生成・交換方法、演算する単位などを工夫して様々なアルゴリズムが存在しているのです。
XOR暗号の原理を検証(実装例)
ここまでの記事を読んでいただき、以下のような疑問がある方も多いとおもいます。
- 原理は分かったけど、本当に2回XOR演算したら元に戻るの?
- XORってビット演算だよね。実際には文章(文字列)だけど大丈夫?
では、実際に検証していきます。
C言語での検証
C言語で XOR を演算させる場合は、ハット「^」を使って記述します。
少し実用には制約がありますが、以下のようなプログラムを書きましょう。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <stdio.h> #include <string.h> static void my_xor (char *src, char *dest, char *key) { int src_len = strlen(src); int key_len = strlen(key); int key_pos = 0, i; for (i = 0; i < src_len; i++, key_pos++) { if (key_pos > key_len) key_pos = 0; dest[i] = src[i] ^ key[key_pos]; } } int main (void) { char ptext[] = "SANACHAN"; char key[] = "1234"; char ctext[16] = {'0'}; char result[16] = {'0'}; /* 平文表示 */ printf ("平文: %s\n", ptext); printf ("鍵 : %s\n", key); /* 暗号化(1回目のXOR) */ my_xor (ptext, ctext, key); printf ("暗号文: %s\n", ctext); /* 復号化(2回目のXOR) */ my_xor (ctext, result, key); printf ("結果: %s\n", result); return 0; } |
実行結果
codepad などで実行すると、以下のような出力を得られます。
Output
1 平文: SANACHAN
2 鍵 : 1234
3 暗号文: bs}uCys}
4 結果: SANACHAN
結果から言えること
- 暗号文は、平文「SANACHAN」と異なる解読不能な文字列
- 鍵との XOR(my_xor関数)を2回実行することで、元の平文「SANACHAN」に戻る
スポンサーリンク
Python言語での検証
Pythonでも同じように検証していきましょう。
今回は、以下の組込関数を利用して XOR 暗号を実現します。
- chr:文字を ASCIIコードに変化する関数
- ord:ASCIIコードを文字に変換する関数
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def my_xor(src, key): return "".join([chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(src, key)]) ptext = "SANACHAN" key = "12345678" print("Plain Text : " + ptext) print("Key : " + key) ctext = my_xor(ptext, key) print("Cipher Text : " + ctext) result = my_xor(ctext, key) print("result : " + result) |
実行結果
codepad などで実行すると、以下のような出力を得られます。
Output
Plain Text : SANACHAN
Key : 12345678
Cipher Text : bs}uv~vv
result : SANACHAN
PythonでもC言語の結果と同様に、結果から以下のことが言えますね。
結果から言えること
- 暗号文は、平文「SANACHAN」と異なる解読不能な文字列
- 鍵との XOR(my_xor関数)を2回実行することで、元の平文「SANACHAN」に戻る
XOR暗号の個人的な実用例
XOR暗号の原理は冒頭で応用例(SSLやAES)を挙げましたが、実際に私が作った個人アプリを紹介します。
パスワード管理アプリ
なぜ作ろうと思ったのか
10年位前にアカウントのハッキング被害にあい、「パスワードの使いまわしってダメだなぁ」と痛感。
幸い何も被害は無かったんですが、「全部ランダムで強固なパスワードにしよう」と思い立ち、
「でも全部覚えられないな・・・」ということで「じゃあアプリ作ろう!」となりました。
なぜ市販のアプリを使わなかったのか
信用できないんですよね。
例えば、鍵をアプリ開発元のサーバーに送信されてても分からないし、
不具合やアプリに感染するウィルスがあっても困るし。
アカウント情報は超重要な個人情報ですので、「自分で作ろう!」になりました。
XOR暗号だけだとすぐに解読される
いろいろ調べていると、XOR暗号を使うのが実装が簡単だと思ったのですが、
暗号文から鍵を推測しやすいので、Camellia暗号アルゴリズムを参考にXOR暗号を応用するようにしました。
おわりに
いかがでしたでしょうか。
XOR暗号を理解していると、メジャーな暗号アルゴリズムであるAESやSSLなど、
鍵の生成や共有方法に着目すればよいので理解がしやすくなると思います。
どんな情報にも暗号化が必須になっている時代ですので、基本は理解していて損はないですよ。
以上、「【原理】XORを利用した暗号方式とC/Pythonでの実装例」でした。