乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

暗号

今回はランサムウェアの項にも出てきた
暗号化技術に関してです。

解説動画はこちら 



前回のランサムウェアの項でも出てきた
暗号化の技術について
最近の暗号を取り上げてみました。


最近の暗号

・DES(Data Encryption Standard)
古い暗号化アルゴリズムであり、56ビットの鍵を使用
DESは現在では安全性の面で脆弱性が指摘されており
より強力な暗号化アルゴリズムが推奨

・AES(Advanced Encryption Standard)
DESの後継として開発された暗号規格
128ビット、192ビット、または256ビットの鍵を使用
AESは現在、広く採用されている暗号化標準であり
高いセキュリティレベルを提供

・RSA
公開鍵暗号方式の一つであり、暗号化と署名に使用
RSAは公開鍵と秘密鍵のペアを使用し
デジタル署名やセキュアな通信のために広く利用

・SHA-256(Secure Hash Algorithm)
256ビットのハッシュ関数
SHA-256は主にデジタル署名やメッセージ認証などの用途で使用され
データの一意のハッシュ値を生成


実際に暗号のコードを見てみましょう


DES暗号
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes

# 鍵を生成
key = get_random_bytes(8)

# データを暗号化する関数
def encrypt_data(data, key):
    cipher = DES.new(key, DES.MODE_ECB)
    padded_data = data + b"\0" * (8 - len(data) % 8)
    encrypted_data = cipher.encrypt(padded_data)
    return encrypted_data

# データを復号化する関数
def decrypt_data(encrypted_data, key):
    cipher = DES.new(key, DES.MODE_ECB)
    decrypted_data = cipher.decrypt(encrypted_data)
    return decrypted_data.rstrip(b"\0")

# データ
data = b"otupy"

# 暗号化
encrypted_data = encrypt_data(data, key)
print("暗号化されたデータ:", encrypted_data)

# 復号化
decrypted_data = decrypt_data(encrypted_data, key)
print("復号化されたデータ:", decrypted_data.decode())


AES暗号
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

# 鍵を生成
key = get_random_bytes(16)

# データを暗号化する関数
def encrypt_data(data, key):
    cipher = AES.new(key, AES.MODE_ECB)
    padded_data = data + b"\0" * (16 - len(data) % 16)
    encrypted_data = cipher.encrypt(padded_data)
    return encrypted_data

# データを復号化する関数
def decrypt_data(encrypted_data, key):
    cipher = AES.new(key, AES.MODE_ECB)
    decrypted_data = cipher.decrypt(encrypted_data)
    return decrypted_data.rstrip(b"\0")

# データ
data = b"otupy"

# 暗号化
encrypted_data = encrypt_data(data, key)
print("暗号化されたデータ:", encrypted_data)

# 復号化
decrypted_data = decrypt_data(encrypted_data, key)
print("復号化されたデータ:", decrypted_data.decode())

RSA暗号
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes

# 秘密鍵の生成
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)
# 公開鍵
public_key = private_key.public_key()

