https://pypi.org/project/pycryptodomex/ 安装目录为:Cryptodome https://pypi.org/project/pycryptodome/ 安装目录为:Crypto,同pycrypto模块一样,因此建议安装pycryptodomex

**_python3 -m pip install pycryptodomex_**

一 传统模式

1 ECB MODE

https://www.pycryptodome.org/en/latest/src/cipher/classic.html#ecb-mode-1

  • Electronic Codebook Book, 电码本模式
  • 数据加密前需要pad, 默认为pkcs7, 将数据补全为block_size的整数倍

Python

  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. from Cryptodome.Util import Padding
  5. # key = AES.get_random_bytes(16)
  6. key = ('A' * 16).encode() # 16 bytes, 即为AES-128 (16 * 8 bits)
  7. data = json.dumps(dict(name='zoro', age=18)).encode()
  8. pad_data = Padding.pad(data, AES.block_size)
  9. mode = AES.MODE_ECB # AES-128-ECB
  10. cipher = AES.new(key, mode)
  11. cy_bytes = cipher.encrypt(pad_data)
  12. ct = base64.b64encode(cy_bytes).decode()
  13. print('KEY:', key.decode())
  14. print('ECB base64:', ct)
  15. cipher = AES.new(key, mode)
  16. dec_message = cipher.decrypt(base64.b64decode(ct))
  17. pt = json.loads(Padding.unpad(dec_message, AES.block_size).decode())
  18. print('Parsed:', pt)
  19. '''输出
  20. KEY: AAAAAAAAAAAAAAAA
  21. ECB base64: BkA+I8H+dwwcUCdis5EGYcGnm9eHpqz6zEc3Qa71U5Y=
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

JavaScript

  1. var key = 'AAAAAAAAAAAAAAAA' // 不能直接使用原始的KEY,需要用Utf8解码
  2. var key = CryptoJS.enc.Utf8.parse('AAAAAAAAAAAAAAAA');
  3. var encrypted = 'BkA+I8H+dwwcUCdis5EGYcGnm9eHpqz6zEc3Qa71U5Y=';
  4. var pt = CryptoJS.AES.decrypt(encrypted, key, {
  5. mode: CryptoJS.mode.ECB,
  6. }).toString(CryptoJS.enc.Utf8);
  7. console.log(JSON.parse(pt));
  8. // {name: 'zoro', age: 18}

2 CBC MODE

https://www.pycryptodome.org/en/latest/src/cipher/classic.html#cbc-mode-1

  • Cipher Block Chaining, 密码分组链接模式
  • 数据加密前需要pad
  • 数据加密时,可指定IV,默认会自动生成随机IV
  • 解密时需要KEY + IV

Python

  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. from Cryptodome.Util import Padding
  5. key = ('A' * 16).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. pad_data = Padding.pad(data, AES.block_size)
  8. mode = AES.MODE_CBC
  9. cipher = AES.new(key, mode)
  10. iv = cipher.iv
  11. cy_bytes = cipher.encrypt(pad_data)
  12. ct = base64.b64encode(cy_bytes).decode()
  13. print('KEY:', key.decode())
  14. print('CBC base64:', ct)
  15. cipher = AES.new(key, mode, iv=iv)
  16. dec_message = cipher.decrypt(base64.b64decode(ct))
  17. pt = json.loads(Padding.unpad(dec_message, AES.block_size).decode())
  18. print('Parsed:', pt)
  19. '''输出
  20. KEY: AAAAAAAAAAAAAAAA
  21. IV: diU6MY0+Y7+LH6asCIvrhQ==
  22. CBC base64: avCVt0G9tp4RC2bSAUrofV2DQU4Jggbgd7nMLG3im0E=
  23. Parsed: {'name': 'zoro', 'age': 18}
  24. '''
  25. # 固定IV
  26. iv = ('B' * 16).encode() # 16 bytes
  27. cipher = AES.new(key, mode, iv=iv)
  28. cy_bytes = cipher.encrypt(pad_data)
  29. ct = base64.b64encode(cy_bytes).decode()
  30. cipher = AES.new(key, mode, iv=iv)
  31. dec_message = cipher.decrypt(base64.b64decode(ct))
  32. pt = json.loads(Padding.unpad(dec_message, AES.block_size).decode())
  33. '''输出
  34. KEY: AAAAAAAAAAAAAAAA
  35. IV: QkJCQkJCQkJCQkJCQkJCQg==
  36. CBC base64: QrG4mKyOKq3fBtDNWUE9CwCNjgDmz7YtDULyWnrW4cs=
  37. Parsed: {'name': 'zoro', 'age': 18}
  38. '''

JavaScript

  1. var key = CryptoJS.enc.Utf8.parse('AAAAAAAAAAAAAAAA');
  2. var iv = CryptoJS.enc.Base64.parse('QkJCQkJCQkJCQkJCQkJCQg==');
  3. // or: var iv = CryptoJS.enc.Utf8.parse('BBBBBBBBBBBBBBBB');
  4. var encrypted = 'QrG4mKyOKq3fBtDNWUE9CwCNjgDmz7YtDULyWnrW4cs=';
  5. CryptoJS.AES.decrypt(encrypted, key, {
  6. mode: CryptoJS.mode.CBC,
  7. iv: iv,
  8. }).toString(CryptoJS.enc.Utf8);
  9. // '{"name": "zoro", "age": 18}'

3 CTR MODE

https://www.pycryptodome.org/en/latest/src/cipher/classic.html#ctr-mode-1

  • Counter, 计算器模式
  • 数据加密前无需pad
  • 数据加密时,可指定nonce,默认会自动生成随机nonce,最大长度为15 (block_size-1 ?)
  • 解密时需要KEY + nonce
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 24).encode()
  5. nonce = ('B' * 15).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_CTR
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes = cipher.encrypt(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('CTR base64:', ct)
  14. cipher = AES.new(key, mode, nonce=nonce)
  15. dec_message = cipher.decrypt(base64.b64decode(ct))
  16. pt = json.loads(dec_message.decode())
  17. print('Parsed:', pt)
  18. '''输出
  19. KEY: AAAAAAAAAAAAAAAAAAAAAAAA
  20. nonce: BBBBBBBBBBBBBBB
  21. CTR base64: 2sC9xBr1Uk/qo7rkurfP9GfSZMkx6UVOZaC6
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

