Il ne manque plus que à envoyer des messages

This commit is contained in:
Fomys 2018-11-27 15:33:51 +01:00
parent 3f1940df00
commit 93aace85cb
5 changed files with 387 additions and 44 deletions

View File

@ -141,7 +141,7 @@ Communication entre un client et un noeud
###### En-tête ###### ###### En-tête ######
EICP2P2 V1 EICP2P2 V1
type: register type: register_client
###### Contenu ###### ###### Contenu ######
@ -160,11 +160,11 @@ Cette requête est envoyée par un client pour s'enregistrer sur le réseau. Le
###### Contenu ###### ###### Contenu ######
id_noeud::id_client id_noeud{%=&%&=%}id_client
###### Utilisation ###### ###### Utilisation ######
Réponse à un `register`. Le noeud communique au client son id et l'id du noeud auquel il est connecté. Réponse à un `register_client`. Le noeud communique au client son id et l'id du noeud auquel il est connecté.
### Send ### ### Send ###
@ -191,7 +191,7 @@ Cette requête est envoyé par un client enregistré à un noeud pour envoyer un
###### Contenu ###### ###### Contenu ######
Rien Rien/Erreur client incunnu
###### Utilisation ###### ###### Utilisation ######
@ -217,6 +217,28 @@ Cette requête est envoyée par le destinataire d'une requête `send` après avo
Envoyé par le client pour se déconnecter du réseau. Ne demande aucune réponse de la part du noeud. Envoyé par le client pour se déconnecter du réseau. Ne demande aucune réponse de la part du noeud.
### Get users ###
###### En-tête ######
EICP2P2 V1
type: getUsers
###### Contenu ######
Rien
### Get User ACK ###
###### En-tête ######
EICP2P2 V1
type: getUsersACK
###### Contenu ######
Listedesutilisateurs
### Ping ### ### Ping ###
###### En-tête ###### ###### En-tête ######

View File

