9

我只有一些关于 RSA 的非常基本的理论知识。

在阅读有关如何在实践中使用它的不同来源时,PKCS#1 OAEP 似乎是一件好事。

对于测试实现,我使用 Python 和 PyCrypto。例如,这是一个使用 PKCS#1 OAEP 的示例。

使用公钥加密然后使用私钥解密可以正常工作。例如,公众可以使用私钥向 X 人发送一些数据。

根据我对 RSA 工作原理的基本了解,我认为我可以互换公钥/私钥,即我可以使用公钥进行加密,使用私钥进行解密。例如,某人 X 可以使用自己的私钥加密某些数据,而公众可以使用公钥对其进行解密。如果解密工作正常,这可以证明数据来自 X 人。

当我尝试使用公钥解密时,PyCrypto 会抱怨。

通过阅读 PyCrypto 源代码,在_RSAKey._decrypt函数(此处)中,似乎密钥对象本身知道它是私钥还是公钥并且它们之间存在差异(令我惊讶的是,再次基于我非常基本的 RSA 理解)。

从那里,看起来我可以破解解密功能,以便它使用公钥。或者有些不同:我可以在关键对象中交换公共指数e和私有指数。d

但这一切似乎并不打算以这种方式使用/被黑客入侵。所以我想在这里问一下我的误解。

另外,出于好奇,我生成了一些键 ( RSA.generate(2048)) 并查看了n,ed. 在所有情况下,n并且d非常巨大,而e在所有情况下都是恒定的(65537)(我没想到会这样)。

我想从这一切来看,我真的不应该只是交换ed.

所以我想我应该使用其他方法来签名,比如 PKCS1_PSS。


如果有人感兴趣,一些用于加密/解密的代码:

def randomString(l):
    import random
    return ''.join(chr(random.randint(0, 0xFF)) for i in range(l))

def genkeypair():
    from Crypto.PublicKey import RSA
    key = RSA.generate(2048)
    pubkey = key.publickey().exportKey("DER")
    privkey = key.exportKey("DER")
    return (pubkey,privkey)

def encrypt(v, rsapubkey):
    from Crypto.PublicKey import RSA
    rsakey = RSA.importKey(rsapubkey)
    from Crypto.Cipher import PKCS1_OAEP
    rsa = PKCS1_OAEP.new(rsakey)
    import binstruct
    from array import array
    aeskey = randomString(32)
    iv = randomString(16)
    from Crypto.Cipher import AES
    aes = AES.new(aeskey, AES.MODE_CBC, iv)
    data = binstruct.varEncode(v)
    data += array("B", (0,) * (-len(data) % 16))
    out = binstruct.strEncode(rsa.encrypt(aeskey + iv))
    out += array("B", aes.encrypt(data))
    return out

def decrypt(stream, rsaprivkey):
    from array import array
    from StringIO import StringIO
    if isinstance(stream, array): stream = stream.tostring()
    if isinstance(stream, str): stream = StringIO(stream)
    from Crypto.PublicKey import RSA
    rsakey = RSA.importKey(rsaprivkey)
    from Crypto.Cipher import PKCS1_OAEP
    rsa = PKCS1_OAEP.new(rsakey)
    import binstruct
    aesdata = binstruct.strDecode(stream)
    aesdata = rsa.decrypt(aesdata)
    aeskey = aesdata[0:32]
    iv = aesdata[32:]
    from Crypto.Cipher import AES
    aes = AES.new(aeskey, AES.MODE_CBC, iv)
    class Stream:
        buffer = []
        def read1(self):
            if len(self.buffer) == 0:
                nextIn = stream.read(16)
                self.buffer += list(aes.decrypt(nextIn))
            return self.buffer.pop(0)
        def read(self, n):
            return "".join([self.read1() for i in range(n)])
    v = binstruct.varDecode(Stream())
    return v

binstruct是一个可以编码/解码树数据结构的小模块 - 类似于 JSON/BSON。)

这就是我认为我也可以使用encrypt私钥和decrypt公钥的地方。


(希望)正确签名/身份验证的最终实现可以在 binstruct 中找到。

4

1 回答 1

16

您对交换公钥和私钥角色的一般理解是正确的。最后,RSA 是基于以下事实:

m^(ed) congruent m (mod n)

通常称为 RSA 加密的通常是操作

m^e mod n,

将消息提升到 e 次方,其中 e 是公钥。

然后解密

(m^e)^d mod n,

将加密消息提高到 d 次方,其中 d 是私钥。现在由于取幂规则和乘法是可交换的事实(这些仍然在模算术中成立),我们有

m congruent (m^e)^d congruent m^(ed) congruent m^(de) congruent (m^d)^e,

因此,如果您以相反的顺序应用这些操作,您会得到相同的结果。

您假设反转导致数字签名是正确的,因为每个人都可以使用公钥 e 验证(“解密”)签名,因此只有使用相应的私钥“加密”(签名)消息才是真实的d。

事实证明,PyCrypto 只是试图防止您在这里误认为另一个,OpenSSL 或 Ruby OpenSSL 允许您同时执行以下操作:public_encrypt/public_decrypt 和 private_encrypt/private_decrypt。

