使用python2和python3的相同代码编码+加密+ pad的麻烦

Gho*_*ica 4 python encryption padding

免责声明:据我所知,以下内容适合在生产环境中提供"安全性".它简单地说比在我的系统上存储的敏感数据上使用XOR或rot13"好一点".

我将以下代码放在一起,以允许我对这些敏感值使用AES加密.AES需要16个字节的块; 所以我需要填充.我想将这些数据保存在文本文件中; 所以我添加了base64编码:

from __future__ import print_function
from Crypto.Cipher import AES
import base64

crypto = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s: s[0:-ord(s[-1])]


def scramble(data):
    return base64.b64encode(crypto.encrypt(pad(data)))


def unscramble(data):
    return unpad(crypto.decrypt(base64.b64decode(data)))

incoming = "abc"
print("in: {}".format(incoming))
scrambled = scramble(incoming)
print("scrambled: {}".format(scrambled))
andback= unscramble(scrambled)
print("reversed : {}".format(andback))
Run Code Online (Sandbox Code Playgroud)

对于python2; 打印:

in:abc

scrambled:asEkqlUDiqlUpW1lw09UlQ ==

逆转:

对于python3; 我遇到了

unpad = lambda s: s[0:-ord(s[-1])]
TypeError: ord() expected string of length 1, but int found
Run Code Online (Sandbox Code Playgroud)

两个问题:

  • 我的"反向"路径与python2有什么问题,为什么不打印"abc"?
  • 我理解使用python3的错误信息; 但我想知道:以一种适用于python2和python3的方式解决这个问题的正确,规范的方法是什么?

Ser*_*sta 7

一个问题是Crypto模块在Python3中返回字节字符串.

所以当你使用时s[-1],你实际上得到一个整数而不再是一个字节串.可移植的方法是使用s[-1:]正确的Python2中的字符和Python3中适合的字节串ord:

unpad = lambda s: s[0:-ord(s[-1:])]
Run Code Online (Sandbox Code Playgroud)


PM *_*ing 5

通常,在Python 2和Python 3中正确处理二进制数据的代码可能会有点混乱.正如您所发现的,当您bytes在Python 3中迭代字符串时,您将获得整数,而不是字符.

因此在Python 2中,这段代码

print([i for i in b'ABCDE'])
print([ord(c) for c in 'ABCDE'])
Run Code Online (Sandbox Code Playgroud)

输出

['A', 'B', 'C', 'D', 'E']
[65, 66, 67, 68, 69]
Run Code Online (Sandbox Code Playgroud)

而在Python 3中它输出

[65, 66, 67, 68, 69]
[65, 66, 67, 68, 69]
Run Code Online (Sandbox Code Playgroud)

处理这个的简洁方法是简单地为两个版本编写单独的代码.但它可以编写两个版本运行的代码.

这是您在问题中发布的代码的修改版本.它还通过每次加密或解密时创建新的AES密码对象来处理AES的有状态.

from __future__ import print_function
from Crypto.Cipher import AES
import base64

BS = 16

def pad(s):
    padsize = BS - len(s) % BS
    return (s + padsize * chr(padsize)).encode('utf-8')

def unpad(s):
    s = s.decode('utf-8')
    offset = ord(s[-1])
    return s[:-offset]

def scramble(data, key, iv):
    crypto = AES.new(key, AES.MODE_CBC, iv)
    raw = crypto.encrypt(pad(data))
    return base64.b64encode(raw)

def unscramble(data, key, iv):
    crypto = AES.new(key, AES.MODE_CBC, iv)
    raw = crypto.decrypt(base64.b64decode(data))
    return unpad(raw)

key = b'This is a key123'
iv = b'This is an IV456'

incoming = "abc def ghi jkl mno"
print("in: {0!r}".format(incoming))

scrambled1 = scramble(incoming, key, iv)
print("scrambled: {0!r}".format(scrambled1))

incoming = "pqr stu vwx yz0 123"
print("in: {0!r}".format(incoming))

scrambled2 = scramble(incoming, key, iv)
print("scrambled: {0!r}".format(scrambled2))

andback = unscramble(scrambled2, key, iv)
print("reversed : {0!r}".format(andback))

andback = unscramble(scrambled1, key, iv)
print("reversed : {0!r}".format(andback))
Run Code Online (Sandbox Code Playgroud)

Python 3输出

in: 'abc def ghi jkl mno'
scrambled: b'C2jA5/WngDo55J7TG3uiArEO7hhyTPld/A3v52t+ANc='
in: 'pqr stu vwx yz0 123'
scrambled: b'FsFAKA2SbhCTimURy0W8+tM4iqLhNlK3OZrRuuYpMpY='
reversed : 'pqr stu vwx yz0 123'
reversed : 'abc def ghi jkl mno'
Run Code Online (Sandbox Code Playgroud)

在Python 2中,反转输出看起来像

reversed : u'pqr stu vwx yz0 123'
reversed : u'abc def ghi jkl mno'
Run Code Online (Sandbox Code Playgroud)

因为我们将字节解码为Unicode.


我把padunpad函数转换成了正确的def函数.这使他们更容易阅读.此外,它通常被认为是lambda用于命名函数的坏样式:lambda应该用于匿名函数.