@ -18,10 +18,41 @@ try:
except ModuleNotFoundError: # Pycryptodomex except ModuleNotFoundError: # Pycryptodomex
from Cryptodome.PublicKey import RSA as RSA from Cryptodome.PublicKey import RSA as RSA
from Cryptodome.Cipher import PKCS1_OAEP as PKCS1_OAEP from Cryptodome.Cipher import PKCS1_OAEP as PKCS1_OAEP
from Cryptodome.Cipher import AES as AES
pycryptodome = True pycryptodome = True
class RsaGenThread(threading.Thread):
def __init__(self, client_, difficulty=2):
threading.Thread.__init__(self)
self.client = client_
self.difficulty = difficulty
def run(self):
if os.path.isfile("private.pem"):
try:
with open("private.pem", "rb") as keyfile:
rsa = RSA.importKey(keyfile.read())
if not rsa.has_private():
warning("Le fichier clef ne contient pas de clef privée")
raise ValueError
self.client.rsa = rsa
except (IndexError, ValueError):
warning("Fichier clef corrompu")
debug("Suppression du fichier clef corromu")
os.remove("private.pem")
if not os.path.isfile("private.pem"): # We're not using if/else because we may delete the file in the
# previous if statement
debug("Génération de la clef RSA pour %s" % self.client.name)
self.client.rsa = RSA.generate(BUFFER_SIZE + 256 * self.difficulty)
with open("private.pem", "wb") as keyfile:
keyfile.write(self.client.rsa.exportKey())
with open("public.pem", "wb") as keyfile:
keyfile.write(self.client.rsa.publickey().exportKey())
def setup_logging(default_path='log_config.json', default_level=logging.INFO, env_key='LOG_CFG'): def setup_logging(default_path='log_config.json', default_level=logging.INFO, env_key='LOG_CFG'):
"""Setup logging configuration """Setup logging configuration
""" """
@ -58,8 +89,8 @@ NAME = b"client1"
VERSION = b"EICP2P2 V1" VERSION = b"EICP2P2 V1"
REQUEST_TYPE = [ REQUEST_TYPE = [
b'ping', b'pingACK', b'updateAsk', b'updateBack', b'transfer', b'register', b'registerACK', b'send', b'sendACK', b'ping', b'pingACK', b'updateAsk', b'updateBack', b'transfer', b'register_client', b'registerACK', b'send', b'sendACK',
b'exit', b'RSASend', b'init', b'exit', b'RSASend', b'init', b'getUsers', b'getUsersACK',
] ]
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -77,20 +108,38 @@ class MainThread(threading.Thread):
self.rsa = None self.rsa = None
self.aes_key = None self.aes_key = None
self.clientSocket = None self.clientSocket = None
self.users = {}
def run(self): def run(self):
rsa = self.RsaGenThread(self) rsa = RsaGenThread(self)
rsa.start() rsa.start()
rsa.join() rsa.join()
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.connect((HOST, PORT)) self.clientSocket.connect((HOST, PORT))
self.initialisation() self.initialisation()
self.register()
self.get_users()
print(self.users)
while True:
try:
type = bytes(input("Type: "), "ascii")
contenu = bytes(input("Contenu: "), "ascii")
self.send_aes(self.gen_header(type_=type)+contenu)
print(self.receive_aes())
except Exception as e:
print(e)
def register(self):
self.send_aes(self.gen_header(type_=b"register_client"))
self.id_noeud, self.id_client = self.receive_aes()[BUFFER_SIZE:].split(b"{%=&%&=%}")
def initialisation(self): def initialisation(self):
header = self.gen_header(b"RSASend", from_=b"client") header = self.gen_header(b"RSASend", from_=b"client")
content = self.rsa.publickey().exportKey() content = self.rsa.publickey().exportKey()
self.send(header + content) self.send(header + content)
print("oki1")
data = self.receive_rsa() data = self.receive_rsa()
print("oki2")
header = self.extract_header(data[:BUFFER_SIZE]) header = self.extract_header(data[:BUFFER_SIZE])
if header[b"type"] != b"init": if header[b"type"] != b"init":
debug("Incorrect request type, end of connection") debug("Incorrect request type, end of connection")
@ -139,34 +188,60 @@ class MainThread(threading.Thread):
header += b"\nfrom: " + from_ header += b"\nfrom: " + from_
return header.ljust(BUFFER_SIZE, b';') return header.ljust(BUFFER_SIZE, b';')
class RsaGenThread(threading.Thread): def get_users(self):
self.send_aes(self.gen_header(type_=b'getUsers'))
back = self.receive_aes()
print(back)
header = self.extract_header(back)
contenu = back[BUFFER_SIZE:]
if header[b"type"] == b"getUsersACK":
self.users = [(contenu.split(b"%!!%")[i], i) for i in range(len(contenu.split(b"%!!%")))]
def __init__(self, client_, difficulty=2): def send_aes(self, to_send, key=None):
threading.Thread.__init__(self) """Send message with aes encryption
self.client = client_
self.difficulty = difficulty
def run(self): :param to_send: Message to send
if os.path.isfile("private.pem"): :type to_send: bytes
try: :param key: key to replace self.aes_key
with open("private.pem", "rb") as keyfile: :type key: bytes
rsa = RSA.importKey(keyfile.read())
if not rsa.has_private(): :rtype: NoneType
warning("Le fichier clef ne contient pas de clef privée") :return: Nothing"""
raise ValueError debug(b"Send with AES encryption: " + to_send)
self.client.rsa = rsa if key is None:
except (IndexError, ValueError): key = self.aes_key
warning("Fichier clef corrompu") if key is None:
debug("Suppression du fichier clef corromu") info("AES key not generated, connection failure.")
os.remove("private.pem") self.client.send(b"Error")
if not os.path.isfile("private.pem"): # We're not using if/else because we may delete the file in the return
# previous if statement # Get RSA key
debug("Génération de la clef RSA pour %s" % self.client.name) aes_object = AES.new(key, AES.MODE_ECB)
self.client.rsa = RSA.generate(BUFFER_SIZE + 256 * self.difficulty) encrypted = b""
with open("private.pem", "wb") as keyfile: for to_send_text in [to_send[i:i + 32] for i in range(0, len(to_send), 32)]:
keyfile.write(self.client.rsa.exportKey()) encrypted += aes_object.encrypt(to_send_text.ljust(32, b"\x00"))
with open("public.pem", "wb") as keyfile: self.send(encrypted)
keyfile.write(self.client.rsa.publickey().exportKey()) return None
def receive_aes(self, key=None):
"""Receive message with aes encryption
:param key: key to replace self.aes_key
:type key: bytes
"""
to_decrypt = self.receive()
debug(b"Received in aes "+to_decrypt)
if key is None:
key = self.aes_key
if key is None:
info("AES key not generated, connection failure.")
self.client.send(b"Error")
return
aes_object = AES.new(key, AES.MODE_ECB)
decrypted = b""
for block in [to_decrypt[i:i + 32] for i in range(0, len(to_decrypt), 32)]:
print(len(block))
decrypted += aes_object.decrypt(block)
return decrypted.rstrip(b"\x00")
################################################ COMMUNICATION WITH RSA ############################################ ################################################ COMMUNICATION WITH RSA ############################################
@ -180,7 +255,8 @@ class MainThread(threading.Thread):
cipher_rsa = PKCS1_OAEP.new(recipient_key) cipher_rsa = PKCS1_OAEP.new(recipient_key)
raw = self.receive() raw = self.receive()
content = b"" content = b""
for to_decrypt in [raw[i:i + self.rsa.publickey().size_in_bytes()] for i in range(0, len(raw), self.rsa.publickey().size_in_bytes())]: for to_decrypt in [raw[i:i + self.rsa.publickey().size_in_bytes()] for i in
range(0, len(raw), self.rsa.publickey().size_in_bytes())]:
content += cipher_rsa.decrypt(to_decrypt) content += cipher_rsa.decrypt(to_decrypt)
return content return content

