PKCS#1 OAEP加密/解密中的交换公钥/私钥 [英] exchange public/private key in PKCS#1 OAEP encryption/decryption

查看:2830
本文介绍了PKCS#1 OAEP加密/解密中的交换公钥/私钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



在阅读不同的来源关于如何使用它在实践中,似乎PKCS#1 OAEP将一个好东西。



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



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



从我对RSA如何工作的基本了解,我认为我可以交换公钥/私钥,即我可以使用公钥进行加密,使用私钥进行解密。例如。人X可以使用其自己的私钥对一些数据进行加密,并且公众可以使用公钥对其进行解密。如果解密工作正常,这给出了一些证据,证明数据来自人X。



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

从读取PyCrypto源代码,在 _RSAKey._decrypt 函数(这里),似乎key对象本身知道它是私钥还是公钥并且在他们之间不同(令我惊讶的是,再次基于我非常基本的RSA理解)。



从这里,它看起来像我可以黑客解密函数,公钥。或者稍微不同:我可以在关键对象中交换公共指数 e 和私有指数 d p>

但这一切似乎都不是为了这样使用/黑客。



此外,出于好奇,我生成了一些键( RSA.generate(2048))并查看 n e d 。在所有情况下, n d 非常巨大,而 e 在所有情况下常数(65537)(我不会预料到的)。



只需交换 e d



我想我应该使用一些其他方法签名像PKCS1_PSS。






一些代码的加密/解密

  def randomString(l):
import random
return''.join (l)中的i的random.randint(0,0xFF))

def genkeypair():
来自Crypto.PublicKey import RSA
key = RSA.generate (2048)
pubkey = key.publickey()。exportKey(DER)
privkey = key.exportKey(DER)
return(pubkey,privkey)
b $ b def encrypt(v,rsapubkey):
来自Crypto.PublicKey import RSA
rsakey = RSA.importKey(rsapubkey)
来自Crypto.Cipher import PKCS1_OAEP
rsa = PKCS1_OAEP .new(rsakey)
import binstruct
从数组import array
aeskey = randomString(32)
iv = randomString(16)
来自Crypto.Cipher import AES
aes = AES.new(aeskey,AES.MODE_CBC,iv)
data = binstruct.varEncode(v)
data + = array(B,(0, (data)%16))
out = binstruct.strEncode(rsa.encrypt(aeskey + iv))
out + = array(B,aes.encrypt(data))
return stream

def decrypt(stream,rsaprivkey):
从数组import数组
从StringIO import StringIO
如果isinstance(stream,array):stream = stream。 tostring()
if isinstance(stream,str):stream = StringIO(stream)
来自Crypto.PublicKey import RSA
rsakey = RSA.importKey(rsaprivkey)
来自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:]
来自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 ([self.read1()for i in range(n)])
v = binstruct.varDecode(Stream())
return v

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



是我认为我也可以用私钥使用 encrypt 和使用公钥使用 decrypt p>




最终实现(希望)正确的签名/身份验证可以在这里在binstruct

解决方案

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

  m ^(ed)congruent m(mod n)



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

  m ^ e mod n,





 (m ^ e)^ d mod n,

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

  m全等(m ^ e)^ d全等m ^(ed)全等m ^(de)全等(m ^ d)^ e,

$ b b

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



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



事实证明,PyCrypto只是试图阻止你错误的对另一个这里,OpenSSL或Ruby OpenSSL允许您例如< a>要同时做两个: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,并且不考虑模数n(其通常大得多),无需知道m是3,而密文是27。因此,危险在于密文,即使在求幂之后,不穿过模数边界,因此允许我们简单地取第e根以到达原始消息。典型模数为1024 - 4096位,这不再是e = 65537的问题。



二进制表示中的几个1也适用于计算m ^ e fast。模数幂运算通常使用乘法和平方算法实现,性能最适合具有少数1的小e。为什么选择这种方式,而不是相反的方式,例如有一个小d与几个1?对于初学者来说,d会更容易猜到这种方式。第二个优点是,通过数字签名,您通常对文档签名一次,但经常进行验证。这意味着m ^ d被执行一次,但是m ^ e经常,所以你有共同的任务执行最好,而罕见的任务被允许执行穷。



编辑:



您问我是否可以进一步解释什么样的方案,如RSA-PSS做,以确保安全。