理论讲了这么多,现在为什么有充分的理由不让你互换使用它们。我刚才描述的通常被称为“教科书 RSA”,它仍然远非安全。需要注意其他事项以使结果在实践中可用。这就是为什么 PyCrypto 中有一个专用的签名包的原因——这有效地完成了你所描述的事情,而且还处理了我提到的事情。虽然了解这些东西是如何工作的对我们的理解有好处,但我们应该始终在实践中使用这些包,因为它们已经犯了并修复了我们在推出自己的包时可能会引入的错误。

至于为什么 e 总是 65537。它实际上不一定是一个固定值,但通常选择它是一个非常小的数字,其二进制表示中的 1 尽可能少(65537 是 10001)。在过去,也选择了 e=3 或 e=17,但在实践中被认为不安全,因为它们可以通过简单地获取密文的第 3 或第 17 根来进行攻击。如果 e=3 且 m=3,则 3^3 为 27,并且考虑到密文为 27,无论模数 n(通常大得多)如何,都不需要天才就能算出 m 为 3。所以危险在于密文,即使在取幂之后,也不会跨越“模边界”,因此允许我们简单地取 e-th 根来得到原始消息。对于 1024 - 4096 位的典型模数,这不再是 e=65537 的问题。

二进制表示中很少有 1 也有利于快速计算 m^e。模幂运算通常使用乘法和平方算法来实现,对于带有几个 1 的小 e 的性能最好。为什么选择这种方式而不是相反,例如有一个小的 d 和几个 1?对于初学者来说,d 会更容易猜到。第二个优点是,使用数字签名,您通常对文档进行一次签名,但经常对其进行验证。这意味着 m^d 执行一次但 m^e 经常执行,因此您可以让常见任务执行得最好,而允许稀有任务执行得较差。

编辑:

你问我是否可以进一步解释像 RSA-PSS 这样的方案为了安全而做了什么。

在比较 OAEP 的加密功能和 PSS 的签名功能时,两者看起来非常相似。事实上,它们都在过程中引入了随机化,这使得在某些假设下可以证明OAEPPSS的安全性。我也找到了这篇论文乐于助人。与老式的 PKCS 1.5 加密和签名相比,可证明的安全性是一个很大的优势,在相同的假设下,它可以被证明是不可证明的安全性(关键点:没有确定性方案可以,随机化是必不可少的)。提议的签名和加密方案之间的一个明显区别是签名方案总是要求首先对要签名的消息进行哈希处理。这不仅在效率方面有意义,而且还可以防止一些原本可能发生的攻击。而且我想这导致了为什么我们应该始终使用签名方案进行签名和加密方案进行加密的要点:建议的方案附带安全证明,而我们的手工方案没有。

密码学家发明了这些方案,使我们这些凡人的生活变得更轻松——它们为我们提供了理想的工具,通过将选项数量减少到最低限度来避免滥用或误用。例如,即使您设法使用 RSA-OAEP 提出了一个好的签名方案,使用它的人可能不知道为什么他们应该在应用签名之前先散列他们的消息。RSA-PSS 甚至不可能发生这种滥用。

您还询问了一些好的阅读材料。虽然这是一个非常主观的话题,但我真的很喜欢这些:

实用的一面:

  • 应用密码学——仍然是经典,值得一读。一些安全人员说这很危险,因为它使人们相信他们知道的足够多,可以编写自己的加密货币。但我想我们都是成年人,不是吗?对“外面有什么”有一种感觉仍然很棒

  • Cryptography Engineering - 有一些很好的实用建议,并且在实现加密代码时也提到了注意事项。

  • Handbook of Applied Cryptography - 它是免费的,并且仍然有很多好的建议,尤其是在实现方面。

理论方面:

  • 现代密码学——它是理论与实践的混合体,对实践中事情如何出错有很多洞察力。

  • 密码学 - 理论与实践- 这对我来说改变了游戏规则,我喜欢这本书。如果你只读过一本书,那就是这本书吧:)

  • 现代密码学简介- 在解释“现代方法”以及安全证明如何实际工作以及在哪些假设下工作方面做得很好。

  • Foundations of Cryptography I&II - 如果在上一本书之后您仍然无法获得足够的单向函数理论和朋友,那么这就是您的书。很有技术含量。

安全不仅仅是密码学:

  • 安全工程- 有大量示例如何合理的原则在实践中会出错

  • 信息安全- 类似于安全工程,在比密码学更广泛的范围内说明安全。

除此之外,我尝试通过阅读有关新攻击、新技术等的最新论文来了解最新情况。我发现r/netsec非常有帮助,并且在 Twitter 上关注研究人员和从业者,他们会定期发布有趣的材料。

最后,如果您有时间,请参加 Coursera 和 Udacity 上的密码学课程!我想他们会在接下来的几周内重新开始,他们真的很棒,我相信你不会后悔的。他们有很多有趣的实践练习,很好地说明了攻击密码学实现的各种方法。

于 2012-06-10T17:01:27.737 回答