View File

@ -13,6 +13,7 @@ try:
from Crypto.PublicKey import RSA as RSA from Crypto.PublicKey import RSA as RSA
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from Crypto.Cipher import PKCS1_OAEP as PKCS1_OAEP from Crypto.Cipher import PKCS1_OAEP as PKCS1_OAEP
from Crypto.Cipher import AES as AES
# noinspection PyUnresolvedReferences,PyProtectedMember # noinspection PyUnresolvedReferences,PyProtectedMember
from Crypto.Random._UserFriendlyRNG import get_random_bytes as get_random_bytes from Crypto.Random._UserFriendlyRNG import get_random_bytes as get_random_bytes
@ -20,6 +21,7 @@ try:
except ModuleNotFoundError: # Pycryptodomex except ModuleNotFoundError: # Pycryptodomex
from Cryptodome.PublicKey import RSA as RSA from Cryptodome.PublicKey import RSA as RSA
from Cryptodome.Cipher import PKCS1_OAEP as PKCS1_OAEP from Cryptodome.Cipher import PKCS1_OAEP as PKCS1_OAEP
from Cryptodome.Cipher import AES as AES
from Cryptodome.Random import get_random_bytes as get_random_bytes from Cryptodome.Random import get_random_bytes as get_random_bytes
pycryptodome = True pycryptodome = True
@ -61,8 +63,8 @@ END_MESSAGE = bytes("fin".ljust(BUFFER_SIZE, ";"), "ascii")
VERSION = b"EICP2P2 V1" VERSION = b"EICP2P2 V1"
REQUEST_TYPE = [ REQUEST_TYPE = [
b'ping', b'pingACK', b'updateAsk', b'updateBack', b'transfer', b'register', b'registerACK', b'send', b'sendACK', b'ping', b'pingACK', b'updateAsk', b'updateBack', b'transfer', b'register_client', b'registerACK', b'send', b'sendACK',
b'exit', b'RSASend', b'init', b'exit', b'RSASend', b'init', b'getUsers', b'getUsersACK',
] ]
DONE = 0 DONE = 0
@ -77,22 +79,99 @@ main_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
main_socket.bind((HOST, PORT)) main_socket.bind((HOST, PORT))
class ServerThread(threading.Thread):
"""Main server thread"""
#### Threads #### #### Threads ####
class RsaGenThread(threading.Thread):
def __init__(self, difficulty=2):
threading.Thread.__init__(self)
self.difficulty = difficulty
def run(self):
rsa = None
if os.path.isfile("private.pem"):
try:
with open("private.pem", "rb") as keyfile:
rsa = RSA.importKey(keyfile.read())
if not rsa.has_private():
warning("Le fichier clef ne contient pas de clef privée")
raise ValueError
except (IndexError, ValueError):
warning("Fichier clef corrompu")
debug("Suppression du fichier clef corromu")
os.remove("private.pem")
if not os.path.isfile("private.pem"): # We're not using if/else because we may delete the file in the
# previous if statement
rsa = RSA.generate(BUFFER_SIZE + 256 * self.difficulty)
with open("private.pem", "wb") as keyfile:
keyfile.write(rsa.exportKey())
with open("public.pem", "wb") as keyfile:
keyfile.write(rsa.publickey().exportKey())
return rsa
class ServerThread(threading.Thread):
"""Main tread for server"""
def __init__(self, socket, ip, port):
threading.Thread.__init__(self) # initialisation du thread
self.socket = socket
self.ip = ip
self.port = port
t = RsaGenThread()
self.rsa_key = None
if os.path.isfile("private.pem"):
try:
with open("private.pem", "rb") as keyfile:
self.rsa_key = RSA.importKey(keyfile.read())
if not self.rsa_key.has_private():
warning("Le fichier clef ne contient pas de clef privée")
raise ValueError
except (IndexError, ValueError):
warning("Fichier clef corrompu")
debug("Suppression du fichier clef corromu")
os.remove("private.pem")
if not os.path.isfile("private.pem"): # We're not using if/else because we may delete the file in the
# previous if statement
debug("Generate new rsa key")
self.rsa_key = RSA.generate(BUFFER_SIZE + 256 * self.difficulty)
with open("private.pem", "wb") as keyfile:
keyfile.write(self.rsa_key.exportKey())
with open("public.pem", "wb") as keyfile:
keyfile.write(self.rsa_key.publickey().exportKey())
debug("RSA key loaded")
self.clients = {}
self.nodes = {
self.rsa_key.publickey().exportKey(): None,
}
def register_client(self, rsa_client, client_thread):
self.clients.update({rsa_client: (self.rsa_key, client_thread)})
return self.rsa_key.publickey().exportKey(), rsa_client
def send_to(self, id_dest, to_send):
if id_dest not in self.clients.keys():
return b"Erreur client inconnu"
if self.clients[id_dest][0] is None:
return self.clients[id_dest].send_to_me(to_send)
else:
return self.nodes[self.clients[id_dest]].transfer(id_dest, to_send)
class ClientThread(threading.Thread): class ClientThread(threading.Thread):
"""Main thread, for each client""" """Main thread, for each client"""
def __init__(self, clientsocket, ip_client, port): def __init__(self, clientsocket, ip_client, port, server):
"""Create ClientThread object """Create ClientThread object
:param clientsocket: Client's socket :param clientsocket: Client's socket
:param ip_client: Client's ip address :param ip_client: Client's ip address
:param port: Client's connection PORT :param port: Client's connection PORT
:param server: Server thread
:type clientsocket: socket.socket :type clientsocket: socket.socket
:type ip_client: str :type ip_client: str
:type port: int :type port: int
:type server: ServerThread
:return: Nothing :return: Nothing
:rtype: NoneType""" :rtype: NoneType"""
@ -104,8 +183,9 @@ class ClientThread(threading.Thread):
self.running = True self.running = True
self.status = None self.status = None
self.rsa_client = None self.rsa_client = None
self.aes_key = get_random_bytes(64) self.aes_key = get_random_bytes(32)
self.type = T_NONE self.type = T_NONE
self.server = server
debug("Creation du thread pour %s reussie" % ip_client) debug("Creation du thread pour %s reussie" % ip_client)
def initialize(self): def initialize(self):
@ -181,7 +261,53 @@ class ClientThread(threading.Thread):
header += b"\nfrom: " + from_, header += b"\nfrom: " + from_,
return header.ljust(BUFFER_SIZE, b';') return header.ljust(BUFFER_SIZE, b';')
################################################ COMMUNICATION WITH RSA ################################################ ################################################ COMMUNICATION WITH AES ############################################
def send_aes(self, to_send, key=None):
"""Send message with aes encryption
:param to_send: Message to send
:type to_send: bytes
:param key: key to replace self.aes_key
:type key: bytes
:rtype: NoneType
:return: Nothing"""
debug(b"Send with AES encryption: " + to_send + bytes(str(self.ip), "ascii"))
if key is None:
key = self.aes_key
if key is None:
info("AES key not generated, connection failure.")
self.client.send(b"Error")
return
# Get RSA key
aes_object = AES.new(key, AES.MODE_ECB)
encrypted = b""
for to_send_text in [to_send[i:i + 32] for i in range(0, len(to_send), 32)]:
encrypted += aes_object.encrypt(to_send_text.ljust(32, b"\x00"))
self.send(encrypted)
return None
def receive_aes(self, key=None):
"""Receive message with aes encryption
:param key: key to replace self.aes_key
:type key: bytes
"""
to_decrypt = self.receive()
if key is None:
key = self.aes_key
if key is None:
info("AES key not generated, connection failure.")
self.client.send(b"Error")
return
aes_object = AES.new(key, AES.MODE_ECB)
decrypted = b""
for block in [to_decrypt[i:i + 32] for i in range(0, len(to_decrypt), 32)]:
decrypted += aes_object.decrypt(block)
return decrypted.rstrip(b"\x00")
################################################ COMMUNICATION WITH RSA ############################################
def send_rsa(self, to_send, key=None): def send_rsa(self, to_send, key=None):
"""Send message with rsa encryption """Send message with rsa encryption
@ -210,7 +336,7 @@ class ClientThread(threading.Thread):
self.send(encrypted) self.send(encrypted)
return None return None
############################################ COMMUNICATION WITHOUT CRYPTING ############################################ ############################################ COMMUNICATION WITHOUT CRYPTING ########################################
def receive(self): def receive(self):
"""Receive message from connection """Receive message from connection
@ -253,6 +379,42 @@ class ClientThread(threading.Thread):
self.client.send(END_MESSAGE) self.client.send(END_MESSAGE)
return None return None
def send_users(self):
print(self.server.clients.keys())
self.send_aes(self.gen_header(type_=b"getUsersACK")+b"%!!%".join(list(self.server.clients.keys())))
def register_client(self):
"""Register client
:rtype: NoneType
:return: Nothing"""
self.server.register_client(self.rsa_client, self)
id_noeud, id_client = self.server.rsa_key.publickey().exportKey(), self.rsa_client
self.send_aes(self.gen_header(type_=b"registerACK") + id_noeud + b"{%=&%&=%}" + id_client)
def send_to_me(self, to_send):
"""Receive message from other poeple
:param to_send: Message to send to client
:type to_send: bytes
:rtype: NoneType
:return: Nothing"""
self.send(self.gen_header(type_=b"send", to_=self.rsa_client) + to_send)
return
def send_to_other(self, id_dest, to_send):
"""Send message to other client
:param id_dest: id of receiver
:param to_send: Message to send
:return: Nothing
:rtype: NoneType"""
server_response = self.server.send_to(id_dest, to_send)
self.send(self.gen_header(type_=b"sendACK"), server_response)
return
def run(self): # main de la connection du client def run(self): # main de la connection du client
"""Run thread mainloop """Run thread mainloop
@ -261,14 +423,25 @@ class ClientThread(threading.Thread):
info(self.ip + "connected, initialize connection...") info(self.ip + "connected, initialize connection...")
self.initialize() self.initialize()
info(self.ip + "connection initialized.") info(self.ip + "connection initialized.")
while self.running:
data = self.receive_aes()
header = self.extract_header(data)
print(header)
print(data)
print(self.rsa_client)
if header[b"type"] == b"register_client":
self.register_client()
elif header[b"type"] == b"getUsers":
self.send_users()
self.client.close() self.client.close()
if __name__ == "__main__": if __name__ == "__main__":
clients = [] clients = []
server = ServerThread(main_socket, ip=HOST, port=PORT)
while True: while True:
main_socket.listen(1) # Waiting for incoming connections main_socket.listen(1) # Waiting for incoming connections
client_socket, (ip, PORT) = main_socket.accept() client_socket, (ip, PORT) = main_socket.accept()
newClient = ClientThread(client_socket, ip, PORT) newClient = ClientThread(client_socket, ip, PORT, server)
newClient.start() newClient.start()
clients.append(newClient) clients.append(newClient)