当比较OAEP用于加密和PSS签名时,两者看起来非常相似。事实上,他们都是在过程中引入随机化,这允许 OAEP PSS 。我还发现这篇论文很有帮助。可靠的安全性是比老式PKCS 1.5加密和签名更大的优势,可以证明在相同的假设(关键点:没有确定性方案可以,随机化是必不可少的)下证明是安全的。所提出的签名和加密方案之间的明显差异是签名方案总是强制要被签名的消息首先被散列。这不仅在效率方面是有意义的,而且还防止了否则可能的一些攻击。我想,这导致了为什么我们应该总是使用签名方案的签名和加密的加密方案的要点:提出的方案附带安全证明,我们的手工方案不。



密码学家发明这些计划,使我们的生活更简单 - 他们给我们的工具,理想地允许没有滥用或误用,通过减少选项的数量到最小。例如,即使您设法使用RSA-OAEP提供了一个好的签名方案,使用它的人可能不知道为什么应用签名之前首先散列它们的消息。这种滥用甚至不是RSA-PSS的可能性。



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



实用方面:




  • 应用密码术 - 仍然是一个经典和值得阅读。一些安全人员说这是危险的,因为它导致人们相信他们知道足够写自己的密码。但我想我们都是成年人,不是吗?



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




理论侧:




  • 现代密码术 - 它是理论和实践之间的混合体,并且有很多洞察力


  • 密码术 - 理论与练习 - 这是一个改变我的游戏规则,我爱这本书。如果你只读过一本书,那就是这本书:)


  • 现代密码学介绍 - 在解释现代方法以及安全证据如何实际工作和根据哪些假设方面做得很好。


  • 密码学基础I& II - 如果在上一本书之后,您仍然无法获得足够的的单向功能和朋友的理论,这是你的书。非常技术性。




安全性不仅仅是加密技术:




  • 安全工程 - 有许多例子说明合理的原则可能出错练习


  • 信息安全 - 类似于安全工程,用于说明比加密技术更广泛的安全性。




除此之外,日期通过阅读最近的文章关于新的攻击,技术等我发现 r / netsec 非常有帮助,以及以下研究人员和Twitter上的从业者,他们定期发布有趣的材料。



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


I only have some very rudimentary theoretical knowledge about RSA.

While reading different sources about how to use it in practice, it seemed that PKCS#1 OAEP would be a good thing.

For a test implementation, I use Python with PyCrypto. E.g. this is an example using PKCS#1 OAEP.

Encrypting using the public key and then decrypting using the private key works fine. E.g. the public can send some data to person X with the private key.

From my basic understanding of how RSA works, I thought that I can just interchange the public/private key, i.e. I can use the public key for encrypting and the private key for decrypting. E.g. person X can encrypt some data with its own private key and the public can decrypt it using the public key. If the decryption works fine, this gives some sort of proof that the data is coming from person X.

PyCrypto complains when I try to decrypt using the public key.

From reading the PyCrypto source code, in the _RSAKey._decrypt function (here), it seems that the key object itself knows if it is the private or public key and differs between them (to my surprise, again based on my very basic RSA understanding).

From there, it looks like I could hack the decrypt function so that it uses the public key. Or somewhat differently: I could just interchange the public exponent e and the private exponent d in the key objects.

But all this seems like it is not intended to be used/hacked this way. So I wanted to ask here about my misunderstandings.

Also, just out of curiosity, I generated some keys (RSA.generate(2048)) and looked at n, e and d. In all cases, n and d was very huge while e was in all cases constant (65537) (I wouldn't have expected that).

I guess from all this that I really shouldn't just interchange e and d.

So I guess I should use some other method for the signature like PKCS1_PSS.


Some code for the encrypting/decrypting, if anyone is interested:

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 is a small module which can encode/decode tree data structures - similar to JSON/BSON.)

That is where I thought I could just also use encrypt with the private key and and decrypt with the public key.


The final implementation with (hopefully) correct signing/authentication can be found here in binstruct.

解决方案

Your general understanding about interchanging the roles of public and private key is correct. In the end, RSA is based on the fact that

m^(ed) congruent m (mod n)

What is normally titled RSA encryption is typically the operation

m^e mod n,

raising the message to the e-th power where e is the public key.

Decryption is then

(m^e)^d mod n,

raising the encrypted message to the d-th power with d being the private key. Now because of the rules of exponentiation and the fact that multiplication is commutative (these still hold in modular arithmetic) we have that

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

and therefore you get the same result if you apply the operations in the reverse order.

You were right in assuming that the reversal leads to digital signatures, because everybody can verify ("decrypt") the signature with the public key e, so the message was authentic only if it was "encrypted" (signed) using the corresponding private key d.

As it turns out, PyCrypto is only trying to prevent you from mistaking one for the other here, OpenSSL or Ruby OpenSSL allow you for example to do both: public_encrypt/public_decrypt and private_encrypt/private_decrypt.

