包含自身校验和的文件

58

能否创建一个包含自身校验和(MD5、SHA1等)的文件?我是指在文件中明文存储校验和,而不是调用函数计算校验和。

12个回答

40

嘿,你是怎么制作这个预计算表的?我想完全复制一遍... :) - Synox
我认为我找到了代码。它很不干净,也没有预计算表。http://www.latinsud.com/pub/crc32/ - LatinSuD
@LatinSuD 我是一个Java程序员,对C语言不太擅长。你能解释一下这段代码是如何工作的吗?我不明白当CRC是计算字符串的一部分时,如何使用预计算表。 - localhost

18
是的,这是可能的,并且在简单的校验和中很常见。让文件包含自己的md5sum将是非常具有挑战性的。
在最基本的情况下,创建一个校验和值,使得总和模等于零。校验和函数变成了以下类似形式的内容:
(n1 + n2 ... + CRC) % 256 == 0

如果校验和成为文件的一部分,并进行检查。这种情况非常常见,例如信用卡号码中使用的Luhn算法。最后一位数字是校验位,也是16位数字的一部分。

没错,这就是我说的。 :-)由于只有32位,完全可以通过暴力破解来解决问题。 - Steven Sudit
10
这并未展示如何在文件内包含md5sum,而这正是问题所问的。 - andrewrk

13

看这个:

echo -e '#!/bin/bash\necho My cksum is 918329835' > magic

15
大约三个月左右,我只是对这个数字进行了递增,并通过一个bash脚本每秒进行大约350次检查。我认为这并不是该文件的唯一有效校验和。 - sasha

8

“我希望我的CRC32值是802892ef...”

嗯,我觉得这很有趣,所以今天我编写了一个小的Java程序来查找冲突。如果有人觉得有用,我把它留在这里:

import java.util.zip.CRC32;

public class Crc32_recurse2 {

    public static void main(String[] args) throws InterruptedException {

        long endval = Long.parseLong("ffffffff", 16);

        long startval = 0L;
//      startval = Long.parseLong("802892ef",16); //uncomment to save yourself some time

        float percent = 0;
        long time = System.currentTimeMillis();
        long updates = 10000000L; // how often to print some status info

        for (long i=startval;i<endval;i++) {

            String testval = Long.toHexString(i);

            String cmpval = getCRC("I wish my crc32 was " + testval + "...");
            if (testval.equals(cmpval)) {
                System.out.println("Match found!!! Message is:");
                System.out.println("I wish my crc32 was " + testval + "...");
                System.out.println("crc32 of message is " + testval);
                System.exit(0);
            }

            if (i%updates==0) {
                if (i==0) {
                    continue; // kludge to avoid divide by zero at the start
                }
                long timetaken = System.currentTimeMillis() - time;
                long speed = updates/timetaken*1000;
                percent =  (i*100.0f)/endval;
                long timeleft = (endval-i)/speed; // in seconds
                System.out.println(percent+"% through - "+ "done "+i/1000000+"M so far"
                        + " - " + speed+" tested per second - "+timeleft+
                        "s till the last value.");
                time = System.currentTimeMillis();
            }       
        }       
    }

    public static String getCRC(String input) {
        CRC32 crc = new CRC32();
        crc.update(input.getBytes());
        return Long.toHexString(crc.getValue());
    }

}

输出结果:
49.825756% through - done 2140M so far - 1731000 tested per second - 1244s till the last value.
50.05859% through - done 2150M so far - 1770000 tested per second - 1211s till the last value.
Match found!!! Message is:
I wish my crc32 was 802892ef...
crc32 of message is 802892ef

请注意,消息末尾的点实际上是消息的一部分。
在我的i5-2500上,需要大约40分钟才能搜索整个crc32空间从00000000到ffffffff,每秒进行约180万次测试。它最多使用一个核心。
我对Java相当陌生,所以任何关于我的代码的建设性评论都将不胜感激。
“我的crc32是c8cb204,而我只得到了这件糟糕的T恤!”

6
当然,这是可能的。但校验和的一个用途是检测文件是否被篡改过 - 如果修改者也能替换校验和,那么你怎么知道文件是否被修改了呢?