57
server/private.pem Normal file
View File

@ -0,0 +1,57 @@
-----BEGIN RSA PRIVATE KEY-----
MIIKRwIBAAKCAkEAlmEl3vGDG4qxqPvO98zCpDbrFrNeTbwThiqCnuu0Cp5h/ejN
eSChnRkmyOffMtvmBCmCJNw0vR9/Cpatzx9TXekXUWRjXbUfR1ECQ2Fo0xqS3GE5
8F8HYnhra9+i3xJXSJRpk7P1FaKGakie0GGoMCcImGZyWuSjNbUa4hj+I+Nlgf3r
wQfll/06Wy7YLWXCXnNCTnirVYJooPphy3QqtNj+clwCvoY8F7Tz5PDfffiDPjxb
+uOWwtfi0LecL5YqZC8aed/70WcujItkGQ2YhReYV+jsXvj1DTA0VB/RdrzRYeTN
qYSoS7t8+cmWM27syEPb+11JLQZB4kPUB4yYFfk/a1ffkmEZYtqRdbndM20Loksr
3C/jHRrmqfCjRVmL2wHK64wUUbGLKcSMLHkAUR1HaeZ+PJ2qXX7ed4MBDRQDaMM7
JGrkwz3hyBq2fblUT+OarHnlwRDvLL+XlACrNJ7yvnigwCkx7MVwakhr0yWc0o9Q
HEmA2DnibE4JCrNfyAwV+hKs9p+F0XjbbutOUSXFxKdImKzf4NG1eNtMKFeIKZgt
zm/JVhvlSC4p6ko+szBd89A11K1oHKRVNC64rnNH89o2qdT495HRfnFoEHgxA0nQ
H85QlCajOT0R122ha5ksJxXgj+HzTXiKa4LtTXy5kWWkYGMGxK6Xmxn32QPZHCgZ
A1L99uvZjr1hgkqxrI8p1wBPABN3vdr1CLwq4kp0g+BOsFAb7X9rWn6xW9wTY8Qm
FW9Ze+K3Fn7aYg41AgMBAAECggJAMZURgAtAj82wm0d/a8vrJ2rmI4N7pgTLqTKO
A+kyYUfshmQCj5wXPW2kEJ0HRkUj955aB15F00+1ux+IamDpQ28N3avMaYLFL+YB
aYcOIYDDCH/UMzGaBTkwLg68LHHnLoqkrusodXwzdorANnPkmQ/dsSYiMV7ug1BV
71OPQ/T/rJc06t34MR9w5MyywXEqX6sAI1B9KYIIjn9PzF3ekOd1Ke61hu1KlgR3
YDadcfMFDvmm2De/Jzo4Ulof2L7Z9lNjluinplkvi26QUARgFA3+EIo/mWbboYAR
2OTu0wgZv33J48lWFjZY1CFH8WhzEOry9tPAw2QrR/fWSrDsGl6mrvV3xVNXo/Tt
/hOM4B5lthqz+PMfHqMWh9TMeJq31UomAyjTA601mpdrYdfSbfQXs4ISb2JUWao6
Zz5VlWt4QsibZ1fB1SacQ7ZwGtHnTFV4yM2wDWlfkal3BiX3wsbdXrz4cfQ6o5FC
qQDuT3Ba21XT1H+d6n4Ulct4gpwYSa+mj7Z5GjNjXSBFlyWj8+BYP9AcrD8SxpgY
orTM5+oGNrY1uwSm1IuryYk5bs3nuppFcRwKnJKGnR/yBocUu8UMx3Gn3vbaCrgD
IsoHt0MjVKYbc3vhEIb/a2uP/kxZVu3MqjbUh5v2SgSHNFTDtNcZRdbZLn8w00p8
QJm9kzmIsryuagrMfu2WI4KCxhW2VgHtEsF0+CsyZAv9Bq1/BMwph7d2CvfEBn2e
AWZVFlEUFwf2uaYIUQt6fruvazmHAoIBIQC+eXhlhrvDRJ15Qce8YCoWxmk0doKI
BzJM4srMNM0P7A1ucWf2sLe2RsFolYzVXEsyZuyZy3jzRvQ/9+/2SwJHckQ0VPQc
3WQXUiNBUBzys5+Wwz33CMnvXRmolr+rNOJcU4cFiIRH3UkEyKJE/YO9AJ7xCm2t
zg1uvp83gNL+Xg09f18h7WfKKVqJg5HvDEbbACI79zbf5fVNCawUOYIIeFX0yb66
Io0DgnhK6wvFvCpEd6ReuPBc5sa0cKpV6Rce+tz0e5lP1kH36wxY8amB3Mza1W/i
YkDyuRggOtjK9Pf0xx9KKYdw+8Tt2zVPyotkcxGPKRIMOc9fDW11SWZQQNzOqExU
h0bi8yO6Owww7FlsKjF1k95yShKJYCJ0PAcCggEhAMocoLkhsDIoLQhpJ24NC69g
oNVLVCUspaovEe2O3fmsIcMT50KcqBi9+4fMV8IJQ0IlHC1wRTuqqAwoItTXMcU8
deQb2mF6G5DMEFPYBtdo6qqyTqUvb7ZvVsc74fbAeVwbaI9jUfHBNL5JWH0cdk82
81lEt9h0+p+UxPxkRQQZkpugMpuuiAktmilVGUsL8WfLS3DXehOlM7SCSIRqM41V
sbasP9go7dbVEWLOFVXtBnUpbJ3UCbuBPB1cYNl2F0iSKLGzCTsZfW6QdSKvl9sc
fZfZGDoE+YP9KyOIv05sMFrhRdhCIN2F6sh1DoHPz5xnwws67fW85d9Da4yqYFDp
TJkfjzjzOjmacvZ9cR2GANLtgsD8Jk+3XjS/6luM4wKCASADpz7I67mkOCexELLt
CQcloq6X5AIC6+hqExuCqSyZtpRXXvN4htEvPNIu2Fd4LCoBEHpPRNjQCbdblrzS
g4PKrQVj35FDEHf//oJQmWHEwe84rAv/NfrxjV6VnjaU6RHCZZO8Zm7rFfJ1lgeO
vTIOqPljdCjAYaheTGevX9gsKs+kM1I2y1/nP73Nz2k4JYsiXjFJ5yb1SmrXDG1x
gdzdnrk/VbNDzK/ZtmHqRfHW3BIPeB/99RwfHAoJofOSbEMWRAeC5O/+2QQw5iA5
gIEYzYxcUiWwOOQ4A5bT320TD3nGBx+xg3RmlV2klkTCgAcGaTKYBVikRWOV1iMY
Uc0VkvjpOc8k++c/KRyxULqeIZphebmzIJ/unz8VxXNDuXcCggEgGwo5dotLR2qY
m8d1vwN/zLbgzaiGWXLftyUVAPNcP0sIOVEwX3kWBDlmrYRpOUyD8xWD75BVsuXi
Fb5I6rDft8OfAiW30dLEXVrv1EfSon8m5i901iTCjWXX9k/Mq3aUxf3kv0pKbE2t
AMhPTm3AZliztf27mvoLjc4ZjKrdkO7a7ndAThPLn266cU0xVRkD8x7/uNkbPAWe
JUhI625J916/p/DjmcfgCh33dYCc2iIb+xDPt03m/28K2+LAElf6RXtIdn29fmqq
nWA2S62yY14sb2RcGWcQ+tekLdzdu2A+F+qLraMrQnfoMWl3x2ZGIGbBcX8w1u+u
0W/zs54XxLtxxAq03qmr4IkJWHDv8UM8WPvtwmP5vDCMKgKDGDyBAoIBIHrq2mon
flzxjYC8QQv3h6hxuO0B8HRiHmZMo2bEyXiMmke1afGwJTgwVJp6QM/OvwQOo+4l
PutQ/S9rbWOfYzo03GH5GZNnVLNd5/RfJ9M0/RRQeV4mw9RKufTauwCFxMD+1SeO
DIWbhZV1FTYk886OHxEtPgHrLbodv3wFiiJrjwdlAs9lMXWZFPRntCrv+hrukhe3
OXUSYIoDaQzy7oFbISEpedFPtcIxebUAuHNf3se4D6ijJLrdOGDgPi2ABnMNUG5b
cCzNNMlII3LzgVN1MN5DvY1/L1w59eTQCOdTAHsaEHYRbjBuN3Stf8WGtoc9+RVd
qdXbfSvXXhZzR+vK+rL/m/Egp4P001jhvZd1+PLbbihLe2IKtlwtdiwZCA==
-----END RSA PRIVATE KEY-----

