首页 文章

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

提问于
浏览
7

我对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)(我不会预料到这一点) .

我想从这一切我真的不应该只是交换 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 .


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

1 回答

  • 16

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

    m^(ed) congruent m (mod n)
    

    通常 Headers 为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验证(“解密”)签名,因此只有在使用相应的私钥“加密”(签名)时,该消息才是真实的 . d .

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

    这个理论非常重要,现在为什么有充分的理由不让你互换使用它们 . 我刚才所描述的通常被称为"textbook RSA",它仍然远非安全 . 需要注意额外的事情,以使结果在实践中可用 . 这就是为什么在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(通常大得多) . 因此,危险在于密文,即使在取幂之后,也不会越过“模数边界”,因此我们可以简单地将第e个根带到原始消息 . 典型模数为1024 - 4096位,这不再是e = 65537的问题 .

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

    Edit:

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

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

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

    你还问过一些好的阅读材料 . 虽然这是一个非常主观的话题,但我真的非常喜欢这些:

    实用方面:

    • Applied Cryptography - 仍然是经典而值得一读的 . 一些安全人员说它很危险,因为它让人们相信他们知道自己能够编写自己的加密 . 但我想我们都是成年人,对于"what's out there"感觉仍然很棒

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

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

    理论方面:

    安全性不仅仅是加密:

    除此之外,我尝试通过阅读有关新攻击,技术等的最新论文来了解最新情况 . 我发现r/netsec非常有帮助,以及关注推特上的研究人员和从业者,他们发布了有趣的资料经常 .

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

相关问题