在PKCS#1 OAEP加密/解密中交换公钥/私钥

时间:2012-06-10 08:42:39

标签: encryption cryptography rsa public-key-encryption

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

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

对于测试实现,我将Python与PyCrypto一起使用。例如。 this是使用PKCS#1 OAEP的示例。

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

根据我对RSA如何工作的基本理解,我认为我可以交换公钥/私钥,即我可以使用公钥进行加密,使用私钥进行解密。例如。人X可以使用自己的私钥加密某些数据,公众可以使用公钥对其进行解密。如果解密工作正常,则可以提供某种证据证明数据来自人X.

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

从阅读PyCrypto源代码开始,在_RSAKey._decrypt函数(here)中,关键对象本身似乎知道它是私钥还是公钥,它们之间有所不同(令我惊讶的是) ,再次基于我对RSA的基本理解)。

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

但所有这些似乎都不打算以这种方式使用/黑客攻击。所以我想问一下我的误解。

另外,出于好奇,我生成了一些键(RSA.generate(2048))并查看了ned。在所有情况下,nd都非常庞大,而e在所有情况下常量(65537)(我不会指望)。< / p>

我想从这一切来说,我真的不应该只是交换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。)

这就是我认为我也可以使用私钥encryptdecrypt使用公钥。


可以找到(希望)正确签名/身份验证的最终实施here in binstruct

1 个答案:

答案 0 :(得分:16)

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

这一事实
m^(ed) congruent m (mod n)

通常标题为RSA加密的通常是操作

m^e mod n,

将消息提升到e-power,其中e是公钥。

然后解密

(m^e)^d mod n,

将加密消息提升为d次幂,其中d为私钥。现在,由于求幂的规则和乘法是可交换的(这些仍然在模运算中),我们有这个

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

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

你认为逆转会导致数字签名是正确的,因为每个人都可以使用公钥e验证(&#34;解密&#34;)签名,因此该消息只有在&#34时才是真实的;加密&#34; (签名)使用相应的私钥d。

事实证明,PyCrypto只是试图阻止你把一个误认为另一个,OpenSSL或Ruby OpenSSL允许你for example同时做两件事:public_encrypt / public_decrypt和private_encrypt / private_decrypt。

对于这个理论来说,现在为什么有充分的理由不让你互换使用它们。我刚刚描述的内容通常被称为&#34;教科书RSA&#34;它还远没有安全。需要注意额外的事情,以使结果在实践中可用。这就是为什么PyCrypto中有一个专用的signature package - 这有效地完成了你所描述的内容,但另外还会照顾我提到的内容。虽然了解这些事情是如何运作的,但我们应该总是在实践中使用这些软件包,因为他们已经制定并修复了我们在推销自己时可能会引入的错误。

至于为什么e总是65537.它实际上不一定是固定值,但通常选择的是一个非常小的数字,其二进制表示尽可能少1。 (65537是10001)。在过去,e = 3或e = 17也被选择,但在实践中被认为是不安全的,因为它们可以通过简单地取密文的第3或第17根来攻击。如果e = 3且m = 3,那么3 ^ 3是27,并且在密文为27的情况下,没有天才来确定m是3,而不管模数n(通常大得多)。因此,危险在于密文即使在取幂之后也不会跨越模数边界&#34;因此,我们可以简单地使用第e个根来获得原始消息。典型模数为1024 - 4096位,这不再是e = 65537的问题。

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

修改

你问我是否可以进一步解释像RSA-PSS这样的方案,以确保安全。

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

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

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

实践方面:

  • Applied Cryptography - 仍然是经典且值得一读的。一些安全人员说它很危险,因为它让人们相信他们知道自己能够编写自己的加密。但我想我们都是成年人,不是吗?能够感受到&#34;那里有什么&#34;

  • 仍然很棒
  • Cryptography Engineering - 有一些很好的实用建议,并在实施加密代码时提到了警告。

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

理论方面:

安全不仅仅是加密:

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

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