[PYTHON] Implementieren Sie RSA

Das Memo, das ich studiert habe. Es gab nicht viele Artikel über die Implementierung von RSA, die mit großen Primzahlen gut funktionieren.

Zweck

Implementieren Sie eine 256-Bit-RSA-Verschlüsselung mit der minimalen Konfiguration.

Wie mache ich RSA

Weitere Informationen finden Sie in wikipedia. Schreiben Sie nur den Flow.

  1. Bestimmen Sie die Anzahl der Bits. (Diesmal 256 Bit)
  2. Machen Sie große Primzahlen $ p, q $. (128 Bit, die Hälfte von 256)
  3. n=pq
  4. Bestimmen Sie eine Zahl $ e $, die kleiner als $ (p-1) (q-1) $ ist und zueinander primiert.
  5. Suchen Sie $ d $, das $ ed \ equiv 1 \ pmod n $ ist.

Sie sind jetzt bereit. Verschlüsselung ist

  1. Machen Sie einfach $ m $ ($ m <n $)
  2. Der Code $ c $ lautet $ m ^ e \ pmod n $

Die Entschlüsselung ist mit $ m = c ^ d \ pmod n $ in Ordnung.

Es gibt keine so schwierige Berechnung, aber der Punkt ist

Leistungsberechnung ist schwer

In RSA werden Potenzen mit Hunderten von Ziffern multipliziert, sodass eine exponentielle Multiplikation nicht gehorsam erfolgen kann. ** Schnelle Exponentialberechnung ** macht es schneller. Da wir außerdem den Überschuss der Dunkelheit und nicht die Dunkelheit selbst finden möchten, können wir auch diesen Punkt optimieren. Angenommen, Sie möchten den Rest von $ a $ berechnen, der um $ n $ auf die $ b $ -Power erhöht wird. Konvertieren Sie zuerst $ b $ in eine Binärzahl. Jede Ziffer der Binärzahl sei $ b_i $ $ a^b = a^{\sum 2^{i}b_i}=\prod a^{2^ib_i} $ Es wird sein. Hier, $ a^{2^{i+1}}=a^{2^i}*a^{2^i} $ So können Sie die nächste Ziffer mit einer Multiplikation berechnen. Darüber hinaus möchten Sie den Potenzwert geteilt durch $ n $ berechnen. Wenn Sie also bei jedem Schritt der Multiplikation zu viel nehmen, müssen Sie nur Werte verarbeiten, die kleiner als $ n $ sind. Wenn beispielsweise $ a = 3, b = 11, n = 21 $, dann ist $ b = (1011) _2 , d. H. $ b=2^3+2^1+2^0 $$

Wie $ a^b=3^{2^3+2^1+2^0}=3^{2^3}*3^{2^1}*3^{2^0} $

3^{2^0}=3
3^{2^1}=3^{2^0}*3^{2^0}=9
3^{2^2}=3^{2^1}*3^{2^1}=81
3^{2^3}=3^{2^2}*3^{2^2}=6561

Teilen Sie jedes durch $ 21 $ und nehmen Sie zu viel, wenn es während der Berechnung 21 überschreitet. $ 3*9*6561\equiv (3*9)*9\equiv 6*9\equiv 12 $

main.py


def modular_exp(a, b, n):
    res = 1
    while b != 0:
        if b & 1 != 0:
            res = (res * a) % n
        a = (a * a) % n
        b = b >> 1

    return res

Wie man eine große Primzahl macht

Anscheinend ist es ziemlich schwierig, Primzahlen direkt zu generieren. Praktisch scheint es einfach zu sein, ** Zufallszahlen zu generieren, bis eine Primzahl erreicht ist **.

Zufällige Generierung

Da die Anzahl der Bits angegeben ist, ist es in Ordnung, wenn 0,1 zufällig für diese Anzahl angeordnet sind.

main.py


def gen_rand(bit_length):
    bits = [random.randint(0,1) for _ in range(bit_length - 2)]
    ret = 1
    for b in bits:
        ret = ret * 2 + int(b)
    return ret * 2 + 1

Die maximalen und minimalen Bits werden stattdessen auf 1 gesetzt. Dies liegt daran, dass das maximale Bit eine ungerade Zahl ist, so dass die Anzahl der Stellen nicht verringert wird. Der Grund, warum es seltsam ist, ist, dass ich jetzt Primzahlen will und gerade Zahlen keine Primzahlen sind.

Primzahlurteil

