Perl中的对称加密

爱丽丝购买了Neiman-Marcus的250美元饼干食谱,想把它发送给鲍勃,但又不希望它落入隔间对面窃听所有人网络流量的伊芙手中。

Perl如何帮助她呢?

密码

密码学算法,或密码,为爱丽丝提供了一种保护数据的方法。通过在网络传输之前加密食谱,她可以使它对除了拥有解密所需秘密信息的鲍勃之外的人无用了。

密码曾经是严格保密的秘密,但依赖于算法的秘密性是一种风险很高的做法。如果您的安全遭到破坏,对手可以阅读您所有的过去消息,并且(如果您发现了漏洞)您必须找到一个全新的算法来在未来使用。

现代密码,通常是公开的且广泛研究的,依赖于密钥的秘密性。它们对每个密钥使用不同的方式加密相同的明文;要解密密文,必须知道产生它的密钥。新密钥很容易生成,因此单个密钥的泄露问题较小。尽管用被盗密钥加密的消息可以变得可读,但算法本身可以重用。

使用相同密钥进行加密和解密的算法称为对称密码。要使用此类算法,爱丽丝和鲍勃必须在他们交换消息之前同意使用密钥。由于解密仅依赖于对密钥的了解,他们必须确保通过Eve无法访问的安全通道共享密钥(例如,爱丽丝可以在晚餐时将密钥低声告诉鲍勃)。

大多数著名的对称密码都是分组密码。要加密的明文必须分成固定长度的块(通常为64或128位长),然后逐个输入到密码中。结果块(长度相同)连接起来形成密文。

今天广泛使用的密码在强度、密钥长度、块大小及其加密数据的方法上有所不同。一些流行的密码(IDEA、Twofish、Rijndael)在CPAN的Crypt::命名空间中实现了同名的模块(Crypt::IDEA等)。

为了决定为特定应用使用哪种密码,必须考虑所需的强度和速度以及可用的计算资源。这个决定不能不经过研究,但IDEA通常被认为是通用密码的最佳实际选择。

密钥

对称密码通常使用随机生成的密钥(长度通常在64到256位之间),而计算机在真正随机数生成方面闻名不佳。幸运的是,许多现代系统都支持生成密码学安全的随机数,从昂贵的硬件到从中断之间的时间延迟中收集熵的设备驱动程序。

Crypt::Random,可在CPAN上获得,是许多Unix系统上/dev/random设备的一个方便接口。一旦安装,它很容易使用

    use Crypt::Random qw( makerandom );

    $key = makerandom( Size => 128, Strength => 1);

对于密码学密钥生成,Strength参数应始终为1。所需密钥的位长度取决于您想要使用密钥的密码。典型的对称密钥长度范围从128位到256位。

我如何在Perl中使用这些?

Crypt模块都支持相同的简单接口:new($key)创建一个密码对象,而encrypt()decrypt()方法操作于单个数据块。密钥生成和共享、提供合适的块以及密文的传输责任在于用户。下面的示例中,我们将使用Crypt::Twofish模块。Twofish是一个免费、无专利的128位分组密码,具有128、192或256位密钥。

    use Crypt::Twofish;

    # Create a new Crypt::Twofish object with the 128-bit key generated
    # above.
    $cipher = Crypt::Twofish->new($key);

    # Encrypt a block full of 0s...
    $ciphertext = $cipher->encrypt(pack "H*", "00"x16);

    # And then decrypt the result.
    print unpack "H*", $cipher->decrypt($ciphertext);

实现中提出了一个重要问题:如何处理一个18字节文件的第二块?Twofish无法在小于16字节块上操作,因此必须在最后一个块的末尾添加填充,使其长度为16字节。通常使用NUL(\000)来填充块,但使用的值并不重要,因为填充在解密密文后会移除。

Alice现在可以使用此代码来加密她的食谱

    # Assume that $key contains a previously-generated key, and that
    # PLAINTEXT and CIPHERTEXT are filehandles opened for reading and
    # writing respectively.

    $cipher = Crypt::Twofish->new($key);

    while (read(PLAINTEXT, $block, 16)) {
        $len   = length $block;
        $size += $len;

        # Add padding if necessary
        $block .= "\000"x(16-$len) if $len < 16;

        $ciphertext .= $cipher->encrypt($block);
    }

    # Record the size of the plaintext, so that the recipient knows how
    # much padding to remove.
    print CIPHERTEXT "$size\n";
    print CIPHERTEXT $ciphertext;

此程序的输出可以安全地通过网络发送给Bob,可能是作为电子邮件附件。Bob通过其他方式收到密钥后,可以使用以下代码来解密消息

    $cipher = Crypt::Twofish->new($key);

    $size = <CIPHERTEXT>;

    while (read(CIPHERTEXT, $ct, 16)) {
        $pt .= $cipher->decrypt($ct);
    }

    # Write only $size bytes of the output; ignore padding.
    print PLAINTEXT substr($pt, 0, $size);

这对于Perl中的对称加密来说就足够了。使用不同的加密算法只需安装另一个模块并将上面的“Twofish”更改即可。然而,从密码学的角度来看,我们仍需要考虑一些问题。

加密模式

