Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using an RSA Public Key to decrypt a string that was encrypted using RSA Private Key

I know the main answer I am likely to get is why the hell would you want to do that?!

Unfortunately despite my protests I have to do it, even though I know it makes little sense.

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.

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.

I can't seem to figure out how to do this. Am I being an idiot? Is this a normal thing to do?

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.

I am hoping someone might be able help me.

It would be great if there is some kind of reasonable solution besides begging them to change what they are doing.

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

"Error occurred while decoding OAEP padding."

If I use rsa.Decrypt(bytes, false) I get a bad key exception.

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;
    }

The two methods above InitRSAProvider and LoadRsaPublicKey were gotten out of tutorials to allow PEM keys as Strings to be used with .Net.

like image 857
Ruairi O'Brien Avatar asked Oct 21 '11 09:10

Ruairi O'Brien


People also ask

Can we use the public key to decrypt a RSA message encrypted by the private key that pairs with the public key?

Once it has been encrypted with a public key, it can only be decrypted by the private key from the same key pair. Even the same public key can't be used to decrypt the data.

Can public key decrypt message encrypted by private key?

Similarly, you can encrypt a message for anyone else by using their public key, and they decrypt it by using their private key. You can then send the message safely over an unsecured connection.

Can you decrypt with a public key RSA?

RSA(Rivest-Shamir-Adleman) is an Asymmetric encryption technique that uses two different keys as public and private keys to perform the encryption and decryption. With RSA, you can encrypt sensitive information with a public key and a matching private key is used to decrypt the encrypted message.


1 Answers

RSA is built into .NET: System.Security.Cryptography.RSA.

Encrypting using the public key and decrypting with the private key is one of the most common things people do with asymmetric algorithms, it allows anybody to send you something securely.

If you do it the other way: encrypt using the private key, and decrypt with the public key then it proves the message was sent by the holder of the private key. But because anyone presumably can get hold of the public key, people don't tend to encrypt the whole message, they instead just sign a hash of the data using the private key. Hence RSACryptoServiceProvider has Sign__ and Verify__ methods to do just that.

Still, there are Encrypt/Decrypt methods if your partner insists.

Saying that, I've found the Microsoft crypto classes a bit tricky to deal with and lacking in certain areas and much prefer the Bouncy Castle libraries.

like image 138
Duncan Smart Avatar answered Oct 21 '22 21:10

Duncan Smart