4 CFB MODE

https://www.pycryptodome.org/en/latest/src/cipher/classic.html#cfb-mode-1

  • Cipher FeedBack, 密码反馈模式
  • 数据加密前无需pad
  • 数据加密时,可指定IV,默认会自动生成随机IV
  • 解密时需要KEY + IV

Python

  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16).encode()
  5. iv = ('B' * 16).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_CFB
  8. cipher = AES.new(key, mode, iv=iv)
  9. cy_bytes = cipher.encrypt(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('IV:', iv.decode())
  13. print('CFB base64:', ct)
  14. cipher = AES.new(key, mode, iv=iv)
  15. dec_message = cipher.decrypt(base64.b64decode(ct))
  16. pt = json.loads(dec_message.decode())
  17. print('Parsed:', pt)
  18. '''输出
  19. KEY: AAAAAAAAAAAAAAAA
  20. IV: BBBBBBBBBBBBBBBB
  21. CFB base64: ShJ+eeRSprU2s/ekM2ht4k9EBuvuUn1usyCH
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

JavaScript

  1. var key = CryptoJS.enc.Utf8.parse('AAAAAAAAAAAAAAAA');
  2. var iv = CryptoJS.enc.Utf8.parse('BBBBBBBBBBBBBBBB');
  3. var encrypted = 'ShJ+eeRSprU2s/ekM2ht4k9EBuvuUn1usyCH';
  4. CryptoJS.AES.decrypt(encrypted, key, {
  5. mode: CryptoJS.mode.CFB,
  6. iv: iv,
  7. }).toString(CryptoJS.enc.Utf8);
  8. // '{"name": "zoro", "age": 18}'

5 OFB MODE

https://www.pycryptodome.org/en/latest/src/cipher/classic.html#ofb-mode-1

  • Output FeedBack, 输出反馈模式
  • 数据加密前无需pad
  • 数据加密时,可指定IV,默认会自动生成随机IV
  • 解密时需要KEY + IV
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. from Cryptodome.Util import Padding
  5. key = ('A' * 16).encode()
  6. iv = ('B' * 16).encode()
  7. data = json.dumps(dict(name='zoro', age=18)).encode()
  8. mode = AES.MODE_OFB
  9. cipher = AES.new(key, mode, iv=iv)
  10. cy_bytes = cipher.encrypt(data)
  11. ct = base64.b64encode(cy_bytes).decode()
  12. print('KEY:', key.decode())
  13. print('IV:', iv.decode())
  14. print('OFB base64:', ct)
  15. cipher = AES.new(key, mode, iv=iv)
  16. dec_message = cipher.decrypt(base64.b64decode(ct))
  17. pt = json.loads(dec_message.decode())
  18. print('Parsed:', pt)
  19. '''输出
  20. KEY: AAAAAAAAAAAAAAAA
  21. IV: BBBBBBBBBBBBBBBB
  22. OFB base64: SsFUDz81sqBec/aIH0O9VeN6PUzGnzBnkTjX
  23. Parsed: {'name': 'zoro', 'age': 18}
  24. '''

二 现代化模式

1 CCM MODE

https://www.pycryptodome.org/en/latest/src/cipher/modern.html#ccm-mode-1

  • Counter with CBC-MAC,
  • 数据加密前无需pad
  • 数据加密时,可指定nonce,默认会自动生成随机nonce, nonce长度任意,推荐12
  • 解密时需要 key + nonce
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16).encode()
  5. nonce = ('B' * 12).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_CCM
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes = cipher.encrypt(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('CCM base64:', ct)
  14. cipher = AES.new(key, mode, nonce=nonce)
  15. dec_message = cipher.decrypt(base64.b64decode(ct))
  16. pt = json.loads(dec_message.decode())
  17. print('Parsed:', pt)
  18. '''输出
  19. KEY: AAAAAAAAAAAAAAAA
  20. nonce: BBBBBBBBBBBB
  21. CCM base64: QLkTB74YqRW4cjEqYKq1oUnyUnOR65LNqc4r
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

2 EAX MODE

https://www.pycryptodome.org/en/latest/src/cipher/modern.html#eax-mode-1

  • key + nonce
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16).encode()
  5. nonce = ('B' * 12).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_EAX
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes = cipher.encrypt(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('EAX base64:', ct)
  14. cipher = AES.new(key, mode, nonce=nonce)
  15. dec_message = cipher.decrypt(base64.b64decode(ct))
  16. pt = json.loads(dec_message.decode())
  17. print('Parsed:', pt)
  18. '''输出
  19. KEY: AAAAAAAAAAAAAAAA
  20. nonce: BBBBBBBBBBBB
  21. EAX base64: 6exKCce+QEjnZZ4uD1GJoRv24cKNSZX/Bd/c
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

3 GCM MODE

https://www.pycryptodome.org/en/latest/src/cipher/modern.html?highlight=GCM#ccm-mode-1

  • Galois/Counter Mode, 伽罗瓦/计数器模式
  • key + nonce
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16).encode()
  5. nonce = ('B' * 16).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_GCM
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes = cipher.encrypt(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('GCM base64:', ct)
  14. cipher = AES.new(key, mode, nonce=nonce)
  15. dec_message = cipher.decrypt(base64.b64decode(ct))
  16. pt = json.loads(dec_message.decode())
  17. print('Parsed:', pt)
  18. '''输出
  19. KEY: AAAAAAAAAAAAAAAA
  20. nonce: BBBBBBBBBBBBBBBB
  21. GCM base64: 6L6INq3hBjFmuazE/AxDygdKoCf0/Xs1MCr6
  22. Parsed: {'name': 'zoro', 'age': 18}
  23. '''

4 SIV MODE

https://www.pycryptodome.org/en/latest/src/cipher/modern.html#siv-mode-1

  • Synthetic Initialization Vector
  • key + nonce + tag
  • key: 32 bytes
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16 * 2).encode()
  5. nonce = ('B' * 16).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_SIV
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes, tag = cipher.encrypt_and_digest(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('tag:', tag)
  14. print('SIV base64:', ct)
  15. cipher = AES.new(key, mode, nonce=nonce)
  16. dec_message = cipher.decrypt_and_verify(base64.b64decode(ct), tag)
  17. pt = json.loads(dec_message.decode())
  18. print('Parsed:', pt)
  19. '''输出
  20. KEY: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  21. nonce: BBBBBBBBBBBBBBBB
  22. tag: b'\xd8>\xd6\x81*\xce\xa0k\x19\x05\xf7\x82\r{\xa1\x11'
  23. SIV base64: jkdH0qf9Fy5KlymtehsLHGiQmX4rettnk16v
  24. Parsed: {'name': 'zoro', 'age': 18}
  25. '''

5 OCB MODE

https://www.pycryptodome.org/en/latest/src/cipher/modern.html#ocb-mode-1

  • Offset CodeBook
  • key + nonce + tag
  • tag: <=15 bytes
  1. import json
  2. import base64
  3. from Cryptodome.Cipher import AES
  4. key = ('A' * 16).encode()
  5. nonce = ('B' * 15).encode()
  6. data = json.dumps(dict(name='zoro', age=18)).encode()
  7. mode = AES.MODE_OCB
  8. cipher = AES.new(key, mode, nonce=nonce)
  9. cy_bytes, tag = cipher.encrypt_and_digest(data)
  10. ct = base64.b64encode(cy_bytes).decode()
  11. print('KEY:', key.decode())
  12. print('nonce:', nonce.decode())
  13. print('tag:', tag)
  14. print('OCB base64:', ct)
  15. cipher = AES.new(key, mode, nonce=nonce)
  16. dec_message = cipher.decrypt_and_verify(base64.b64decode(ct), tag)
  17. pt = json.loads(dec_message.decode())
  18. print('Parsed:', pt)
  19. '''输出
  20. KEY: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  21. nonce: BBBBBBBBBBBBBBB
  22. tag: b'\x13\x00g\xcc\xbb\x8aX{\xed\xe8\xf8\xd4h\xfa\x0f\xa1'
  23. OCB base64: znbNBeF5YVgihsx1S+IRP4d4cZiJK1PunOcf
  24. Parsed: {'name': 'zoro', 'age': 18}
  25. '''

总结

模式 加密 解密
ECB KEY + PAD UNPAD + KEY
CBC KEY + PAD [ + IV ] UNPAD + KEY + IV
CTR KEY [ + nonce ] KEY + nonce
CFB KEY [ + IV ] KEY + IV
OFB KEY [ + IV ] KEY + IV
CCM KEY [ + nonce ] KEY + nonce
EAX KEY [ + nonce ] KEY + nonce
GCM KEY [ + nonce ] KEY + nonce
SIV KEY [ + nonce ] KEY + nonce + tag
OCB KEY [ + nonce ] KEY + nonce + tag