异或加密大部分时间有效
我被要求给一个Java应用加上密码保护,但对安全性要求不高。所以我觉得把用户名和密码存到一个文本文件里,然后再加密是个不错的主意。为了加密,我选择了XOR加密,因为它简单又快(记住——这只是为了让普通用户不容易破解,而不是要做到万无一失)。
我写好了所有的Java代码,然后意识到我需要一个方法来加密配置文件。我写了一个额外的方法,但用起来很麻烦,重复使用一两次就觉得不方便(而且似乎只对某些输入有效),所以我决定用Python写点东西,方便在REPL里玩。
最后我得到了这个:
from itertools import izip, cycle
KEY = "stackoverflow"
def encrypt(text):
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(text,cycle(KEY)))
def decrypt(text):
return encrypt(text)
def export(users, file):
with open(file, "w") as f:
for user, password in users.items():
f.write(encrypt('"%s" "%s"'%(user, password)) + "\n")
def import_data(file):
with open(file) as f:
return [decrypt(i) for i in f.readlines()]
表面上看,它是能工作的:
>>> x = encrypt("Hello world!")
>>> x
';\x11\r\x0f\x04O\x01\n\x00\n\x08N'
>>> decrypt(x)
'Hello world!'
但接下来事情就开始出问题了:
>>> export({"foo" : "bar", "baz" : "quux", "spam" : "eggs"}, "users.dat")
>>> import_data("users.dat")
['"foo" "bar"e', '"baz" "quux"}', '"spam" "eggs"y']
这是vim读取它的样子 -
然后:
>>> export({"what" : "not", "this" : "that", "admin_istrator" : "quux"}, "users2.dat")
>>> import_data("users2.dat")
['"thi', "k97$ma{~'l", '"what" "not"}', '"admin_istrator" "quux', '7~']
Vim:
我突然想到,可能是某个字符加密后的形式变成了换行符,但就我所见,这并不能解释第一个例子中的奇怪行为,或者第二个例子中所有的奇怪行为。
关于换行符,我的备用计划是加密整个文件——包括换行符——然后再把它读回来,解密后按“\n”分割,然后继续我的逐行解析。
提前谢谢你们。
更新:这是我备用计划的实现(在两段前提到过)。
def import2(file):
with open(file) as f:
return decrypt(f.read())
然后:
>>> export({"foo" : "bar", "this" : "that", "admin_istrator" : "letmein"}, "users2.dat")
>>> import2("users2.dat")
'"this" "that"y%smg&91uux!}"admin_istrator" "letmein"y'
更新二:二进制。
[代码和上面的一样,只是所有的open
都是open(file, "rb")
或open(file, "wb")
。]
>>> export({"foo" : "bar", "this" : "that", "admin_istrator" : "letmein"}, "users2.dat")
>>> import2("users2.dat")
'"this" "that"y%smg&91uux!}"admin_istrator" "letmein"y'
>>> import_data("users2.dat")
['"t', "k97$ma{~'", '"foo" "bar"', '"admin_istrator" "letmein"']
最后更新:Base 64,其他小花招。
def import2(file):
with open(file, "rb") as f:
return filter(str.strip, [decrypt(i) for i in f.readlines()])
其中encrypt
和decrypt
是用Base 64进行encode
和decode
。
3 个回答
你可以通过对换行符进行加密来解决这个问题,并且在行与行之间不要重置密钥。
from itertools import izip, cycle
KEY = "stackoverflow"
def encrypt(text):
return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in izip(text,key))
def decrypt(text):
return encrypt(text)
def export(users, file):
with open(file, "w") as f:
for user, password in users.items():
f.write(encrypt('"%s" "%s"\n'%(user, password)))
def import_data(file):
with open(file) as f:
return [decrypt(i) for i in f]
key = cycle(KEY)
export({"foo" : "bar", "baz" : "quux", "spam" : "eggs"}, "users.dat")
key = cycle(KEY)
for row in import_data("users.dat"):
print row
这部分代码应该改成一个类,并且key
应该作为一个实例变量,而不是像现在这样作为全局变量。
问题在于你读取的数据和你编码时的数据不一样(你在加密后添加了一个'\n')。只需要对你读取的数据使用rstrip()方法就可以了:
def import_data(file):
with open(file) as f:
return [decrypt(i.rstrip()) for i in f.readlines()]
你正在尝试在文本模式的文件中存储二进制数据。要写入二进制文件,请使用 open(file, "wb")
,要读取二进制文件,请使用 open(file, "rb")
,这样可以以二进制模式打开文件,解决问题。
在文本模式下,每个 "\r"
、"\n"
和 "\r\n"
的组合都会被当作换行符处理,所以它们会被转换成你电脑操作系统的换行格式(在Windows上是 "\r\n"
,在Unix上是 "\n"
,在旧款Mac上是 "\r"
)。如果你从文本文件中读取这些内容,你总是会得到 "\n"
,而如果你写入这些内容,我不太记得具体的表现,但你肯定会得到一些混乱的数据,而不是你想要的内容 :)
而且使用异或加密时,你很可能会遇到这种问题 :)
如果你被迫不能使用二进制文件,可以尝试使用base64编码(例如 "some\0te\n\nxt with bi\x01naries".encode('base64')
)。要解码的话,使用 .decode
(谢谢你,显而易见的提醒!)。