15
server/public.pem Normal file
View File

@ -0,0 +1,15 @@
-----BEGIN PUBLIC KEY-----
MIICYjANBgkqhkiG9w0BAQEFAAOCAk8AMIICSgKCAkEAlmEl3vGDG4qxqPvO98zC
pDbrFrNeTbwThiqCnuu0Cp5h/ejNeSChnRkmyOffMtvmBCmCJNw0vR9/Cpatzx9T
XekXUWRjXbUfR1ECQ2Fo0xqS3GE58F8HYnhra9+i3xJXSJRpk7P1FaKGakie0GGo
MCcImGZyWuSjNbUa4hj+I+Nlgf3rwQfll/06Wy7YLWXCXnNCTnirVYJooPphy3Qq
tNj+clwCvoY8F7Tz5PDfffiDPjxb+uOWwtfi0LecL5YqZC8aed/70WcujItkGQ2Y
hReYV+jsXvj1DTA0VB/RdrzRYeTNqYSoS7t8+cmWM27syEPb+11JLQZB4kPUB4yY
Ffk/a1ffkmEZYtqRdbndM20Loksr3C/jHRrmqfCjRVmL2wHK64wUUbGLKcSMLHkA
UR1HaeZ+PJ2qXX7ed4MBDRQDaMM7JGrkwz3hyBq2fblUT+OarHnlwRDvLL+XlACr
NJ7yvnigwCkx7MVwakhr0yWc0o9QHEmA2DnibE4JCrNfyAwV+hKs9p+F0XjbbutO
USXFxKdImKzf4NG1eNtMKFeIKZgtzm/JVhvlSC4p6ko+szBd89A11K1oHKRVNC64
rnNH89o2qdT495HRfnFoEHgxA0nQH85QlCajOT0R122ha5ksJxXgj+HzTXiKa4Lt
TXy5kWWkYGMGxK6Xmxn32QPZHCgZA1L99uvZjr1hgkqxrI8p1wBPABN3vdr1CLwq
4kp0g+BOsFAb7X9rWn6xW9wTY8QmFW9Ze+K3Fn7aYg41AgMBAAE=
-----END PUBLIC KEY-----