当前位置: 首页 > 知识库问答 >
问题:

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

东方华荣
2023-03-14

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

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

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

使用公钥加密,然后使用私钥解密,效果很好。例如,公众可以使用私钥向X个人发送一些数据。

从我对RSA工作原理的基本理解来看,我认为我可以交换公钥/私钥,也就是说,我可以使用公钥进行加密,使用私钥进行解密。例如,person X可以使用自己的私钥对某些数据进行加密,而公众可以使用公钥对其进行解密。如果解密工作正常,这将提供某种证明,证明数据来自X个人。

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

阅读PyCrypto源代码,在\rsau密钥中_decrypt函数(此处),密钥对象本身似乎知道它是私钥还是公钥,并且它们之间存在差异(让我惊讶的是,同样基于我对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

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

这就是我认为我也可以使用用私钥加密用公钥解密的地方。

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

共有1个答案

李和裕
2023-03-14

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

m^(ed) congruent m (mod n)

通常称为RSA encryption的操作通常是

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个根来得到原始消息。对于1024 - 4096位的典型模数,这不再是e=65537的问题。

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

编辑:

你问我是否能进一步解释像RSA-PSS这样的方案是为了安全。

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

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

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

实用的一面:

>

密码学工程-有一些很好的实用建议,还提到了在实现密码学代码时的注意事项。

应用密码学手册-它是免费的,仍然有很多好的建议,特别是关于实现。

理论方面:

>

密码学——理论和实践——这对我来说是一个游戏规则的改变者,我喜欢这本书。如果你只读过一本书,就让它成为这本吧:)

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

密码学基础I

安全不仅仅是加密:

>

  • 安全工程-有许多例子说明了正确的原则在实践中可能会出错

    信息安全-类似于安全工程,说明了安全范围比仅仅加密更广。

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

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

  •  类似资料:
    • 我正在升级一个平台上的安全。但是,我无法破解如何安全地存储我的加密/解密密钥。 当前安装程序: MySQL Server Webserver 我需要使用aes_encrypt/aes_decrypt存储和读取数据库中的所有敏感数据。但是,加密/解密数据的密钥我想要安全地存储。因此,当某人获得数据库或Web服务器的访问权时,他们将无法轻易猜出组合键或从配置文件中读取组合键。 密钥可以根据我需要读/写

    • 我想使用带有RSA算法的OpenSSL使用私钥加密文件: 现在,如果我执行解密操作: 此操作需要私钥 我知道我应该使用公钥进行加密,如果我使用私钥,我会得到一个签名。 然而,我想这样做是为了学习。

    • 我找到了几个可以使用的解决方案。Net RSA Provider使用公钥对消息进行加密,并使用私钥对其解密。 但我想要的是用私钥加密,用公钥解密。 我希望在我的应用程序中存储公钥,并使用私钥加密许可证,例如在我的开发人员计算机上,将其发送到应用程序,并让信息使用公钥解密。 我怎样才能做到这一点?

    • 并且我将这个函数称为用RSA公钥加密DSA密钥的函数:

    • 我找到了几种解决方案,在这些方案中,我可以使用RSA提供程序用公钥加密消息,并用私有密钥解密消息。 但我想要的是用私钥加密,用公钥解密。 我想将公钥存储在我的应用程序中,并用私钥在我的dev机器上加密一个许可证,将其发送到应用程序,并让信息用公钥解密。

    • 我被告知,对于非对称密码学,您使用公钥加密明文,并使用私钥解密明文。所以我尝试了以下方法: 以及加密和解密函数 我希望控制台显示,但它显示的是这个。我是否错误地使用了RSACryptoServiceProvider?