2020-04-15 23:05:03 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
#program to generate rsa key pair using methods in EE-305
|
|
|
|
# Hesham Banafa
|
|
|
|
|
2020-04-17 19:35:20 +02:00
|
|
|
"""
|
|
|
|
Large Prime check: https://www.alpertron.com.ar/ECM.HTM
|
|
|
|
To crack a key, find p or q through n. (prime factorazation)
|
|
|
|
Another way to find p or q from the private key:
|
|
|
|
https://crypto.stackexchange.com/questions/13113/how-can-i-find-the-prime-numbers-used-in-rsa
|
|
|
|
"""
|
2020-04-17 11:23:08 +02:00
|
|
|
|
2020-04-16 04:07:52 +02:00
|
|
|
import math
|
2020-04-16 04:54:36 +02:00
|
|
|
import os
|
2020-04-16 11:35:42 +02:00
|
|
|
import sys
|
2020-04-15 23:05:03 +02:00
|
|
|
|
2020-04-17 04:21:58 +02:00
|
|
|
keysFolder = "keys/"
|
|
|
|
byteOrder = "little"
|
2020-04-17 19:35:20 +02:00
|
|
|
N=0
|
|
|
|
E=1
|
|
|
|
D=2
|
|
|
|
P=3
|
|
|
|
Q=4
|
|
|
|
PHI=5
|
2020-04-17 22:53:07 +02:00
|
|
|
ID=6
|
2020-04-17 04:21:58 +02:00
|
|
|
|
2020-04-15 23:05:03 +02:00
|
|
|
def main():
|
2020-04-17 15:32:10 +02:00
|
|
|
|
|
|
|
if sys.argv[1] == "gen": ##rsa gen <keysize> <keyname>
|
2020-04-16 17:56:04 +02:00
|
|
|
keyFileName = sys.argv[3]
|
2020-04-17 22:53:07 +02:00
|
|
|
key = generateKeys(keyFileName, int(sys.argv[2]))
|
|
|
|
printKey(key)
|
2020-04-16 11:35:42 +02:00
|
|
|
try:
|
|
|
|
saveKeyFile(key, keyFileName)
|
|
|
|
except IOError:
|
|
|
|
print("could not write file")
|
|
|
|
exit(1)
|
|
|
|
except Exception as ex:
|
|
|
|
print(ex)
|
|
|
|
exit(1)
|
2020-04-16 13:33:24 +02:00
|
|
|
if len(sys.argv) == 4:
|
|
|
|
if sys.argv[1] == "encrypt": ##rsa encrypt <message> <key>
|
|
|
|
msg = sys.argv[2]
|
2020-04-17 16:31:34 +02:00
|
|
|
msg_list = msg.split()
|
2020-04-16 13:33:24 +02:00
|
|
|
keyName = sys.argv[3]
|
|
|
|
key = readKeyFile(keyName)
|
2020-04-17 19:35:20 +02:00
|
|
|
key_public = (key[N], key[E])
|
2020-04-17 16:31:34 +02:00
|
|
|
msg_encrypted = ""
|
|
|
|
for word in msg_list:
|
|
|
|
msg_encrypted = msg_encrypted + " " + str(encrypt(word, key_public))
|
|
|
|
#msg_encrypted = encrypt(msg, key_public)
|
|
|
|
print("Encrypted msg: \n", msg_encrypted)
|
2020-04-17 22:53:07 +02:00
|
|
|
print("Signed: \n", sign(msg_encrypted, key))
|
2020-04-16 13:33:24 +02:00
|
|
|
if sys.argv[1] == "decrypt": ##rsa decrypt <cipher> <key>
|
2020-04-17 16:31:34 +02:00
|
|
|
cipher = sys.argv[2]
|
|
|
|
cipher_list = cipher.split()
|
2020-04-18 01:27:16 +02:00
|
|
|
sig = verify(cipher_list)
|
|
|
|
del cipher_list[-1]
|
2020-04-17 16:31:34 +02:00
|
|
|
msg_decrypted = ""
|
2020-04-16 13:33:24 +02:00
|
|
|
key = readKeyFile(sys.argv[3])
|
2020-04-17 16:31:34 +02:00
|
|
|
for cipher_word in cipher_list:
|
2020-04-17 19:35:20 +02:00
|
|
|
msg_decrypted = msg_decrypted + " " + str(decrypt(int(cipher_word),key[D],key[N]))
|
2020-04-16 17:56:04 +02:00
|
|
|
#with open(fileName, "r") as cipherFile:
|
|
|
|
# cipher = int(cipherFile.readline()) ##one line may make problems later with padding
|
2020-04-18 01:27:16 +02:00
|
|
|
print("Signed by: ", sig)
|
2020-04-17 16:31:34 +02:00
|
|
|
print("Decrypted message: \n", msg_decrypted)
|
2020-04-16 13:33:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2020-04-16 04:07:52 +02:00
|
|
|
|
2020-04-17 22:53:07 +02:00
|
|
|
def generateKeys(id, bits=64):
|
2020-04-17 11:23:08 +02:00
|
|
|
from multiprocessing.pool import Pool
|
2020-04-16 08:48:31 +02:00
|
|
|
#Primes of size 32 bit random
|
|
|
|
#resulting in a 64-bit key mod
|
2020-04-17 11:23:08 +02:00
|
|
|
pool = Pool()
|
|
|
|
result1 = pool.apply_async(getPrime, [int(bits/2)])
|
|
|
|
result2 = pool.apply_async(getPrime, [int(bits/2)])
|
|
|
|
p = result1.get()
|
|
|
|
q = result2.get()
|
2020-04-16 08:48:31 +02:00
|
|
|
n = p*q
|
2020-04-16 11:35:42 +02:00
|
|
|
#print("n: ", n)
|
2020-04-16 08:48:31 +02:00
|
|
|
|
|
|
|
#lamda(n) = LCM(p-1, q-1)
|
|
|
|
#Since LCM(a,b) = ab/GCD(a,b)
|
|
|
|
#gcd = math.gcd(p-1, q-1)
|
|
|
|
#print("GCD: ", gcd)
|
|
|
|
#lcm = abs((p-1) * (q-1)) / gcd
|
|
|
|
#print("LCM: ", lcm)
|
|
|
|
phi = (p-1)*(q-1)
|
2020-04-16 11:35:42 +02:00
|
|
|
#print("phi: ", phi)
|
2020-04-16 08:48:31 +02:00
|
|
|
#e exponant should be 1 < e < lamda(n) and GCD(e, lamda(n)) = 1 (coprime)
|
|
|
|
# recommended value is 65,537
|
|
|
|
e = 65537
|
|
|
|
d = pow(e,-1,phi)
|
2020-04-17 22:53:07 +02:00
|
|
|
return (n, e, d, p, q, phi, id)
|
2020-04-16 08:48:31 +02:00
|
|
|
|
2020-04-16 11:35:42 +02:00
|
|
|
def encrypt(message, publicKey):
|
2020-04-16 13:33:24 +02:00
|
|
|
msg_text = message
|
2020-04-17 19:35:20 +02:00
|
|
|
n = publicKey[N]
|
|
|
|
e = publicKey[E]
|
2020-04-16 13:33:24 +02:00
|
|
|
print("using n: {0}, e: {1}".format(n, e))
|
|
|
|
|
2020-04-17 04:21:58 +02:00
|
|
|
msg_number_form = int.from_bytes(msg_text.encode(), byteOrder)
|
2020-04-17 16:31:34 +02:00
|
|
|
print("Word: %s or %d" % (msg_text, msg_number_form))
|
2020-04-16 13:33:24 +02:00
|
|
|
|
|
|
|
msg_encrypted_number_form = pow(msg_number_form, e, n) # c = msg^e mod n
|
|
|
|
return msg_encrypted_number_form
|
2020-04-16 08:48:31 +02:00
|
|
|
|
2020-04-16 11:35:42 +02:00
|
|
|
def decrypt(cipher, privateKey, n):
|
2020-04-16 13:33:24 +02:00
|
|
|
msg_encrypted_number_form = cipher
|
|
|
|
d = privateKey
|
2020-04-17 03:38:09 +02:00
|
|
|
msg_decrypted_number_form = pow(msg_encrypted_number_form, d, n) # msg = c^d mod n
|
2020-04-16 13:33:24 +02:00
|
|
|
msg_decrypted = int(msg_decrypted_number_form)
|
2020-04-16 17:56:04 +02:00
|
|
|
try:
|
2020-04-17 04:21:58 +02:00
|
|
|
msg_decrypted = str(msg_decrypted.to_bytes(msg_decrypted.bit_length(), byteOrder).decode()).strip()
|
2020-04-16 17:56:04 +02:00
|
|
|
except UnicodeDecodeError:
|
|
|
|
print("Cant decrypt properly")
|
2020-04-16 13:33:24 +02:00
|
|
|
return msg_decrypted
|
2020-04-15 23:05:03 +02:00
|
|
|
|
|
|
|
def getPrime(bits):
|
2020-04-16 04:54:36 +02:00
|
|
|
while True:
|
2020-04-16 08:48:31 +02:00
|
|
|
#Byte order "little" or "big" does not matter here since we want a random number from os.urandom()
|
2020-04-17 04:21:58 +02:00
|
|
|
x = int.from_bytes(os.urandom(int(bits/8)), byteOrder)
|
2020-04-17 03:40:31 +02:00
|
|
|
print("trying: ", x, end="")
|
2020-04-16 04:54:36 +02:00
|
|
|
if isPrime(x):
|
2020-04-17 03:40:31 +02:00
|
|
|
print("\nprime: ", x)
|
2020-04-16 04:54:36 +02:00
|
|
|
return x
|
2020-04-17 03:40:31 +02:00
|
|
|
print("\r",end="")
|
2020-04-16 04:54:36 +02:00
|
|
|
|
2020-04-15 23:05:03 +02:00
|
|
|
|
|
|
|
def isPrime(number):
|
2020-04-16 04:07:52 +02:00
|
|
|
if number == 2:
|
|
|
|
return True
|
|
|
|
|
|
|
|
#if 2 devides number then num is not prime. pg.21
|
|
|
|
if number % 2 == 0 or number == 1:
|
|
|
|
return False
|
|
|
|
|
|
|
|
#largest integer less than or equal square root of number (K)
|
|
|
|
rootOfNum = math.sqrt(number)
|
|
|
|
K = math.floor(rootOfNum)
|
|
|
|
|
|
|
|
#Take odd D such that 1 < D <= K
|
|
|
|
#If D devides number then number is not prime. otherwise prime.
|
2020-04-16 20:51:36 +02:00
|
|
|
for D in range(1, K, 2):
|
|
|
|
if D % 2 == 0 or D == 1:
|
2020-04-16 04:07:52 +02:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if number % D == 0 or number % 5 == 0:
|
|
|
|
return False
|
|
|
|
return True
|
2020-04-15 23:05:03 +02:00
|
|
|
|
2020-04-17 22:53:07 +02:00
|
|
|
def sign(encrypted_msg, key):
|
|
|
|
enc_msg = str(encrypted_msg)
|
|
|
|
encrypted_msg_list = enc_msg.split()
|
|
|
|
enc_sig = encrypt("sig:"+key[ID], (key[N], key[D]))
|
|
|
|
encrypted_msg_list.append(enc_sig)
|
|
|
|
signed_msg = ""
|
|
|
|
for word in encrypted_msg_list:
|
|
|
|
signed_msg = str(signed_msg) + " " + str(word)
|
2020-04-18 01:27:16 +02:00
|
|
|
return signed_msg.strip()
|
|
|
|
|
|
|
|
def verify(cipher_list):
|
|
|
|
sig = "Unknown"
|
|
|
|
local_keys = os.listdir(keysFolder)
|
|
|
|
cipher_list.reverse() #To get last word using index 0
|
|
|
|
encrypted_sig = cipher_list[0]
|
|
|
|
cipher_list.reverse()
|
|
|
|
for key_name in local_keys:
|
|
|
|
key = readKeyFile(key_name)
|
|
|
|
print("Found key: ", key_name)
|
|
|
|
sig = str(decrypt(int(encrypted_sig), key[E], key[N]))
|
|
|
|
if "sig:" in sig:
|
|
|
|
return sig.replace("sig:","")
|
2020-04-17 22:53:07 +02:00
|
|
|
|
2020-04-16 11:35:42 +02:00
|
|
|
def readKeyFile(keyName):
|
|
|
|
key = tuple()
|
2020-04-17 04:21:58 +02:00
|
|
|
with open(keysFolder+keyName, "r") as keyFile:
|
2020-04-16 11:35:42 +02:00
|
|
|
tempkey = keyFile.readlines()
|
2020-04-17 19:35:20 +02:00
|
|
|
if len(tempkey) == 2: #means it only public part (n, e)
|
2020-04-17 22:53:07 +02:00
|
|
|
key = (int(tempkey[N].strip(), 16), int(tempkey[E].strip(), 16), 0, 0, 0, 0, tempkey[ID])
|
2020-04-17 19:35:20 +02:00
|
|
|
else: #Make this a loop from 0 to 5
|
|
|
|
key = (int(tempkey[N].strip(), 16),
|
|
|
|
int(tempkey[E].strip(), 16),
|
|
|
|
int(tempkey[D].strip(), 16),
|
|
|
|
int(tempkey[P].strip(), 16),
|
|
|
|
int(tempkey[Q].strip(), 16),
|
2020-04-17 22:53:07 +02:00
|
|
|
int(tempkey[PHI].strip(), 16),
|
|
|
|
str(tempkey[ID].strip()))
|
2020-04-16 11:35:42 +02:00
|
|
|
return key
|
|
|
|
|
|
|
|
|
|
|
|
def saveKeyFile(key, fileName):
|
2020-04-17 15:34:03 +02:00
|
|
|
if not os.path.isdir(keysFolder):
|
|
|
|
os.makedirs(keysFolder)
|
2020-04-17 04:21:58 +02:00
|
|
|
with open(keysFolder+fileName, "w") as keyFile:
|
2020-04-17 22:53:07 +02:00
|
|
|
for entry in range(0, 5):
|
|
|
|
keyFile.write(hex(key[entry])+"\n")
|
|
|
|
keyFile.write(key[ID]+"\n")
|
2020-04-15 23:05:03 +02:00
|
|
|
|
2020-04-17 15:32:10 +02:00
|
|
|
def printKey(key):
|
2020-04-17 19:35:20 +02:00
|
|
|
n = key[N]
|
|
|
|
e = key[E]
|
|
|
|
d = key[D]
|
2020-04-17 15:32:10 +02:00
|
|
|
print("----------------------------------------------"+
|
2020-04-18 01:27:16 +02:00
|
|
|
"\nID: {}".format(key[ID]) +
|
2020-04-17 15:32:10 +02:00
|
|
|
"\n{}-BIT KEY".format(n.bit_length())+
|
|
|
|
"\nPUBLIC PART:"+
|
|
|
|
"\n{0}/{1}".format(hex(n), hex(e))+
|
|
|
|
"\nPTIVATE PART:"+
|
|
|
|
"\n{0}".format(hex(d))+
|
|
|
|
"\n----------------------------------------------",
|
|
|
|
)
|
|
|
|
|
2020-04-15 23:05:03 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|