├── .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 |
4 |
5 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 | 
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 | 
57 |
58 | 2. 客户端(A)
59 |
60 | 
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 |
--------------------------------------------------------------------------------