如何确定所选密码是否正确?

时间:2009-07-31 13:21:59

标签: encryption cryptography

如果存在加密文件并且有人想要解密,则有几种方法可以尝试。 例如,如果您选择暴力攻击,那很简单:只需尝试所有可能的密钥,您就会找到正确的密钥。对于这个问题,这可能需要太长时间并不重要。 但尝试按键意味着以下步骤:

  1. 选择密钥
  2. 使用密钥
  3. 解密数据
  4. 检查解密是否成功
  5. 除了你需要知道用于加密的算法的问题外,我无法想象会怎么做#3。

    原因如下:在解密数据后,我得到了一些“其他”数据。如果使用我能理解的语言加密纯文本文件,我现在可以检查结果是否是该语言中的文本。 如果它是一个已知的文件类型,我可以检查特定的文件头。

    但是,由于人们试图解密某些秘密内容,如果正确解密,很可能不知道会有什么样的信息。

    如果不知道要查找什么,如何检查解密结果是否正确?

6 个答案:

答案 0 :(得分:4)

您可以使用像unix

这样的启发式方法
file

命令用于检查已知的文件类型。如果你有解密的数据没有可识别的类型,解密它无论如何都无济于事,因为你无法解释它,所以它仍然像加密一样好。

答案 1 :(得分:4)

就像你建议的那样,人们会期望明文具有某种已知的格式,例如,JPEG图像,PDF文件等。这个想法是,给定的密文不可能被解密为有效的JPEG图像和有效的PDF文件(但见下文)。

但实际上并不重要。当人们谈论密码系统 secure 时,一个(粗略地)谈论你能够猜测对应于给定密文的明文的可能性。所以我选择随机消息 m 并加密它 c = E m )。我给你 c ,如果你不能猜测 m ,那么我们说密码系统是安全的,否则它就坏了。

这只是一个简单的安全定义。还有其他定义要求系统能够hide known plaintexts(语义安全):你给我两条消息,我加密其中一条消息,你将无法分辨我选择的消息。

关键是,在这些定义中,我们并不关心明文的格式,我们要求的是你无法猜出加密的明文。 所以没有第3步: - )

如果不考虑你的第3步,我们会尽可能清楚地提出安全问题:而不是争论猜测你使用哪种格式(zip,gzip,bzip2,...)是多么困难,我们只讨论与猜测的可能性相比,打破系统的几率。这是一个old principle,您应该将所有安全性集中在密钥中 - 当您唯一的假设是密钥的保密时,它会大大简化事情。

最后,请注意,由于所有密钥都是合法的,因此您无法验证是否拥有正确的密钥。 one-time pad是一个极端的例子:你用明文 m ,选择一个完全随机的密钥 k 并将密文计算为 c < / em> = m XOR k 。这为您提供了一个完全随机的密文,它非常安全(唯一完全安全的密码系统,顺便说一句)。

搜索加密密钥时,您无法知道何时找到了正确的加密密钥。这是因为 c 可以是与 m 长度相同的任何文件的加密:如果使用密钥*加密消息 m' k'= c XOR m'你会看到你再次获得相同的密文,因此你无法知道 m m'是原始信息。

而不是考虑排他性或者,你可以想到这样的一次性垫:我给你42号并告诉你这是两个整数的总和(负,正,你不知道)。一个整数是消息,另一个是密钥,42是密文。如上所述,您猜测密钥是没有意义的 - 如果您希望消息为100,则声明密钥为-58,如果您希望消息为0,则声明密钥为42,等等。一次性打击垫的工作方式与此类似,但改为使用位值。

关于在一次性密码中重用密钥:让我说我的密钥是7,你看到密文10和20,对应于明文3和13.从单独的密文,你现在知道了明文的差异是10.如果你以某种方式获得其中一个明文的知识,你现在可以得到另一个!如果数字对应于单个字母,您可以开始查看几个这样的差异,并尝试解决最终的填字游戏(或者让程序根据相关语言的频率分析来完成)。

答案 2 :(得分:2)

我不久前写了一个工具,通过简单地检查字节值的分布来检查文件是否可能加密,因为加密文件应该与随机噪声无法区分。这里的假设是,不正确解密的文件保留随机性,而正确解密的文件将表现出结构。

#!/usr/bin/env python

import math
import sys
import os

MAGIC_COEFF=3