上面的代码使用Twofish加密算法在电子密码本(ECB)模式下工作,这意味着第n个密文块只取决于密钥和第n个明文块。对于特定的密钥,可以构建一个穷举表(或密码本)来列出明文块及其对应的密文块。然后,实际上不需要加密明文,只需查看表中的相关条目即可找到密文。

由于大多数文本具有高度重复性,明文块及其在密文中的对应块往往会频繁重复。此外,通常可以就明文的部分内容进行有根据的猜测(例如,Eve知道Alice的消息签名中都有一个长的托尔金语录)。

如果Eve有足够的耐心和密文,她可以开始构建一个密码本,将密文块映射到明文块。然后,即使不知道算法或密钥,她也可以简单地查阅截获的密文中的相关块,并写下原始明文的大部分内容!

已经发明了几种新的加密模式来解决此问题。其中最有用的一种是密码块链(CBC)。CBC首先生成一个随机块(称为初始化向量,或IV)并对其进行加密。然后,将第一个明文块与加密的IV进行XOR操作,然后再进行加密。之后,每个块都与前一个块的密文进行XOR操作,然后再进行加密。

在这里,每个密文块都依赖于前一个密文块和到目前为止的明文块。因此,块必须按顺序解密,ECB显示的任何模式都不会出现。IV本身不需要保密,通常像上面那样与密文一起传输。

密文解密按相反的顺序进行。第一个密文块被解密并与IV进行XOR操作以形成第一个明文块,然后每个随后的密文块都与前一个密文块进行XOR操作以形成一个明文块。其他模式在意图上相似,但在细节上有所不同,包括传输错误如何影响密文,以及反馈量或对前一个块的依赖程度。

Alice和Bob可以修改他们的代码以执行密码块链,但方便的Crypt::CBC模块可以为他们省去麻烦。该模块可以从CPAN获得,并与对称加密模块(如Crypt::Twofish)一起使用。它处理填充、IV生成以及所有其他细节。用户只需指定密钥和要加密或解密的数据。

因此,Alice可以这样做

    use Crypt::CBC;
    $cipher = new Crypt::CBC ($key, 'Twofish');
    undef $/; $plaintext = <PLAINTEXT>;
    print CIPHERTEXT $cipher->encrypt($plaintext);

而Bob可以这样做

    use Crypt::CBC;
    $cipher = new Crypt::CBC ($key, 'Twofish');
    undef $/; $ciphertext = <CIPHERTEXT>;
    print PLAINTEXT $cipher->decrypt($ciphertext);

简单得多!


非对称加密

非对称(或公钥)加密算法使用一对数学相关的密钥,算法设计得使得只能用密钥对中的一半来解密用另一半加密的数据。鲍勃可以生成一个密钥对并保留一半秘密,另一半公之于众。爱丽丝可以用鲍勃的公钥加密食谱,因为她知道只有用秘密的一半才能解密。虽然这消除了在安全通道中共享密钥的需要,但也有其问题。首先,大多数公钥加密方案需要更长的密钥(通常是2048位或更多),而且速度更慢。

Crypt命名空间还包含用于公钥密码学的模块。《Crypt::RSA》是RSA算法的便携式实现,RSA是目前研究最广泛的公钥加密方案之一。它提供了对多种版本的PGP的接口(Crypt::PGP2Crypt::PGP5Crypt::GPG),以及基于公钥的签名算法的实现(《Crypt::DSA》)。


密码分析

不幸的是,我们隐含的假设是密文对伊芙来说是无用的,并不总是正确的。根据她可用的信息和资源,她可以尝试各种方法来恢复食谱。最简单的策略是猜测爱丽丝使用的密钥。这被称为暴力攻击,涉及到反复生成随机密钥并尝试用每个密钥解密密文。

这种方法的有效性取决于密钥的大小:密钥越长,可能的密钥就越多,平均需要猜测的次数也就越多,才能找到正确的密钥。因此,唯一的可能防御措施是使用足够长的密钥,使得密钥搜索在计算上变得不实际。

安全密钥有多长?56位的DES最近在不到一天的时间内被破解了,但128位的密钥空间(可能的密钥范围)仍然比这大4 * 10**21倍。尽管计算能力正在变得便宜,但似乎128位的密钥在未来许多年内都将是安全的,免受暴力攻击。

当然,还有更复杂的攻击方法,它们可能容易受到攻击。正如我们在ECB的描述中所看到的,密码分析者可以经常利用明文(长的签名、重复的短语)或密文(重复的块)中的模式,或者他们可能会寻找算法中的弱点(或利用已知的弱点)。通常,这些技术的组合可以减少潜在密钥空间,使得暴力攻击变得可行。

密码分析和密码技术是相辅相成的;新的加密算法被设计用来抵御旧攻击,而新的攻击尝试也在不断进行。因此,如果你认真对待长期保护你的数据,紧跟密码技术的最新进展是非常重要的。


更多资源

google.com

伟大的谷歌神知道足够的密码学相关材料,足以让你忙碌一段时间。

[email protected]

虽然目前这个perl-crypto邮件列表不太活跃,但它旨在讨论与Perl相关的所有方面的密码学(包括用户和开发人员级别)。

Crypt::Twofish

Crypt::Twofish模块是一个很好的例子,它受益于几个人的贡献,展示了如何编写可移植的加密算法实现。

标签

反馈

这篇文章有什么问题?请在GitHub上打开一个问题或拉取请求来帮助我们。