So much for the theory, now to why there is good reason for not letting you use them interchangeably. What I just described is often referred to as "textbook RSA" and it is still far from being secure. Additional things need to be taken care of to make the result usable in practice. And that's why there is a dedicated signature package in PyCrypto - this effectively does what you described, but also additionally takes care of the things I mentioned. While it is good for our understanding to know how these things work, we should always use such packages in practice because they already made and fixed the mistakes we would probably introduce when rolling our own.

As to why e is always 65537. It doesn't really have to be a fixed value, but it is commonly chosen to be a very small number with as few 1's in its binary representation as possible (65537 is 10001). In the past, e=3 or e=17 were also chosen, but were considered as not safe in practice because they could be attacked by simply taking the 3rd or 17th root of the ciphertext. If e=3 and m=3, then 3^3 is 27, and it takes no genius to figure out that m is 3 given that the ciphertext is 27, regardless of the modulus n (which is typically much larger). So the danger lies in the fact that the ciphertext, even after exponentiation, does not cross "the modulus boundary" and therefore allows us to simply take the e-th root to arrive at the original message. With typical moduli of 1024 - 4096 bits, this is no longer an issue with e=65537.

Few 1's in the binary representation are also good for computing m^e fast. Modular exponentiation is often implemented using a Multiply and Square algorithm, and performance is best for small e's with few 1's. Why is it chosen this way and not the other way round, for example having a small d with few 1's? Well for starters, d would be easier to guess that way. A second advantage is that with digital signatures, you typically sign a document once but verify it often. This means m^d is performed once but m^e often, so you have the common task perform best while the rare task is allowed to perform poor.

Edit:

You asked whether I could further explain what schemes like RSA-PSS do in order to be secure.

When comparing what OAEP does for encryption and what PSS does for signatures, the two look pretty similar. And in fact they are, they both introduce randomization in the process, which allows for provable security of OAEP and PSS under certain assumptions. I also found this paper to be helpful. Provable security is a big advantage over old-school PKCS 1.5 encryption and signatures, which can be shown to be not provably secure under the same assumptions (key point: no deterministic scheme can be, randomization is essential). An obvious difference between the proposed signature and encryption schemes is that the signature schemes always mandate the to-be-signed message to be hashed first. This makes sense not only with regard to efficiency, but it also prevents some attacks that would otherwise be possible. And I guess that leads to the gist of why we should always use signature schemes for signatures and encryption schemes for encryption: the proposed schemes come with security proofs attached, our handmade schemes don't.

Cryptographers invent those schemes to make the lives of us mere mortals easier - they give us tools that ideally allow for no abuse or misuse by reducing the number of options to a minimum. For example, even if you managed to come up with a good signature scheme using RSA-OAEP, somebody who uses it might not know about why they should hash their messages first before applying the signature. That kind of misuse is not even a possibility with RSA-PSS.

You also asked about some good reading material. Although this is a very subjective topic, I really enjoyed these:

The practical side:

  • Applied Cryptography - still a classic and worth reading. Some security people say it is dangerous because it leads people to believing they know enough to write their own crypto. But I guess we are all grown-ups, aren't we? It's still great to get a feeling about "what's out there"

  • Cryptography Engineering - Has some good practical advice and also mentions the caveats when implementing cryptography code.

  • Handbook of Applied Cryptography - It's free and still has a lot of good advice especially with regard to implementations.

The theoretical side:

  • Modern Cryptography - It's a hybrid between theory and practice and has a lot of insight how things can go wrong in practice.

  • Cryptography - Theory and Practice - this was a game changer for me, I love this book. If you only ever read one book, let it be this one :)

  • Introduction to Modern Cryptography - does a great job at explaining "the modern approach" and how the security proofs actually work and under which assumptions.

  • Foundations of Cryptography I&II - if after the previous book you still can't get enough of the theory of one-way functions and friends, this is your book. Very technical.

Security is not only cryptography:

  • Security engineering - has numerous examples how sound principles can go wrong in practice

  • Information Security - Similar to Security Engineering, illustrating security in a scope wider than just cryptography.

Apart from that, I try to keep up to date by reading recent papers about new attacks, technologies etc. I found r/netsec very helpful, as well as following researchers and practitioners on Twitter, they post interesting material regularly.

Finally, if you have the time, take the Cryptography courses on Coursera and Udacity! I think they'll start over in the next few weeks, they are really great, I'm sure you won't regret it. They had a lot of practical exercises that are a lot of fun and nicely illustrate various ways to attack cryptography implementations.

这篇关于PKCS#1 OAEP加密/解密中的交换公钥/私钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