3
@AmigableClarkKant,我的观点是,走这条路是有害的-它违背了首先设置校验和的目的。问题明确提到了密码算法,因此我认为意图是检测有意的篡改而不是意外的损坏。 - Mark Ransom
@MarkRansom我不相信任何从公开讨论如何破解它的缺乏中获得“安全性”的加密算法。在这种情况下,应该有公开讨论。这不会破坏安全性,因为任何安全性都是虚假的,人们会知道该算法实际上并不安全,他们应该使用其他东西代替。 - flarn2006
@flarn2006 我的观点是,在文件上放置校验和根本不会提供任何安全性。如果您想检测文件的意外损坏,那么它可能有用,但对于有意攻击则毫无价值。 - Mark Ransom

5
当然,您可以将文件本身的摘要连接到文件末尾。要检查它,您将计算除最后一部分以外的所有内容的摘要,然后将其与最后一部分中的值进行比较。当然,如果没有某种形式的加密,任何人都可以重新计算摘要并替换它。
编辑
我应该补充说,这并不是那么不寻常。一种技术是将CRC-32连接起来,使得整个文件(包括该摘要)的CRC-32为零。但这对基于密码哈希的摘要不起作用。

2

在python-stdnum库中有一个Luhn Mod N算法的实现(请参见luhn.py)。calc_check_digit函数将计算一个数字或字符,当附加到文件(表示为字符串)时,将创建一个有效的Luhn Mod N字符串。正如上面许多答案所指出的那样,这可以对文件的有效性进行一次检查,但不能防止被篡改。接收者需要知道用于定义Luhn mod N有效性的字母表。


1

如果问题是询问一个文件是否可以包含自己的校验和(除其他内容之外),对于固定大小的校验和来说,答案显然是肯定的,因为一个文件可以包含所有可能的校验和值。

如果问题是一个文件是否可以由自己的校验和(而没有其他内容)组成,那么很容易构造一个使这样的文件不可能存在的校验和算法:对于一个n字节的校验和,取文件的前n个字节的二进制表示并加1。由于也很容易构造一个总是编码自身的校验和(即执行上述操作但不加1),显然有些校验和可以编码自身,而有些则不能。很难确定标准校验和属于哪一类。


1

我不确定我是否正确理解了你的问题,但是您可以将文件的前16个字节作为文件其余部分的校验和。

因此,在编写文件之前,您需要计算哈希值,先写入哈希值,然后再写入文件内容。


1
虽然这是一种完全有效的实用方法,但我指的是包括自身的校验和。 - zakovyrya
我不是数学家,但我认为这是根本不可能的。 - Philippe Leybaert
4
这不是不可能,但非常非常困难。 - Lasse V. Karlsen
2
对于 CRC-32,实际上非常简单。对于加密哈希,您是完全正确的。 - Steven Sudit

0

有许多方法可以嵌入信息以检测传输错误等。 CRC 校验和对检测连续位翻转的情况很好,并且可以以使校验和始终为 0 的方式添加。这些种类的校验和(包括纠错码)很容易被重新生成,不能阻止恶意篡改。

如果接收方除了来自发送方之外不知道其他信息,就不可能将某些东西嵌入到消息中以便接收方验证其真实性。接收方可以与发送方共享一个秘密密钥。发送方随后可以附加一个加密的校验和(需要是加密安全的,如 md5/sha1)。还可以使用非对称加密,在此情况下,发送方可以发布他的公钥并使用他的私钥签名 md5 校验和/哈希。然后可以将哈希和签名作为一种新类型的校验和附加到数据上。这在现今的互联网上经常发生。

剩下的问题是:1. 接收者如何确保他得到了正确的公钥,2. 所有这些东西在现实中有多安全?对于第一个问题的答案可能会有所不同。在互联网上,通常会有每个人都信任的某个人签署公钥。另一个简单的解决方案是接收者从个人会议中获取公钥... 对于第二个问题的答案可能会随时改变,但是今天强制执行的成本很可能在未来某个时候被轻易破解。到那时,新算法和/或扩大的密钥尺寸已经出现了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接