title: Using an RSA Public Key to decrypt a string that was encrypted using RSA Private Key
body: <p>I know the main answer I am likely to get is why the hell would you want to do that?! </p>
  
  <p>Unfortunately despite my protests I have to do it, even though I know it makes little sense.</p>
  
  <p>I have functions written in .Net to decrypt using a private key, encrypt using a public key.
  I also RSA sign and verify and have a reasonable understanding of how this all work I think.</p>
  
  <p>I am now being sent a value that is RSA encrypted using a private key which I am supposed to derive a usable value by decrypting using the public key.</p>
  
  <p>I can't seem to figure out how to do this. Am I being an idiot? Is this a normal thing to do?</p>
  
  <p>I am told by the person sending me the value that this is no problem in PHP. I don't know and haven't used PHP yet. I can't find a library to do it in any of the main languages I know i.e. C++, Java, C#. The server I am working on uses .Net.</p>
  
  <p>I am hoping someone might be able help me. </p>
  
  <p>It would be great if there is some kind of reasonable solution besides begging them to change what they are doing.</p>
  
  <p>This is my method (updated from my previous bad one as pointed out by Iridium) but when I try to decrypt the value I get an exception </p>
  
  <p>"Error occurred while decoding OAEP padding."</p>
  
  <p>If I use rsa.Decrypt(bytes, false) I get a bad key exception.</p>
  
  <pre><code>public static string DecryptUsingPublic(string dataEncrypted, string publicKey)
      {
          if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted");
          if (publicKey == null) throw new ArgumentNullException("publicKey");
          try
          {
              RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false);
              RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey);
  
              byte[] bytes = Convert.FromBase64String(dataEncrypted);
              byte[] decryptedBytes = rsa.Decrypt(bytes, true);
  
              ArrayList arrayList = new ArrayList();
              arrayList.AddRange(decryptedBytes);
  
             return Encoding.UTF8.GetString(decryptedBytes);
          }
          catch
          {
              return null;
          }
      }
  
      private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile)
      {
          RSAParameters RSAKeyInfo = new RSAParameters();
          byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile);
          byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
          byte[] seq = new byte[15];
          // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
          MemoryStream mem = new MemoryStream(pubkey);
          BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
          byte bt = 0;
          ushort twobytes = 0;
  
          try
          {
  
              twobytes = binr.ReadUInt16();
              if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                  binr.ReadByte();    //advance 1 byte
              else if (twobytes == 0x8230)
                  binr.ReadInt16();   //advance 2 bytes
              else
                  return RSAKeyInfo;
  
              seq = binr.ReadBytes(15);       //read the Sequence OID
              if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
                  return RSAKeyInfo;
  
              twobytes = binr.ReadUInt16();
              if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
                  binr.ReadByte();    //advance 1 byte
              else if (twobytes == 0x8203)
                  binr.ReadInt16();   //advance 2 bytes
              else
                  return RSAKeyInfo;
  
              bt = binr.ReadByte();
              if (bt != 0x00)     //expect null byte next
                  return RSAKeyInfo;
  
              twobytes = binr.ReadUInt16();
              if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                  binr.ReadByte();    //advance 1 byte
              else if (twobytes == 0x8230)
                  binr.ReadInt16();   //advance 2 bytes
              else
                  return RSAKeyInfo;
  
              twobytes = binr.ReadUInt16();
              byte lowbyte = 0x00;
              byte highbyte = 0x00;
  
              if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
                  lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
              else if (twobytes == 0x8202)
              {
                  highbyte = binr.ReadByte(); //advance 2 bytes
                  lowbyte = binr.ReadByte();
              }
              else
                  return RSAKeyInfo;
              byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
              int modsize = BitConverter.ToInt32(modint, 0);
  
              byte firstbyte = binr.ReadByte();
              binr.BaseStream.Seek(-1, SeekOrigin.Current);
  
              if (firstbyte == 0x00)
              {   //if first byte (highest order) of modulus is zero, don't include it
                  binr.ReadByte();    //skip this null byte
                  modsize -= 1;   //reduce modulus buffer size by 1
              }
  
              byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes
  
              if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
                  return RSAKeyInfo;
              int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
              byte[] exponent = binr.ReadBytes(expbytes);
  
  
              RSAKeyInfo.Modulus = modulus;
              RSAKeyInfo.Exponent = exponent;
  
              return RSAKeyInfo;
          }
          catch (Exception)
          {
              return RSAKeyInfo;
          }
  
          finally { binr.Close(); }
          //return RSAparams;
  
      }
  
   private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam)
      {
          //
          // Initailize the CSP
          //   Supresses creation of a new key
          //
          CspParameters csp = new CspParameters();
          //csp.KeyContainerName = "RSA Test (OK to Delete)";
  
          const int PROV_RSA_FULL = 1;
          csp.ProviderType = PROV_RSA_FULL;
  
          const int AT_KEYEXCHANGE = 1;
          // const int AT_SIGNATURE = 2;
          csp.KeyNumber = AT_KEYEXCHANGE;
          //
          // Initialize the Provider
          //
          RSACryptoServiceProvider rsa =
            new RSACryptoServiceProvider(csp);
          rsa.PersistKeyInCsp = false;
  
          //
          // The moment of truth...
          //
          rsa.ImportParameters(rsaParam);
          return rsa;
      }
  
      private static int GetIntegerSize(BinaryReader binr)
      {
          byte bt = 0;
          byte lowbyte = 0x00;
          byte highbyte = 0x00;
          int count = 0;
          bt = binr.ReadByte();
          if (bt != 0x02)     //expect integer
              return 0;
          bt = binr.ReadByte();
  
          if (bt == 0x81)
              count = binr.ReadByte();    // data size in next byte
          else
              if (bt == 0x82)
              {
                  highbyte = binr.ReadByte(); // data size in next 2 bytes
                  lowbyte = binr.ReadByte();
                  byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                  count = BitConverter.ToInt32(modint, 0);
              }
              else
              {
                  count = bt;     // we already have the data size
              }
  
          while (binr.ReadByte() == 0x00)
          {   //remove high order zeros in data
              count -= 1;
          }
          binr.BaseStream.Seek(-1, SeekOrigin.Current);       //last ReadByte wasn't a removed zero, so back up a byte
          return count;
      }
  
      private static bool CompareBytearrays(byte[] a, byte[] b)
      {
          if (a.Length != b.Length)
              return false;
          int i = 0;
          foreach (byte c in a)
          {
              if (c != b[i])
                  return false;
              i++;
          }
          return true;
      }
  </code></pre>
  
  <p>The two methods above InitRSAProvider and LoadRsaPublicKey were gotten out of tutorials to allow PEM keys as Strings to be used with .Net.</p>