# データを暗号化する関数
def encrypt_data(data, public_key):
    encrypted_data = public_key.encrypt(
        data,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return encrypted_data

# データを復号化する関数
def decrypt_data(encrypted_data, private_key):
    decrypted_data = private_key.decrypt(
        encrypted_data,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return decrypted_data

# データ
data = b"otupy"

# 暗号化
encrypted_data = encrypt_data(data, public_key)
print("暗号化されたデータ:", encrypted_data)

# 復号化
decrypted_data = decrypt_data(encrypted_data, private_key)
print("復号化されたデータ:", decrypted_data.decode())

SHA-256
import hashlib

# データをハッシュ化する関数
def hash_data(data):
    hash_object = hashlib.sha256()
    hash_object.update(data)
    hash_value = hash_object.hexdigest()
    return hash_value

# データ
data = b"otupy"

# ハッシュ値を計算
hashed_value = hash_data(data)
print("計算されたSHA-256ハッシュ値:", hashed_value)




RSA暗号を破るとしたら・・・

ショアのアルゴリズム
量子コンピューターを使用して素因数分解問題を解くためのアルゴリズム
古典コンピューターでは大きな合成数の素因数分解は効率的に解くことが難しい

ショアのアルゴリズムは、1994年にPeter Shorによって提案
素因数分解問題を効率的に解くことができるという特性を持ち
RSA暗号や他の公開鍵暗号方式のセキュリティを脅かす可能性がある


ショアのアルゴリズムの基本的な手順
量子フーリエ変換
 入力として素因数分解したい合成数をエンコードし、量子フーリエ変換を行う
位相推定
 位相推定アルゴリズムを使用して、周期性を持つ関数の周期を見つける
因数分解
 見つかった周期性を利用して、元の合成数の素因数を見つける


ショアのアルゴリズムのコード例

import numpy as np
from qiskit import Aer, QuantumCircuit, transpile, assemble
from qiskit.circuit.library import QFT
from qiskit.visualization import plot_histogram
from math import gcd
from numpy.random import randint

def a_to_the_power_mod(a, power, N):
    result = 1
    for _ in range(power):
        result = (result * a) % N
    return result

def qpe_amod15(a):
    # 位相推定に使用する量子ビットの数
    n_count = 8
    # 量子回路を初期化
    qc = QuantumCircuit(4 + n_count, n_count)
    
    for q in range(n_count):
        qc.h(q)
    qc.x(3 + n_count)
    
    for q in range(n_count):
        # 各位相推定用の量子ビットに対して、逆量子フーリエ変換 QFT を適用
        qc.append(QFT(num_qubits=n_count, inverse=True).to_gate(), range(n_count))
        # a_to_the_power_mod 関数を使用してモジュラエクスポネンシエーションを計算し、その結果を量子回路に追加
        qc.append(a_to_the_power_mod(a, 2**q, 15), [q + n_count, n_count + 1, n_count + 2, n_count + 3])
    # 全ての位相推定用の量子ビットに対して再度逆量子フーリエ変換 QFT を適用
    qc.append(QFT(num_qubits=n_count, inverse=True).to_gate(), range(n_count))
    qc.measure(range(n_count), range(n_count))

    # Aer シミュレーターを使用して量子回路をシミュレーションし、結果を取得
    aer_sim = Aer.get_backend('qasm_simulator')
    t_qc = transpile(qc, aer_sim)
    qobj = assemble(t_qc)
    result = aer_sim.run(qobj).result()
    # 測定結果のカウントを返す
    counts = result.get_counts()
    return counts

N = 15
a = 7
counts = qpe_amod15(a)
plot_histogram(counts)


RSA暗号への影響

RSA暗号は素因数分解の困難さに基づいて安全性が保証されている
しかし、ショアのアルゴリズムを用いる量子コンピューターが実用化されると
現在のRSA暗号の鍵長が十分に安全でなくなる可能性がある

量子コンピューターを用いたショアのアルゴリズムによって
RSA暗号の破綻が懸念されてる

ショアのアルゴリズムは量子コンピューターの登場による
セキュリティ上の脅威として注目されており
RSA暗号を含む多くの暗号方式の安全性に影響を与える可能性がある


もしRSA暗号が破られた場合は

通信の機密性の喪失
デジタル署名の信頼性の喪失
金融取引やオンラインショッピングの脆弱性
国家機密や軍事情報の漏洩
RSA暗号を用いたランサムウェアの取引が成立しなくなる


今後の暗号技術はどうなるか

ポスト量子暗号 (Post-Quantum Cryptography):
量子コンピューターの発展に伴い、RSAやECCなどの
従来の暗号方式が量子コンピューターによって破られる可能性が出てきている

ポスト量子暗号は、量子コンピューターが存在しても
安全性が確保されるように設計されている

例として、NTRU暗号や格子(Lattice)ベース暗号方式が挙げられる


終わりに

量子コンピューターがいつ実用化されるのか
RSA暗号がいつ破られるのか
新しい暗号技術がどうなるのか

今後もセキュリティー上の観点から
暗号関連は見逃せない展開になっていくと思われます

それでは


今回はCTFの問題を解いてみました

解説動画はこちら



CTFとは

さて、まずCTFとは何でしょうか?

CTF(Capture The FLAG )
のことで情報セキュリティの分野で
専門知識や技術を駆使して
隠されているFlag(答え)を見つけ出し
時間内に獲得した合計点数を競う
ハッキングコンテストのことになります

クイズ形式の問題の他
疑似的な攻防戦も有り
以下のような技術が必要となってきます

・ネットワーク
  通信技術、トラフィックのキャプチャ
・フォレンジクス
  情報の秘匿、ログ解析、ファイルフォーマット、データの復元
・Web技術    
  Webアプリケーションの脆弱性、データベースアクセス
・プログラミング
  プログラミング言語、組み込み技術、リバースエンジニアリング
・暗号化技術
  符号化、公開鍵基盤(PKI)
・脆弱性調査
  バグ、攻撃コードの送信

この問題が解ける様になると
次のような技術向上のメリットがあります
 情報セキュリティに対する知識が身につく
 情報セキュリティに限らない広範囲の知識が身につく
 実践的なスキルが身につく
 トラブルシューティングのカンが身につく


世界中でこういった問題が
解けるサイトがありますが
日本にも問題が解けるサイトがあるので
今回はこちらの問題を拝借いたします

https://ctf.cpaw.site/index.php
なお問題を見るにはアカウントが必要です


[Crypto] 1
古典暗号文解読の問題です

シーザー暗号というものがあります
これは元の平文を何文字かシフトさせたものです

アルファベットでシーザー暗号を考えると
平文 : abc

3文字シフト

暗号文 : def

のようになります

平文を暗号化、暗号を平文にするのも
同じプログラムで出来るので
シーザー暗号を作るプログラムを考えれば
この問題を解けます

このようなコードで暗号解読できます
st = 'fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}'

def caesar_cipher_encrypt(plaintext , key):
    cipher = ""
    for p in list(plaintext):
        if 'A' <= p <= 'Z':
            cipher += chr((ord(p) - ord('A') + key)%26 + ord('A'))
        elif 'a' <= p <= 'z':
            cipher += chr((ord(p) - ord('a') + key)%26 + ord('a'))
        else:
            cipher += p
    return cipher
    
for i in range(20,26):
    print(i,caesar_cipher_encrypt(st , i))




[PPC]

並べ替えの問題です

配列の中身を大きい順に並べ替えて
くっつけて 並べ替えた後の値 
がフラグになります

配列のソート
数値の文字列化
文字列の連結

これが出来ればこの問題が解けるので
プログラムに関わらず
エクセルやテキストエディターなどでも
解くことは出来ると思います

一応この問題を解くコードはこれです
data = [15,1,93,52,66,31,87,0,42,77,46,24,99,10,19,36,27,4,58,76,2,81,50,102,33,94,20,14,80,82,49,41,12,143,121,7,111,100,60,55,108,34,150,103,109,130,25,54,57,159,136,110,3,167,119,72,18,151,105,171,160,144,85,201,193,188,190,146,210,211,63,207]

txt = 'cpaw{{{0}}}'
txt = txt.format(''.join(map(str,sorted(data,reverse=True))))
print(txt)




[Forensics] 

画像解析の問題です

画像ファイルには位置情報など
様々な情報(Exif情報)が
付加されることがあるので

そこから撮影場所を
特定することも出来たりします

写真に写っている川の名前を特定する問題で
画像名は river.jpg です

画像をみたい方は問題のサイトに
アクセスして手に入れて頂くか
動画の方をみてくださいませ


画像を見るコードはこれです
from PIL import Image
import PIL.ExifTags as ExifTags
import matplotlib.pyplot as plt

file_path = 'river.jpg'

img = Image.open(file_path)

plt.imshow(img)
plt.axis('off')
plt.show()

ここから画像のExif情報を抽出します
Exif情報はかなりたくさんの項目がありますが
GPSの緯度経度を取得します

画像のExif情報を取得するコード
exif = {}
exif = img._getexif()
for k, v in exif.items():
    if k==34853:
        for gk,gv in v.items():
            print(gk,gv)
0 b'\x02\x03\x00\x00'
1 N
2 (31.0, 35.0, 2.76)
3 E
4 (130.0, 32.0, 51.7272)



緯度・経度は一般に度・分・秒で表します
分,秒は時間と同じように60進法になっています
秒に小数点以下の数値をつける場合は
秒の後ろに10進法で表します


ExifのGPSは度分秒なので
10進法にするには変換が必要です
deg , minu , sec = 31.0, 35.0, 2.76
N = deg + minu/60.0 + sec/3600.0

deg2 , minu2 , sec2 = 130.0, 32.0, 51.7272
E = deg2 + minu2/60.0 + sec2/3600.0

print('{0},{1}'.format(N,E))
31.5841,130.547702


あとはこの緯度経度を
地図に打ち込んで見てみましょう

そうすると
スクリーンショット 2022-11-12 10.03.50

こんな場所でした

拡大してみると
スクリーンショット 2022-11-12 10.04.13

鹿児島県にある川の
陸橋の上あたりから
取られた写真であることが分かります

この川の名前が答えですね




[Crypto] 2

最後は
次のC言語のコードを実行すると
回答が得られる問題です

元のCのプログラムを見てみると
暗号を平文に直すアルゴリズムのようでした

C言語が分かる方は
そのまま実行すれば答えば出てきます

C言語が分からない人は
他の言語に直すのもアリです

Pythonに直してみると
次の様なコードになります

flag = 'ruoYced_ehpigniriks_i_llrg_stae'

for key in range(2,7):
    answer = ''
    for i in range( key - 1, len(flag)+1,key):
        for j in range(i , i-key , -1):
            idx = j
            if j < len(flag):
                answer+=flag[idx]

    print(x , 'cpaw{{{0}}}'.format(answer))


まとめ
CTFには
暗号解読やフォレンジックスなど
プログラミングで解ける問題も
多く含まれています

プログラミング技術の向上意外にも
セキュリティー知識を身に付けるための
問題が数多くあるので
これを解ける様になると
セキュリティーに関する技術力の
向上が見込めると思います

それでは

今回はRSA暗号について
考えてみることにしました。


解説動画はこちら


さてさて
RSAッて良く分からないので
Wikiで調べてみることにしました。

RSA暗号とは、桁数が大きい合成数の素因数分解問題が
困難であることを安全性の根拠とした公開鍵暗号の一つ
鍵生成、暗号化、復号の3つのアルゴリズムで定義される

うん・・・・・

RSA暗号は次のような方式である

鍵のペア(公開鍵と秘密鍵)を作成して公開鍵を公開する
まず、適当な正整数 E を選択する

また、大きな2つの素数の組み P,Q を生成し
それらの積(N=PQ)を求めて、組みE,N を
平文の暗号化に使用する鍵(公開鍵)とする

2つの素数P,Qは秘密に保管し
暗号文の復号に使用する鍵(秘密鍵)D の生成にも使用する

暗号化(平文 M から暗号文 C を作成する):
復号(暗号文 C から元の平文 M を得る): 
暗号化(E 乗)は公開鍵 E,N があれば容易に計算でき
復号(D 乗)も容易に計算できる

しかし、秘密鍵 D を知らずに
解読(法 N の下で E 乗根を計算)するのは
「N の素因数を知らないと難しい」と考えられている

つまり
「秘密鍵を用いずに暗号文から平文を得ることは難しい」
と信じられている

これがRSA暗号の安全性の根拠である


はい良くわかりませんです!!!


というわけで
サンプルコードを作ってみました。

# RSA暗号の例
import random
import fractions
import sympy
import warnings 
warnings.filterwarnings('ignore')

# 乱数生成
def _random():
    digit = 10
    return random.randrange(10**(digit - 1),10**digit)

# 最小公倍数
def _lcm(p , q):
    return (p * q) // fractions.gcd(p, q)

# 拡張ユークリッド
def _euclid(x,y):
    c0, c1 = x, y
    a0, a1 = 1, 0
    b0, b1 = 0, 1
    while c1 != 0:
        m = c0 % c1
        q = c0 // c1
        c0, c1 = c1, m
        a0, a1 = a1, (a0 - q * a1)
        b0, b1 = b1, (b0 - q * b1)
    return c0, a0, b0

# キー生成
def generate_key(p = 0,q = 0,e = 0,d = 0,n = 0,l = 0):
    if p == 0:
        while True:
            p = _random()
            if sympy.isprime(p):break
    _p = p
    if q == 0:
        while True:
            q = _random()
            if sympy.isprime(q) and p != q:break
    _q = q
    if n == 0:
        n = p * q
    _n = n
    if l == 0:
        l = _lcm(p - 1, q  - 1)
    _l = l
    if e == 0:
        while True:
            i = random.randint(2,l)
            if fractions.gcd(i, l) == 1:
                e = i
                break
    _e = e
    if d == 0:
        _c, a, _b = _euclid(e, l)
        d = a % l
    _d = d
    return _e,_d,_n,_p,_q,_l

# 暗号化
def encrypt(_e,_n,plaintext):
    st,ciphertext = "",[]
    for i in map((lambda x: pow(ord(x), _e,_n)),list(plaintext)):
        ciphertext.append(i)
        st += str(i)
    return st,ciphertext

# 復号化
def decrypt(_d,_n,ciphertext):
    cip , st = [] , ""
    for i in  list(ciphertext):
        tmp = chr(pow(i, _d,_n))
        cip.append(tmp)
        st += str(tmp)
    return st

使う際は平文として暗号化したい
文字列を定義しておきます。

plain_text = '文字列を入力'


早速使っていきましょう。
暗号化するところと
復号化するところのコードです。
# 初期化
ciphertext = []
_e = _d = _n = _p = _q = _l = 0

# キー生成
_e,_d,_n,_p,_q,_l = generate_key(0,0,65537)

# 暗号化
e_text , ciphertext = encrypt(_e,_n,plain_text)

# 復号化
d_text = decrypt(_d,_n,ciphertext)

print('素数 p : {0}'.format(_p))
print('素数 q : {0}'.format(_q))
print('l : {0}'.format(_l))
print('公開鍵 e : {0}'.format(_e))
print('公開鍵 n : {0}'.format(_n))
print('秘密鍵 d : {0}'.format(_d))
print()

print('暗号文(String) : {0}'.format(e_text))
print()
print('暗号文(List) : {0}'.format(ciphertext))

素数 p : 9558530557
素数 q : 5377612577
l : 12850518531506468064
公開鍵 e : 65537
公開鍵 n : 51402074140962015389
秘密鍵 d : 104902992421928993

暗号文(String) : 502447033995121916661752375033108949410513472439923007515854175237503310894941051347243992300751585434094647152221189902182494137669912247793511588278946816434351158827894681643422454674385942603378

暗号文(List) : [50244703399512191666, 17523750331089494105, 13472439923007515854, 17523750331089494105, 13472439923007515854, 34094647152221189902, 18249413766991224779, 3511588278946816434, 3511588278946816434, 22454674385942603378]

素数P,Qや
鍵N,Dは毎回変わってしまいます。

公開鍵Nと秘密鍵Dがあれば
暗号を解読することができます。

暇な方は上記の鍵と暗号文から
復号してみてくださいね。

人類を平和にする
「あいことば」を暗号にしてみましたよ

解読面倒な方は
動画見て下さいねーー


このサンプルコードでは
およそ20桁ほどの秘密鍵になりますんで
9千京回ほどブルートフォースすれば
暗号解読できるかもしれません!!!!

鼻血出せば解けるかもしれませんねえ
そんなアニメが有るとか無いとか

自分ならやらないっすねーー

今回はここまででーす
それでは。



今回はモールス信号で
あそんでみました。

解説動画はこちら




さて
モールス符号は、電信で用いられている
可変長符号化された文字コードのことで

国際モールス符号は
短点(・)と長点(-)
を組み合わせて

アルファベット・数字・記号を
表現するそうです。

和文なども有るみたいだけど
面倒くさいので今回はアルファベットだけを
変換するコードを作ります。

アルファベットをモールス符号に変えるコード
words = {"A": "・-","B": "-・・・","C": "-・-・","D": "-・・","E": "・","F": "・・-・",
              "G": "--・","H": "・・・・","I": "・・","J": "・---","K": "-・-","L": "・-・・",
              "M": "--","N": "-・","O": "---","P": "・--・","Q": "--・-","R": "・-・",
              "S": "・・・","T": "-","U": "・・-","V": "・・・-","W": "・--","X": "-・・-","Y": "-・--","Z": "--・・"
}

def morse_code_encrypt(st):
    codes = [words[s] for s in st if s in words]
    return '  '.join(codes)

モールス符号をアルファベットに変えるコード
morses = {v:k for k,v in words.items()}

def morse_code_decrypt(code):
    codes = code.replace('  ',' ').split(' ')
    answers = [morses[c] for c in codes if c in morses]
    return '  '.join(answers)


これでコードを実行して
モールスに変えてみると
morse_code_encrypt('SOS')
・・・ --- ・・・


解読してみると
morse_code_decrypt('・・・  ---  ・・・')
S O S


行けましたね!!
これで金田一も真っ青な
モールス解読ができる様になりました。

プログラムさえあれば
ダイイングメッセージも
お茶の子さいさいですね。


最後にモールスクイズです

何と書いてあるでしょうか?
答えはコードを実行するか
動画をどうぞwww

1.
insta



2.
omedetou



3.
yorosiku




今回はこれまでです
それでは。











このページのトップヘ