title: exchange public/private key in PKCS#1 OAEP encryption/decryption
body: <p>I only have some very rudimentary theoretical knowledge about RSA.</p>
  
  <p>While reading different sources about how to use it in practice, it seemed that PKCS#1 OAEP would be a good thing.</p>
  
  <p>For a test implementation, I use Python with PyCrypto. E.g. <a href="https://www.dlitz.net/software/pycrypto/api/current/Crypto.Cipher.PKCS1_OAEP-module.html" rel="noreferrer">this</a> is an example using PKCS#1 OAEP.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>PyCrypto complains when I try to decrypt using the public key.</p>
  
  <p>From reading the PyCrypto source code, in the <code>_RSAKey._decrypt</code> function (<a href="https://github.com/dlitz/pycrypto/blob/master/lib/Crypto/PublicKey/_slowmath.py" rel="noreferrer">here</a>), 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).</p>
  
  <p>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 <code>e</code> and the private exponent <code>d</code> in the key objects.</p>
  
  <p>But all this seems like it is not intended to be used/hacked this way. So I wanted to ask here about my misunderstandings.</p>
  
  <p>Also, just out of curiosity, I generated some keys (<code>RSA.generate(2048)</code>) and looked at <code>n</code>, <code>e</code> and <code>d</code>. In all cases, <code>n</code> and <code>d</code> was very huge while <code>e</code> was in all cases <em>constant</em> (65537) (I wouldn't have expected that).</p>
  
  <p>I guess from all this that I really shouldn't just interchange <code>e</code> and <code>d</code>.</p>
  
  <p>So I guess I should use some other method for the signature like PKCS1_PSS.</p>
  
  <hr>
  
  <p>Some code for the encrypting/decrypting, if anyone is interested:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>(<a href="https://github.com/albertz/binstruct/blob/master/binstruct.py" rel="noreferrer"><code>binstruct</code></a> is a small module which can encode/decode tree data structures - similar to JSON/BSON.)</p>
  
  <p>That is where I thought I could just also use <code>encrypt</code> with the private key and and <code>decrypt</code> with the public key.</p>
  
  <hr>
  
  <p>The final implementation with (hopefully) correct signing/authentication can be found <a href="https://github.com/albertz/binstruct/blob/master/binstruct.py" rel="noreferrer">here in binstruct</a>.</p>