def get_random_bytes(filename):
        BLOCK_SIZE=1024*1024
        BLOCKS=10

        f=open(filename)
        bytes=list(f.read(BLOCK_SIZE))

        if len(bytes) < BLOCK_SIZE:
                return bytes

        f.seek(0, 2)
        file_len = f.tell()
        index = BLOCK_SIZE
        cnt=0
        while index < file_len and cnt < BLOCKS:
                f.seek(index)
                more_bytes = f.read(BLOCK_SIZE)
                bytes.extend(more_bytes)
                index+=ord(os.urandom(1))*BLOCK_SIZE
                cnt+=1

        return bytes

def failed_n_gram(n,bytes):
        print "\t%d-gram analysis"%(n)
        N = len(bytes)/n
        states = 2**(8*n)
        print "\tN: %d states: %d"%(N, states)

        if N < states:
                print "\tinsufficient data"
                return False

        histo = [0]*states
        P = 1.0/states

        expected = N/states * 1.0
        # I forgot how this was derived, or what it is suppose to be
        magic = math.sqrt(N*P*(1-P))*MAGIC_COEFF
        print "\texpected: %f magic: %f" %(expected, magic)

        idx=0
        while idx<len(bytes)-n:
                val=0
                for x in xrange(n):
                        val = val << 8
                        val = val | ord(bytes[idx+x])

                histo[val]+=1
                idx+=1

                count=histo[val]
                if count - expected > magic:
                        print "\tfailed: %s occured %d times" %( hex(val), count)
                        return True

        # need this check because the absence of certain bytes is also
        # a sign something is up
        for i in xrange(len(histo)):
                count = histo[i]
                if expected-count > magic:
                        print "\tfailed: %s occured %d times" %( hex(i), count)
                        return True

        print ""

        return False

def main():
        for f in sys.argv[1:]:
                print f
                rand_bytes = get_random_bytes(f)

                if failed_n_gram(3,rand_bytes):
                        continue

                if failed_n_gram(2,rand_bytes):
                        continue

                if failed_n_gram(1,rand_bytes):
                        continue


if __name__ == "__main__":
        main()

我觉得这很合理:

$ entropy.py ~/bin/entropy.py entropy.py.enc entropy.py.zip 
/Users/steve/bin/entropy.py
        1-gram analysis
        N: 1680 states: 256
        expected: 6.000000 magic: 10.226918
        failed: 0xa occured 17 times
entropy.py.enc
        1-gram analysis
        N: 1744 states: 256
        expected: 6.000000 magic: 10.419895

entropy.py.zip
        1-gram analysis
        N: 821 states: 256
        expected: 3.000000 magic: 7.149270
        failed: 0x0 occured 11 times

此处.enc来源是:

openssl enc -aes-256-cbc -in entropy.py -out entropy.py.enc

.zip不言自明。

一些警告:

  1. 它不会检查整个文件,只检查第一个KB,然后检查文件中的随机块。因此,如果一个文件是随机数据附加说jpeg,它将欺骗该程序。唯一可以确定是否检查整个文件的方法。

  2. 根据我的经验,代码可以可靠地检测文件何时未加密(因为几乎所有有用的数据都具有结构),但由于其统计性质,有时可能会误诊加密/随机文件。

  3. 正如已经指出的那样,这种分析对于OTP来说是失败的,因为你可以让它说出你想要的任何东西。

  4. 使用风险自负,而且肯定不是检查结果的唯一方法。

答案 3 :(得分:0)

其中一种方法是使用zip等标准算法压缩源数据。如果在解密后你可以解压缩结果 - 它被解密了。压缩几乎通常是在加密之前通过加密程序完成的 - 因为这是执行者需要为每次试验重复并在其上浪费时间的另一步,因为加密数据几乎肯定是不可压缩的(使用链式算法压缩后大小不会减少) )。

答案 4 :(得分:0)

如果没有更明确定义的方案,我只能指向cryptanalysis methods。我会说,通常认为验证结果是密码分析的一个简单部分。与解密甚至已知的密码相比,彻底的验证检查只需要很少的CPU。

答案 5 :(得分:-1)

你真的在问这样的问题吗? 好吧,如果知道什么内部那么你不需要解密它无论如何正确吗?

某种程度上这与编程问题无关,它更具有数学性。我在大学里上了一些加密数学课。

如果没有大量的数据点,你无法确认。 当然,如果你的结果是有意义的,并且明确的是用普通英语(或任何使用的语言)有意义,但要回答你的问题。

如果你能够解密,你也应该能够加密。 因此,使用反向解密过程对结果进行加密,如果得到相同的结果,则可能是金色的...如果没有可能出错的话。