Ich möchte feststellen, ob die riesige Zufallszahl $ p $ eine Primzahl ist. $ P $ ist so groß, dass es zu langsam ist, es in der richtigen Reihenfolge zu teilen. ** [Mirror-Rabin Prime Number Judgement Method](https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A9%E3%83%BC%E2%80%93%E3% 83% A9% E3% 83% 93% E3% 83% B3% E7% B4% A0% E6% 95% B0% E5% 88% A4% E5% AE% 9A% E6% B3% 95) ** Scheint oft benutzt zu werden. Der Algorithmus ist auch auf Wikipedia, also implementieren Sie ihn so wie er ist. Einfach ausgedrückt, wir überprüfen die Anforderungen für Primzahlen viele Male und stellen fest, dass es sich bei allen Passzahlen wahrscheinlich um Primzahlen handelt. Wenn sie nicht einmal eine Passzahl bestehen, handelt es sich um synthetische Zahlen. ** Da dies ein probabilistischer Algorithmus zur Beurteilung von Primzahlen ist, kann er eine zusammengesetzte Zahl als Primzahl beurteilen. ** ** ** Es scheint jedoch, dass das falsch positive Ergebnis in einem Check ungefähr $ \ frac {1} {4} $ ist. Wenn Sie es also Dutzende Male tun, wird es im praktischen Gebrauch fast 0 sein, sodass es kein Problem zu geben scheint.

main.py


def mr_primary_test(n, k=100):
    if n == 1:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    d = n - 1
    s = 0
    while d % 2 != 0:
        d /= 2
        s += 1

    r = [random.randint(1, n - 1) for _ in range(k)]
    for a in r:
        if modular_exp(a, d, n) != 1:
            pl = [(2 ** rr) * d for rr in range(s)]
            flg = True
            for p in pl:
                if modular_exp(a, p, n) == 1:
                    flg = False
                    break
            if flg:
                return False
    return True

Die Erzeugung von Primzahlen ist also so.

main.py


def gen_prime(bit):
    while True:
        ret = gen_rand(bit)
        if mr_primary_test(ret):
            break
    return ret

Wie entscheide ich mich für e?

Es spielt keine Rolle, ob es sich um eine Primzahl für $ (p-1) (q-1) $ handelt, daher ist es schnell möglich, daraus eine Primzahl zu machen.

p = gen_prime(128)
q = gen_prime(128)
e = gen_prime(128)

Ich denke, damit gibt es kein Problem. wikipedia gab an, dass $ 65537 $ häufig verwendet wird. Es scheint, dass eine feste Nummer in Ordnung ist.

d?

Sie können die erweiterte euklidische Methode der gegenseitigen Teilung verwenden. Ursprünglich für natürliche Zahlen $ a, b $ $ ax+by=gcd(a,b) $

Es ist ein Algorithmus, um $ x, y $ wie z. (gcd ist die maximale Verpflichtung) Verwenden Sie dies für $ e, (p-1) (q-1) $. Da die maximale Verpflichtung 1 aus der Definition von $ e $ beträgt, $ ex+(p-1)(q-1)y=1 $

Teilen Sie beide Seiten durch $ (p-1) (q-1) $ $ ex\equiv 1 $ Also ist dieses $ x $ $ d $. Die erweiterte euklidische Methode der gegenseitigen Teilung war hier, also habe ich sie kopiert. $ x_0 $ ist $ d $.

main.py


def xgcd(b, n):
    x0, x1, y0, y1 = 1, 0, 0, 1
    while n != 0:
        q, b, n = b // n, n, b % n
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return b, x0, y0


def gen_d(e, l):
    _, x, _ = xgcd(e, l)
    return x % l

Lauf

Der Rest funktioniert, wenn Sie einen einfachen Text vorbereiten.

main.py


bit_length = 128
p = gen_prime(bit_length)
q = gen_prime(bit_length)
e = gen_prime(bit_length)
d = gen_d(e, (p - 1) * (q - 1))
n = p * q

m = 123456789
c = modular_exp(m, e, n)  #Geheimtext
m_ = modular_exp(c, d, n)  # 123456789

Es funktionierte sogar mit 1024bit. (Es dauerte ungefähr 20 Sekunden) Tatsächlich ist es gefährlich, denselben Klartext viele Male zu verschlüsseln. Es scheint also, dass am Ende eine Zufallszahl hinzugefügt wird (zum Zeitpunkt der Entschlüsselung gelöscht), und wenn m zu klein ist, wird sie aufgefüllt.

Recommended Posts

Implementieren Sie RSA
Implementieren Sie REPL