异或加密大部分时间有效

2 投票
3 回答
1401 浏览
提问于 2025-04-16 19:04

我被要求给一个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读取它的样子 -

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:

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()])

其中encryptdecrypt是用Base 64进行encodedecode

3 个回答

0

你可以通过对换行符进行加密来解决这个问题,并且在行与行之间不要重置密钥。

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应该作为一个实例变量,而不是像现在这样作为全局变量。

0

问题在于你读取的数据和你编码时的数据不一样(你在加密后添加了一个'\n')。只需要对你读取的数据使用rstrip()方法就可以了:

  def import_data(file):
    with open(file) as f:
       return [decrypt(i.rstrip()) for i in f.readlines()]
1

你正在尝试在文本模式的文件中存储二进制数据。要写入二进制文件,请使用 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(谢谢你,显而易见的提醒!)。

撰写回答