├── .gitignore ├── .idea ├── .gitignore ├── identity_authentication_system.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── client-private.pem ├── client-public.pem ├── client.py ├── key_generator.py ├── picture ├── client.png ├── sever.png └── 教材配图.png ├── server-private.pem ├── server-public.pem └── sever.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/identity_authentication_system.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # identity_authentication_system 2 | 3 | ## 网络安全实践作业,实现一个简单的身份认证系统 4 | 5 | ### 一、原理简介 6 | 7 | 本例程参考教材《计算机网络安全》(马利,姚永雷著)第86页,实现了一个基于公钥密码的双向身份认证系统 8 | 9 | ![教材配图](/picture/教材配图.png) 10 | 11 | 1. A用B的公钥对含有其标识IDA和挑战(N1)的消息加密,并发送给B。其中N1用来唯一标识本次交易。 12 | 2. B发送一条用PUA加密的消息,该消息包含A 的挑战(N1)和B产生的新挑战(N2)。因为只有B可以解密消息(1),所以消息(2)中的N1可使A确信其通信伙伴是B。 13 | 3. A用B的公钥对Nz加密﹐并返回给B,这样可使B确信其通信伙伴是A。至此,A与B实现了双向认证。 14 | 4. A选择密钥K,,并将M=E(PU, ,E(PR,,K,))发送给B。使用B的公钥对消息加密可以保证只有B才能对它解密;使用A的私钥加密可以保证只有A才能发送该消息。 15 | 5. B计算D(PUA,D(PRB,M))得到密钥。 16 | 步骤(4)、(5)实现了对称密码的密钥分配。 17 | 18 | ### 二、例程说明 19 | 20 | 1. 本例程基于python3.8实现 21 | 22 | 2. 本例程中A为客户端,B为服务端,双方通信采用TCP方式进行,TCP通信使用python自带的socket库实现,socket编程参考了https://www.jmjc.tech/tutorial/python/52 23 | 24 | 3. 公钥密码采用RSA体制,使用了python密码库pycrypto,初次使用需要安装,安装命令为`pip install pycrypto`,编程过程中参考了https://www.cnblogs.com/lanston1/p/11875706.html 25 | 26 | 需要注意,pycrypto库对于高版本的python支持有些许问题,编程过程中遇到异常`AttributeError module 'time' has no attribute 'clock'`,查阅资料发现,Python3.8不再支持`time.clock`,但在调用时依然包含该方法。解决方法是使用`time.perf_counter()`替换报错的`time.clock()` 27 | 28 | 4. 例程中消息的识别使用了正则表达式匹配,再书写正则表达式的过程中,发现了两个有趣的网站 29 | 30 | https://regex101.com/r/1Z02qx/1/ 31 | 32 | https://jex.im/regulex/#!flags=&re=%5BA-Z%5D%7C%5Cd%2B%5C.%5Cd%2B 33 | 34 | 有助于理解正则表达式 35 | 36 | 5. 由于pycrypto加密长度限制,原理介绍中的(4)和(5)无法正常运行,已被注释,但是身份认证功能已经实现。 37 | 38 | ### 三、例程运行 39 | 40 | #### 运行前检查 41 | 42 | 1. 安装pycrypto 43 | 44 | `pip install pycrypto` 45 | 46 | #### 运行方式 47 | 48 | 1. 运行`key_generator.py`生成A和B的公私钥密钥对,==注意:使用高版本python运行可能报错,解决方法请查看 二、例程说明== 49 | 2. 运行`sever.py`开启服务端(B) 50 | 3. 运行`client.py`开启客户端(A) 51 | 52 | #### 运行效果截图 53 | 54 | 1. 服务端(B) 55 | 56 | ![sever](/picture/sever.png) 57 | 58 | 2. 客户端(A) 59 | 60 | ![client](/picture/client.png) 61 | 62 | -------------------------------------------------------------------------------- /client-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQChb6tP6gvhPWK2UNlWvPWjtLgEIM3Rgch81BiBL2181gT3P68S 3 | 8RAaxrRNQpPgy1l7XlHowMPb/7dBkxI3XHaQFqpYhXPOjiR3XpDgbGRoYNPD36fv 4 | RjHRyAu5Gistlu65cqwrW3WAu4rDmMP72pfR/qkDWVAT/Vg4cBVCrqKdCwIDAQAB 5 | AoGBAJTYCgKnj4Z/+6iPSKRRmsXylQuxVzMIz1pQj7tkiDVmerOgswHq42dct9bY 6 | QNIti3ZU036fZLZNwqAiQtRqWyEsrbgp3dj6VWrZD0R4m0iyA4tdkgVh/GPCtBmk 7 | 2De+r8zKUyv8cXITbTpGYIHEdNFdg+WpHcRKUNSWPtpv4A/BAkEAuzGHSGbL4E/s 8 | dQB4KHzH/jMdMkVqqw68wz2hImtkqQYRVc0/FXRa89UECdCBKJ8whhUj6wX1iWpS 9 | hyErnLwp6wJBANzGcSlMqrmqcTPKMoCq/6hvFbcgpHUIlCKRctpPKMnQfjYym7pO 10 | r0bxptFogOxZkPNsMQZIMrH7q/+wk3sCcWECQCKByc9l67VI5mzXDk9gWTmZ0IsA 11 | 0t3/lHplNPv0UhT6Yl2vvS5GSbHjFaB9iCGl3rsI0Js1eQLAz7/LNI/q+8sCQQCf 12 | J5t6Z1+IGzwiVrhTk3JcbUBdVPXZvMZ1qR8ADCLeYiQqVA95nrGy1hApVEnkmKjr 13 | IuqwmamaL1EOMbAP9HHhAkAIdosGkVD7qx5qyWaU4Vm07GsaxNG1b0yn/xz4mfow 14 | PyHbL81HHScwOWfDuS9LsXothocWVbA/auAKYcRe7g2l 15 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /client-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChb6tP6gvhPWK2UNlWvPWjtLgE 3 | IM3Rgch81BiBL2181gT3P68S8RAaxrRNQpPgy1l7XlHowMPb/7dBkxI3XHaQFqpY 4 | hXPOjiR3XpDgbGRoYNPD36fvRjHRyAu5Gistlu65cqwrW3WAu4rDmMP72pfR/qkD 5 | WVAT/Vg4cBVCrqKdCwIDAQAB 6 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from Crypto import Random 3 | 4 | from Crypto.Hash import SHA 5 | from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 6 | from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5 7 | from Crypto.PublicKey import RSA 8 | import base64 9 | import time 10 | import re 11 | 12 | def encrypt(puk, message): 13 | with open(puk) as f: 14 | key = f.read() 15 | rsakey = RSA.importKey(key) 16 | cipher = Cipher_pkcs1_v1_5.new(rsakey) 17 | cipher_text = base64.b64encode(cipher.encrypt(message.encode('utf-8'))) 18 | # print("Encrypt Message: " + cipher_text.decode('utf-8')) 19 | return cipher_text 20 | 21 | 22 | def decrypt(prk, message): 23 | with open(prk) as f: 24 | key = f.read() 25 | rsakey = RSA.importKey(key) 26 | cipher = Cipher_pkcs1_v1_5.new(rsakey) 27 | random_generator = Random.new().read 28 | text = cipher.decrypt(base64.b64decode(message), random_generator) 29 | # print(text.decode('utf-8')) 30 | return text 31 | 32 | 33 | def re_decode(data): 34 | pattern = re.compile(r"[A-Z]") 35 | id = re.findall(pattern, data) 36 | pattern = re.compile(r"\d+\.\d+") 37 | n = re.findall(pattern, data) 38 | return id, n 39 | 40 | # socket.AF_INET (IPV4) 41 | # socket.SOCK_STREAM (TCP) 42 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 43 | # 连接服务端 44 | s.connect(('127.0.0.1', 9999)) 45 | # 发送标识A和挑战n1 46 | n1 =time.time() 47 | message = f"A,{n1}" 48 | cipher_text = encrypt("server-public.pem", message) 49 | s.sendall(cipher_text) 50 | print(f"Send n1: {n1}") 51 | # 接收B发来的挑战n1和n2 52 | raw_data = s.recv(1024) 53 | decrypted_data = decrypt("client-private.pem", raw_data) 54 | id ,n= re_decode(decrypted_data.decode()) 55 | print(f"Received n: {n}") 56 | # 验证A发回来的n1 57 | if n[0] == str(n1): 58 | print("n1 verified!") 59 | print("identity of B verified!") 60 | pass 61 | else: 62 | print("n1 verify error") 63 | exit(0) 64 | # 向B发送收到的n2 65 | message = f"{n[1]}" 66 | cipher_text = encrypt("server-public.pem", message) 67 | s.sendall(cipher_text) 68 | print(f"Send back n2: {n[1]}") 69 | 70 | # 向B发送key 71 | key = "1321231" 72 | # cipher_text = encrypt("client-private.pem", key) # 先用A的私钥加密 73 | # cipher_text = encrypt("server-public.pem", cipher_text.decode()) # 再用B的公钥加密 74 | s.sendall(key.encode()) 75 | print("Send key") 76 | 77 | 78 | 79 | # 关闭 socket 80 | s.close() 81 | -------------------------------------------------------------------------------- /key_generator.py: -------------------------------------------------------------------------------- 1 | from Crypto import Random 2 | from Crypto.Hash import SHA 3 | from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 4 | from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5 5 | from Crypto.PublicKey import RSA 6 | 7 | # 伪随机数生成器 8 | random_generator = Random.new().read 9 | # rsa算法生成实例 10 | rsa = RSA.generate(1024, random_generator) 11 | 12 | # Server的秘钥对的生成 13 | private_pem = rsa.exportKey() 14 | with open("server-private.pem", "w") as f: 15 | f.write(private_pem.decode()) 16 | 17 | public_pem = rsa.publickey().exportKey() 18 | with open("server-public.pem", "w") as f: 19 | f.write(public_pem.decode()) 20 | 21 | # 伪随机数生成器 22 | random_generator = Random.new().read 23 | # rsa算法生成实例 24 | rsa = RSA.generate(1024, random_generator) 25 | # Client的秘钥对的生成 26 | private_pem = rsa.exportKey() 27 | with open("client-private.pem", "w") as f: 28 | f.write(private_pem.decode()) 29 | 30 | public_pem = rsa.publickey().exportKey() 31 | with open("client-public.pem", "w") as f: 32 | f.write(public_pem.decode()) -------------------------------------------------------------------------------- /picture/client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superyu-github/identity_authentication_system/5bcab65b4331740eb63134fd0c48a530aa586932/picture/client.png -------------------------------------------------------------------------------- /picture/sever.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superyu-github/identity_authentication_system/5bcab65b4331740eb63134fd0c48a530aa586932/picture/sever.png -------------------------------------------------------------------------------- /picture/教材配图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Superyu-github/identity_authentication_system/5bcab65b4331740eb63134fd0c48a530aa586932/picture/教材配图.png -------------------------------------------------------------------------------- /server-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQC9w1oOUTwbc/Dnu/ZwMwp+YuLFj+yvNpzH1uTrsBmdQe7GKrFj 3 | vw+9fLNx5CVEdRpYtlEVuZPKWp1XXgGnku7NN6WHiaIGLnRvpafqgX0VBsKa4v2i 4 | Zgld0M7wQW0XQ4Qmc0mGx++Tb+3UUNklGne+g4KU6qAMtDq8gACMrk/QiwIDAQAB 5 | AoGANxq+UQAH+1TctP5hHs3Dk1X10jbsHSk3t9RQX5ygo4jc5JDLOFLoKDdJCJ6e 6 | r9VtlCtFOpDMOOu9WvyCcH37d/5R8aPJmiVQOJkjSY2gib+mWcO/OmARFVa99oZp 7 | YUvk+Nr2Cy1GW8GUJeZBIEsDt97U7mgngtjtfrtkfQHcY8kCQQDXqvicwwLgrc0I 8 | xEhax7k3SW4VL0+OAC7bcxNuSSMHi4f+2qolQSnDKrD90pmcqp7mq5+AvLAaU1wJ 9 | HCtgHVolAkEA4UAyz7fNT0cPZGgqAF6oOivSa58v5G6k+Q3TUqMk5iHMUAwmskqk 10 | DmVwsXu6o0qFqeCId4QOX5lxKNeYd2GI7wJAQoeag/lglODopDCwxVhhWZ3MWLqs 11 | hvile8sHDnl/Vd+ER2k7HZz5fhxYVyS1gG/d7vi4jqUmiih29tR6yP6lBQJBAMfj 12 | nrOymmGV39TskwlM9uDYbpzO4UNH5OKXIEcfO+l1aMhIYDasAjY8UutNr2yk/0Sn 13 | WrMk31R66G6kFEsspMUCQGQNPgL508ubdPpQSdDg7oJX7EGVDyS2a5Hld0uUV2Ug 14 | rKrY9A9PGH8u/NmrBmXFWsVYq+NJV4uOm1/UAXf7WG0= 15 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /server-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9w1oOUTwbc/Dnu/ZwMwp+YuLF 3 | j+yvNpzH1uTrsBmdQe7GKrFjvw+9fLNx5CVEdRpYtlEVuZPKWp1XXgGnku7NN6WH 4 | iaIGLnRvpafqgX0VBsKa4v2iZgld0M7wQW0XQ4Qmc0mGx++Tb+3UUNklGne+g4KU 5 | 6qAMtDq8gACMrk/QiwIDAQAB 6 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /sever.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | from Crypto import Random 5 | from Crypto.Hash import SHA 6 | from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 7 | from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5 8 | from Crypto.PublicKey import RSA 9 | import base64 10 | import re 11 | 12 | 13 | def encrypt(puk, message): 14 | with open(puk) as f: 15 | key = f.read() 16 | rsakey = RSA.importKey(key) 17 | cipher = Cipher_pkcs1_v1_5.new(rsakey) 18 | cipher_text = base64.b64encode(cipher.encrypt(message.encode('utf-8'))) 19 | # print("Encrypt Message: " + cipher_text.decode('utf-8')) 20 | return cipher_text 21 | 22 | 23 | def decrypt(prk, message): 24 | with open(prk) as f: 25 | key = f.read() 26 | rsakey = RSA.importKey(key) 27 | cipher = Cipher_pkcs1_v1_5.new(rsakey) 28 | random_generator = Random.new().read 29 | text = cipher.decrypt(base64.b64decode(message), random_generator) 30 | # print(text.decode('utf-8')) 31 | return text 32 | 33 | 34 | def re_decode(data): 35 | pattern = re.compile(r"[A-Z]") 36 | id = re.findall(pattern, data) 37 | pattern = re.compile(r"\d+\.\d+") 38 | n = re.findall(pattern, data) 39 | return id, n 40 | 41 | 42 | # socket.AF_INET (IPV4) 43 | # socket.SOCK_STREAM (TCP) 44 | # 创建socket 45 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 46 | # 绑定监听地址和端口 47 | s.bind(("127.0.0.1", 9999)) 48 | # 设置最大允许连接数量 49 | s.listen(3) 50 | # 死循环,重复的处理着每个客户端的请求 51 | while True: 52 | # 阻塞 每当有客户端的请求过来开始执行 53 | # 连接处理 (已完成三次握手)并获取资源对象 | conn 请求对象 | addr 客户端地址 ip: port 54 | conn, addr = s.accept() 55 | # 接收A发来的标识和挑战n1 56 | raw_data = conn.recv(1024).decode('utf-8') 57 | decrypted_data = decrypt("server-private.pem", raw_data) 58 | id , n = re_decode(decrypted_data.decode()) 59 | print(f"Received! id: {id[0]} n1: {n[0]}") 60 | # 产生n2,和n1一起发回给A 61 | n2 = time.time() 62 | # 检验n1与n2时间差,大于2s视为重放攻击 63 | if n2-float(n[0]) > 2: 64 | print("Replay attack!") 65 | exit() 66 | else: 67 | pass 68 | message = f"{n[0]},{n2}" 69 | cipher_text = encrypt("client-public.pem", message) 70 | conn.sendall(cipher_text) 71 | print(f"Send back: n1: {n[0]} n2: {n2}") 72 | # 接收来自A的n2 73 | raw_data = conn.recv(1024).decode('utf-8') 74 | decrypted_data = decrypt("server-private.pem", raw_data) 75 | id, n = re_decode(decrypted_data.decode()) 76 | print(f"Received! n: {n}") 77 | # 验证n2 78 | if n[0] == str(n2): 79 | print("n2 verified!") 80 | print("identity of A verified!") 81 | pass 82 | else: 83 | print("n2 verify error!") 84 | exit(0) 85 | # 接收来自A的密钥key 86 | raw_data = conn.recv(1024).decode('utf-8') 87 | print("Received key from A") 88 | print(raw_data) 89 | # decrypted_data = decrypt("server-private.pem", raw_data) # 先用B的私钥解密 90 | # decrypted_data = decrypt("client-public.pem", decrypted_data) # 再用A的公钥解密 91 | # print(decrypted_data) 92 | 93 | # 关闭客户端连接 94 | conn.close() 95 | --------------------------------------------------------------------------------