1

I've got a private key stored on my PHP server. A message is encrypted using the private key on the PHP server. My C# code is meant to receive the ciphertext from PHP's response and decrypt it with the public key. However, I keep running into errors such as

Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Key does not exist.'

My PHP code is as follows:

    $pkey = openssl_pkey_get_private("file://pkey");
    openssl_private_encrypt($text, $ciphertext, $pkey, OPENSSL_PKCS1_PADDING);
    echo base64_encode($ciphertext);

My C# code includes an implementation of the function mentioned in this thread, where the answer has a comment linking to Github. A snippet is below:

  string pubkey = "M...Q==";
  RSA rsa = CreateRsaProviderFromPublicKey(pubkey);
  byte[] bytes = Convert.FromBase64String(dataEncrypted);
  byte[] decryptedBytes = rsa.Decrypt(bytes, RSAEncryptionPadding.Pkcs1);
  return Encoding.UTF8.GetString(decryptedBytes);

The error occurs on the line where rsa.Decrypt is being called. I obtained the public key from the PHP function openssl_pkey_get_details, where I then stripped the ----BEGIN PUBLIC KEY----- header and footer and removed all spaces.

Edit 1: Perhaps I should post the code I used to make the private key, since that could be problematic:

$config = array(
  "digest_alg" => "sha512",
  "private_key_bits" => 4096,
  "private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$res = openssl_pkey_new($config);

Edit 2: I tried using sha256 with 2048 bytes for a new key, it made no difference.

Bobdabiulder
  • 160
  • 3
  • 12
  • 1
    Just a note - the **encryption** has to be done with the **Public key** of the recipient, the **decryption** (here with C#-platform) is done with the **Private key** of the recipient, not the other way round. It is possible only on some systems but won't run e.g. on Java platforms. – Michael Fehr Dec 29 '20 at 07:16
  • Thanks for the reply. I’ll try that tomorrow (it’s late here). It seems strange to think that the PHP server would need to keep the public key secret, while the client (which is public) will carry the private key. Hopefully that’s not a problem for security. – Bobdabiulder Dec 29 '20 at 07:31
  • 1
    Think of this situation: you want me to send you some secret data - for that you publish your **public key** on your website, newspaper or send it via Email to me. I will **encrypt** the data with **your public key** and send the encrypted data to you. The only one who can/should decrypt the data is the one (you) having access to **your private key** [that's why it's called "private"]. B.t.w.: a digital signature works the other way round: the signing is done with the private key of the signer, the verification is done by the published public key. Greetings to USA from Germany :-) – Michael Fehr Dec 29 '20 at 10:33
  • Greetings from the USA! So to be clear, for this purpose, the PHP code will need one of the keys so it can encrypt data, and the C# will need the other key for decryption. The C# code will be distributed widely, but I assume it makes no difference in terms of security whether the key held secretly is the private key or the public key? – Bobdabiulder Dec 29 '20 at 14:20

2 Answers2

1

Let me please explain the complete workflow for RSA encryption with 3 small, simple programs. I'm running all programs in Online compiler so you can easily run the codes directly. The key pair is a 4096 bit long RSA one, the online generation will take some seconds and the encryption padding is OAEP for security reasons.

Security warning: all programs run without any exception handling and are for educational purpose only.

Step 1: Key generation

As the encryption is done with the public key of the recipient of the message the recipient has to generate the RSA key pair. As per your definition the PHP-side is the sender and the C#-side is the recipient let's generate the key pair in C#.

The first code will generate a 4096 bit long RSA key pair in XML-encoding - that's the default output encoding of C#:

privateKeyXML: <RSAKeyValue><Modulus>u0pkuoAjII9KQ9OgEILB6MnGBAedO/Ho+O69W5USpQc+pDGxQbqcC9PKKQk4dymGnS70HVswHVUkdiLcEIMCjU8jdXFnOfh5SPKfR9bgmRQ3HhEaY5ejcCerCW96J5leNEcfkMpWjXy+hvzt0Whnv21Xckbkzo17HZrN+G9C5D1KEeQkdKdg8WVR+3XfhiFLj/S6ANmNjCNbcrR8GKY1GjZahDUyddEza76RuJ3Wqyle0BmNT0RoSHGvjBP+LlY3aOqzsU2sqnzGnItRzr2HnQwVV10MMFo3fbuWopzXqzVjQbnxJM9G79l20Np2eFLDVp4zpiYrGHWSFOnfIDqmy5ke57WnISJwnPz6A+ByhYVJfaxohkk+csbaPCsILQ0ZCzTnCcoh9svw9AIvlHn5YyAa8Gl5o6Ub8lsJXOSftGVtcCsWRiLbD1pg1CTQmoKPPxUFsaU9iPXrfb48+5hFFOVe6+22S8YShFl/HaRq8TMESXmCiRgbgzBV7zmABkJFtH6WUBkXqBTSlUoEsYUHIV61vptroHHui/aVxnynvtkJqTTfto2ukq5ZzpDmQeDE075kM/rOyUuUiJBQ7eszrXKTDKW2xqc6q/OQMfjEiPdtl1Rc44obdRGaTmJhi9h5a/SqH6KPDVoaR+hwLXggnLERroyogElyjbzrkfD5nCE=</Modulus><Exponent>AQAB</Exponent><P>1dlD8Q7ESEgsk507oFT1qZHKgV1Cl6bfNxm7dnCFlkp27kbvsKp5RBzRTCSxClkbwTBfbN0NIQq0o0Rwo9e1Z7yyMGs3v4k3i0Wlw9NkdtineE1dtk/7cBwb6anYZCiyV7TpYK6UCwcwwfyhNBYl5Y7Lr879tUsd1yXfZLVR0YS88gS0LMSsIMRJ6it9H+tcdg+eLTNr9CLIpxuUfYPixhI0xSTF5ZlCplfRaxlvqCh3RJ1IO6Mluv72Df46hKR6mJcMVc2YFUNp/O7038iqGnnBPAlwiT00C2eVh5Qjx4muTg64Zt98ckotsX1Lou8IlBOyem4R1c3b9G6Ga5AZNQ==</P><Q>4DUD8vf0+2LS2PC+cM1s9YqGmR7iukKTBtMPH+rj+wRezwPx7vmNuy8Z5bgfJxfdgHfdQWmQS6gK9OatYR+ZtxQN0B70C2tfEsPBRpPYwOWIiHZQgeczCKevI24w/YyhBU2p1IKljQUsjo1cxyH/es7tVIvfhDw6RVKg4KL8keV7YLSV8kYtJ3/VlrMw5NasfIC2vJ+fatachYRk1BUfB/x7mCDuQmNkQDPiSy24AjzDjMEOPLNNQRWdShkbVYXmX5388VLdbNCrXrCHVdwKrASCGfrCfqhmIY5BE/78c56MprbUCNphkywyiRzzEdhjs6ZgERu7ybQQvd+FQxsAvQ==</Q><DP>yyLdZSjvzvGwVnBvaXgK/A7fXE9oqIH9W2v4JSPx4bfOjb2YhisJkmgFcN0Rx5CpyrPWkaTRieePe9/RV/HaAja+1Tuj8y+3MXbxZblyn2pieuaS9FG5uN8arLWINmxqLOJPJmXvfyJPcJY30zCOwycH2XM4kXRPXkY9lH6gv/PlP8i0Fiqkj2OmCulQrHd4lzVyveEw1PINxJ9wSGJ1kk1ND1BFJDqXk0esSJ4a8JOv59+3896gQqdauCQdFk7fnf8t2nu30cr97SRYPBNzcj7iDdOxsMwQmhzFb8qKykrA5qkZBa+ZmT9ZdFZe+OEzNuYXDjFsg7pPDDvJOHfjWQ==</DP><DQ>1LZXnc9TtKCoi7uz4k9zPW2Ej4c28iGxXfsl0VFHpGIcFOmYQfKvXdStDD3oCG3i7zxJRQMDLFQsuPaVrZyOeJxlEnwbay4eEC56kxw3u6LJKzac4PHZgC+ewM2NWzBtoBtzsbOVnzo2cLR7Pk3qFbh2gA0ILWv6mMRw706Ss1R1/mS2Eho0QtJMcOJm0+U71g9Dz6bdVz5vV1kpU/EJ9Mi89bLgeUnU1fijjFG+zxiZKlQKztoJxGLeTGuPfE9CJps8XDQS8Wbz/y0Z6QKPeW35hO5cUlAwPUu6QF9dYdAdfxncBt4xTrDS7TTzA4454TpK0FBwlEfqzklfJDBdPQ==</DQ><InverseQ>KrqeLyuPFVeAb30HGnirKGLnAJ5SDNACcLnoqscbMC6huenwLL9E18EJQpmVFJLgqyXJTslfmyCcHYsnErKif21VqfQ9QnJrjS5Q2pDrUria7E1opwrhYAKWM/U2MXllCGMurOTLi4sfpkwiUQ8ld8jUJxrIk9XLSAOB+PBN1HZ5UTvzfzPtotms9Q1XBHIHwk1n0jT9z0kg91vVXwFv9JOqLOnDh5RX0JKOZw357bf7toZ2n5aLoAwdzTG6Gqdf/fkUQXOLmH5eaIxwLOJfmjrKHGzyOpbNqYyjjM+N/g3J1vBkDFhdSAibz35neFsJgMyHETSsFaWKZG3PzR80uQ==</InverseQ><D>gDzC1XPxGVUjS2e2XDe7n7GwNIgmH6pWk0OcQf3DQ12/hVtE/DVSrUJ5VDmtAOJc+njwyrzqJ9tpWL4rod6iO7EpHTvlqyq3mLwPVXgKVqYhyyN4xyJH1P5EPPSoVj7L4s2kDlaMhtoX9oNuLojnKIosauGhvs/esXmSd/1udqqEcdWdJIaKeR6f0KZ3wvY/L7UoOna7VW3MKrDrm1EKYQVAWu+1ujfglnBohFkGaDGuapxkRhcXtCqrQSVbT8zWqa6yFL+TbzR6JSu7euwI0/43Cpd6Rg0vm5Z64KK1hNPMP2+ablLCRm3GzeAXZ/F7MG6n/KZ7sYam3Ez56q5eDzO66UL2Ksi81FLTtq/NNegWnELZ79bJy9D1i2R6GmtDG/Lm6BWYHz4m7e7+DMmBJcQbE3cky6iKVyhYVgJ74cREetbXER6Ifriuv9RqumcE1+dN/9yBkinJqa9jFblIGwJMAK9u5fC/Y7Ddba1DS43TYGvnGuYO26QBZPwlyd78t3utHW+Fn38uN/XfYlxMcxlxUHo6eC//zWxRMga+U7AWAZZWLDgHZ+Jp+7cfYypBsIryDiZMUQSkH3o87SMVE9lhT7tO/waJ8mjcdwfAXmCXLhKkFU9CorsU5bIpGkQgGIXNswAGOh8hDvvW1GHLSruGMhoOM9TqHsoO66Ow77E=</D></RSAKeyValue>
publicKeyXML: <RSAKeyValue><Modulus>u0pkuoAjII9KQ9OgEILB6MnGBAedO/Ho+O69W5USpQc+pDGxQbqcC9PKKQk4dymGnS70HVswHVUkdiLcEIMCjU8jdXFnOfh5SPKfR9bgmRQ3HhEaY5ejcCerCW96J5leNEcfkMpWjXy+hvzt0Whnv21Xckbkzo17HZrN+G9C5D1KEeQkdKdg8WVR+3XfhiFLj/S6ANmNjCNbcrR8GKY1GjZahDUyddEza76RuJ3Wqyle0BmNT0RoSHGvjBP+LlY3aOqzsU2sqnzGnItRzr2HnQwVV10MMFo3fbuWopzXqzVjQbnxJM9G79l20Np2eFLDVp4zpiYrGHWSFOnfIDqmy5ke57WnISJwnPz6A+ByhYVJfaxohkk+csbaPCsILQ0ZCzTnCcoh9svw9AIvlHn5YyAa8Gl5o6Ub8lsJXOSftGVtcCsWRiLbD1pg1CTQmoKPPxUFsaU9iPXrfb48+5hFFOVe6+22S8YShFl/HaRq8TMESXmCiRgbgzBV7zmABkJFtH6WUBkXqBTSlUoEsYUHIV61vptroHHui/aVxnynvtkJqTTfto2ukq5ZzpDmQeDE075kM/rOyUuUiJBQ7eszrXKTDKW2xqc6q/OQMfjEiPdtl1Rc44obdRGaTmJhi9h5a/SqH6KPDVoaR+hwLXggnLERroyogElyjbzrkfD5nCE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>

Step 2: RSA encryption on PHP-side with public key

As we are using the PHP-built-in OpenSSL-engine we need to convert the XML-encoded public key to a PEM encoding first. As it is the public key I'm using an online service for this task - there are many other options but for the sake of demonstration it's okay. Go to https://superdry.apphb.com/tools/online-rsa-key-converter and copy/paste the publicKeyXML to the first inputbox and press the button "Convert". In the outputbox below you will find this Public key:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu0pkuoAjII9KQ9OgEILB
6MnGBAedO/Ho+O69W5USpQc+pDGxQbqcC9PKKQk4dymGnS70HVswHVUkdiLcEIMC
jU8jdXFnOfh5SPKfR9bgmRQ3HhEaY5ejcCerCW96J5leNEcfkMpWjXy+hvzt0Whn
v21Xckbkzo17HZrN+G9C5D1KEeQkdKdg8WVR+3XfhiFLj/S6ANmNjCNbcrR8GKY1
GjZahDUyddEza76RuJ3Wqyle0BmNT0RoSHGvjBP+LlY3aOqzsU2sqnzGnItRzr2H
nQwVV10MMFo3fbuWopzXqzVjQbnxJM9G79l20Np2eFLDVp4zpiYrGHWSFOnfIDqm
y5ke57WnISJwnPz6A+ByhYVJfaxohkk+csbaPCsILQ0ZCzTnCcoh9svw9AIvlHn5
YyAa8Gl5o6Ub8lsJXOSftGVtcCsWRiLbD1pg1CTQmoKPPxUFsaU9iPXrfb48+5hF
FOVe6+22S8YShFl/HaRq8TMESXmCiRgbgzBV7zmABkJFtH6WUBkXqBTSlUoEsYUH
IV61vptroHHui/aVxnynvtkJqTTfto2ukq5ZzpDmQeDE075kM/rOyUuUiJBQ7esz
rXKTDKW2xqc6q/OQMfjEiPdtl1Rc44obdRGaTmJhi9h5a/SqH6KPDVoaR+hwLXgg
nLERroyogElyjbzrkfD5nCECAwEAAQ==
-----END PUBLIC KEY-----

Copy and paste your public key in the second program (function loadRsaPublicKeyPem), let it run and encrypt the data, giving this Base64 encoded output:

RSA 2048 encryption OAEP-padding string
plaintext: The quick brown fox jumps over the lazy dog

* * * encrypt the plaintext with the RSA public key * * *
ciphertextBase64: Rot82ugHFtc3b/JNC+uUYMUSQgdHl4bwcdpLIpY7EJzrI7xwMdzKMoastjc4IFiAcXExC7YmpVkVXsHYVGDbMb+o/IKAp/LyH5SrlQqAJq65ZG7Fe54UgKatdqAIBMQIawiv+3g2YmrstdA2KIjW1+93FeH1vUgqSYxydSvIkngjqE//1Dwa/DY+bFrDfG9B1HtDEDFvAOOZE7qC4I2XYvD3LQVwp4zBzFXSzeJP9/BkksC6CN6caKDRdaQa+/oyRjZXJiFIx4YAhok4aBwg54er+2jEeKHcUM01eZBTCFtrIZF4Yg9GQRkCA89XsZkGypqPVL/HLZXx6yHF3kR3XVvCuKwzX/IyostUNbPeWWAWieUFbBWhdPSrENC//7M8nmE7dvcBbRHpoX4LWmD+kWYAFV3M+5n9SJhzwayI0rvQY9DQ4U5IiaP32TDjqyPyK2gzimpuYRe7KWYCBgCG503XThdWe4vpcDNYOKp0KO5TwHMNMTg/FLbwuQcvcpiT9lQLbr5Pz84swe1olhle0rTlBXOS6ONn5LWfrKYjfwluBYsxF/XtJcg+HytBZbCqMNHTeeS4a40r0XLxH/Gjdvw1zs7APrA9AN2SObthMeIXJlryLSshmoKsgBDCi3JzZ80kO4729SVtH1SbwElkZtJNqD6IilY2c1M6CV94tHg=

Step 3: RSA decryption on C#-side with Private key

Copy and paste your private key in the third program (function loadRsaPrivateKeyXml), copy and paste the ciphertextBase64 and run the program, you will get this output:

RSA 2048 decryption OAEP-padding string

* * * decrypt the ciphertext with the RSA private key * * *
ciphertextReceivedBase64: Rot82ugHFtc3b/JNC+uUYMUSQgdHl4bwcdpLIpY7EJzrI7xwMdzKMoastjc4IFiAcXExC7YmpVkVXsHYVGDbMb+o/IKAp/LyH5SrlQqAJq65ZG7Fe54UgKatdqAIBMQIawiv+3g2YmrstdA2KIjW1+93FeH1vUgqSYxydSvIkngjqE//1Dwa/DY+bFrDfG9B1HtDEDFvAOOZE7qC4I2XYvD3LQVwp4zBzFXSzeJP9/BkksC6CN6caKDRdaQa+/oyRjZXJiFIx4YAhok4aBwg54er+2jEeKHcUM01eZBTCFtrIZF4Yg9GQRkCA89XsZkGypqPVL/HLZXx6yHF3kR3XVvCuKwzX/IyostUNbPeWWAWieUFbBWhdPSrENC//7M8nmE7dvcBbRHpoX4LWmD+kWYAFV3M+5n9SJhzwayI0rvQY9DQ4U5IiaP32TDjqyPyK2gzimpuYRe7KWYCBgCG503XThdWe4vpcDNYOKp0KO5TwHMNMTg/FLbwuQcvcpiT9lQLbr5Pz84swe1olhle0rTlBXOS6ONn5LWfrKYjfwluBYsxF/XtJcg+HytBZbCqMNHTeeS4a40r0XLxH/Gjdvw1zs7APrA9AN2SObthMeIXJlryLSshmoKsgBDCi3JzZ80kO4729SVtH1SbwElkZtJNqD6IilY2c1M6CV94tHg=
decryptedData: The quick brown fox jumps over the lazy dog

And voila - we receive the original plaintext from PHP-side here on C#. Here are the online-ressources:

program 1 - generate RSA keys on C#-side: https://repl.it/@javacrypto/CsharpGenerateRsaKeypair#main.cs program 2 - encrypt on PHP-side: https://repl.it/@javacrypto/PhpRsaOaepPaddingEncryption#main.php program 3 - decrypt on C#-side: https://repl.it/@javacrypto/CsharpRsaOaepPaddingDecryption#main.cs

code program 1:

using System;
using System.Security.Cryptography;
using System.Text;

class RSACSPSample
{
    static void Main()
    {
        // Create a new instance of RSACryptoServiceProvider to generate
        // public and private key data.
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(4096);
        rsa.PersistKeyInCsp = false;
        string privateKeyXML = rsa.ToXmlString(true);
        string publicKeyXML = rsa.ToXmlString(false);
        Console.WriteLine("privateKeyXML: " + privateKeyXML);
        Console.WriteLine("publicKeyXML: " + publicKeyXML);
        Console.WriteLine();
    }
}

code program 2:

<?php
function rsaEncryptionOaepSha1($publicKey, $plaintext) {
    openssl_public_encrypt($plaintext, $ciphertext, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
    return $ciphertext;
 }

function base64Encoding($input) {return base64_encode($input);}

function loadRsaPublicKeyPem() {
    // this is a sample key - don't worry !
    return '
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu0pkuoAjII9KQ9OgEILB
6MnGBAedO/Ho+O69W5USpQc+pDGxQbqcC9PKKQk4dymGnS70HVswHVUkdiLcEIMC
jU8jdXFnOfh5SPKfR9bgmRQ3HhEaY5ejcCerCW96J5leNEcfkMpWjXy+hvzt0Whn
v21Xckbkzo17HZrN+G9C5D1KEeQkdKdg8WVR+3XfhiFLj/S6ANmNjCNbcrR8GKY1
GjZahDUyddEza76RuJ3Wqyle0BmNT0RoSHGvjBP+LlY3aOqzsU2sqnzGnItRzr2H
nQwVV10MMFo3fbuWopzXqzVjQbnxJM9G79l20Np2eFLDVp4zpiYrGHWSFOnfIDqm
y5ke57WnISJwnPz6A+ByhYVJfaxohkk+csbaPCsILQ0ZCzTnCcoh9svw9AIvlHn5
YyAa8Gl5o6Ub8lsJXOSftGVtcCsWRiLbD1pg1CTQmoKPPxUFsaU9iPXrfb48+5hF
FOVe6+22S8YShFl/HaRq8TMESXmCiRgbgzBV7zmABkJFtH6WUBkXqBTSlUoEsYUH
IV61vptroHHui/aVxnynvtkJqTTfto2ukq5ZzpDmQeDE075kM/rOyUuUiJBQ7esz
rXKTDKW2xqc6q/OQMfjEiPdtl1Rc44obdRGaTmJhi9h5a/SqH6KPDVoaR+hwLXgg
nLERroyogElyjbzrkfD5nCECAwEAAQ==
-----END PUBLIC KEY-----
';
}

echo 'RSA 2048 encryption OAEP-padding string' . PHP_EOL;
$dataToEncryptString = "The quick brown fox jumps over the lazy dog";
echo 'plaintext: ' . $dataToEncryptString . PHP_EOL;

// encryption
echo PHP_EOL . '* * * encrypt the plaintext with the RSA public key * * *' .PHP_EOL;

$publicKeyLoad = openssl_pkey_get_public(loadRsaPublicKeyPem());
$ciphertextBase64 = base64Encoding(rsaEncryptionOaepSha1($publicKeyLoad, $dataToEncryptString));
echo 'ciphertextBase64: ' . $ciphertextBase64 . PHP_EOL;

// transport the encrypted data to recipient
?>

code program 3:

using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

class RsaDecryptionOaepPadding {
    static void Main() {
    Console.WriteLine("RSA 2048 decryption OAEP-padding string");
    
    string ciphertextBase64 = "Rot82ugHFtc3b/JNC+uUYMUSQgdHl4bwcdpLIpY7EJzrI7xwMdzKMoastjc4IFiAcXExC7YmpVkVXsHYVGDbMb+o/IKAp/LyH5SrlQqAJq65ZG7Fe54UgKatdqAIBMQIawiv+3g2YmrstdA2KIjW1+93FeH1vUgqSYxydSvIkngjqE//1Dwa/DY+bFrDfG9B1HtDEDFvAOOZE7qC4I2XYvD3LQVwp4zBzFXSzeJP9/BkksC6CN6caKDRdaQa+/oyRjZXJiFIx4YAhok4aBwg54er+2jEeKHcUM01eZBTCFtrIZF4Yg9GQRkCA89XsZkGypqPVL/HLZXx6yHF3kR3XVvCuKwzX/IyostUNbPeWWAWieUFbBWhdPSrENC//7M8nmE7dvcBbRHpoX4LWmD+kWYAFV3M+5n9SJhzwayI0rvQY9DQ4U5IiaP32TDjqyPyK2gzimpuYRe7KWYCBgCG503XThdWe4vpcDNYOKp0KO5TwHMNMTg/FLbwuQcvcpiT9lQLbr5Pz84swe1olhle0rTlBXOS6ONn5LWfrKYjfwluBYsxF/XtJcg+HytBZbCqMNHTeeS4a40r0XLxH/Gjdvw1zs7APrA9AN2SObthMeIXJlryLSshmoKsgBDCi3JzZ80kO4729SVtH1SbwElkZtJNqD6IilY2c1M6CV94tHg=";

        try {
        // receiving the encrypted data, decryption
        Console.WriteLine("\n* * * decrypt the ciphertext with the RSA private key * * *");
        string ciphertextReceivedBase64 = ciphertextBase64;
        Console.WriteLine("ciphertextReceivedBase64: " + ciphertextReceivedBase64);
        string privateKeyLoad = loadRsaPrivateKeyXml();
        byte[] ciphertextReceived = Base64Decoding(ciphertextReceivedBase64);
        byte[] decryptedtextByte = rsaDecryptionOaep(privateKeyLoad, ciphertextReceived);
        Console.WriteLine("decryptedData: " + Encoding.UTF8.GetString(decryptedtextByte, 0, decryptedtextByte.Length));
        }
        catch(ArgumentNullException) {
            Console.WriteLine("The data was not RSA encrypted");
        }
    }

  public static byte[] rsaDecryptionOaep(string privateKeyXml, byte[] ciphertext) {
    RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(2048);
        RSAalg.PersistKeyInCsp = false;
        RSAalg.FromXmlString(privateKeyXml);
    return RSAalg.Decrypt(ciphertext, true);
  }

  static byte[] Base64Decoding(String input) {
    return Convert.FromBase64String(input);
  }

  public static string loadRsaPrivateKeyXml() {
    return "<RSAKeyValue><Modulus>u0pkuoAjII9KQ9OgEILB6MnGBAedO/Ho+O69W5USpQc+pDGxQbqcC9PKKQk4dymGnS70HVswHVUkdiLcEIMCjU8jdXFnOfh5SPKfR9bgmRQ3HhEaY5ejcCerCW96J5leNEcfkMpWjXy+hvzt0Whnv21Xckbkzo17HZrN+G9C5D1KEeQkdKdg8WVR+3XfhiFLj/S6ANmNjCNbcrR8GKY1GjZahDUyddEza76RuJ3Wqyle0BmNT0RoSHGvjBP+LlY3aOqzsU2sqnzGnItRzr2HnQwVV10MMFo3fbuWopzXqzVjQbnxJM9G79l20Np2eFLDVp4zpiYrGHWSFOnfIDqmy5ke57WnISJwnPz6A+ByhYVJfaxohkk+csbaPCsILQ0ZCzTnCcoh9svw9AIvlHn5YyAa8Gl5o6Ub8lsJXOSftGVtcCsWRiLbD1pg1CTQmoKPPxUFsaU9iPXrfb48+5hFFOVe6+22S8YShFl/HaRq8TMESXmCiRgbgzBV7zmABkJFtH6WUBkXqBTSlUoEsYUHIV61vptroHHui/aVxnynvtkJqTTfto2ukq5ZzpDmQeDE075kM/rOyUuUiJBQ7eszrXKTDKW2xqc6q/OQMfjEiPdtl1Rc44obdRGaTmJhi9h5a/SqH6KPDVoaR+hwLXggnLERroyogElyjbzrkfD5nCE=</Modulus><Exponent>AQAB</Exponent><P>1dlD8Q7ESEgsk507oFT1qZHKgV1Cl6bfNxm7dnCFlkp27kbvsKp5RBzRTCSxClkbwTBfbN0NIQq0o0Rwo9e1Z7yyMGs3v4k3i0Wlw9NkdtineE1dtk/7cBwb6anYZCiyV7TpYK6UCwcwwfyhNBYl5Y7Lr879tUsd1yXfZLVR0YS88gS0LMSsIMRJ6it9H+tcdg+eLTNr9CLIpxuUfYPixhI0xSTF5ZlCplfRaxlvqCh3RJ1IO6Mluv72Df46hKR6mJcMVc2YFUNp/O7038iqGnnBPAlwiT00C2eVh5Qjx4muTg64Zt98ckotsX1Lou8IlBOyem4R1c3b9G6Ga5AZNQ==</P><Q>4DUD8vf0+2LS2PC+cM1s9YqGmR7iukKTBtMPH+rj+wRezwPx7vmNuy8Z5bgfJxfdgHfdQWmQS6gK9OatYR+ZtxQN0B70C2tfEsPBRpPYwOWIiHZQgeczCKevI24w/YyhBU2p1IKljQUsjo1cxyH/es7tVIvfhDw6RVKg4KL8keV7YLSV8kYtJ3/VlrMw5NasfIC2vJ+fatachYRk1BUfB/x7mCDuQmNkQDPiSy24AjzDjMEOPLNNQRWdShkbVYXmX5388VLdbNCrXrCHVdwKrASCGfrCfqhmIY5BE/78c56MprbUCNphkywyiRzzEdhjs6ZgERu7ybQQvd+FQxsAvQ==</Q><DP>yyLdZSjvzvGwVnBvaXgK/A7fXE9oqIH9W2v4JSPx4bfOjb2YhisJkmgFcN0Rx5CpyrPWkaTRieePe9/RV/HaAja+1Tuj8y+3MXbxZblyn2pieuaS9FG5uN8arLWINmxqLOJPJmXvfyJPcJY30zCOwycH2XM4kXRPXkY9lH6gv/PlP8i0Fiqkj2OmCulQrHd4lzVyveEw1PINxJ9wSGJ1kk1ND1BFJDqXk0esSJ4a8JOv59+3896gQqdauCQdFk7fnf8t2nu30cr97SRYPBNzcj7iDdOxsMwQmhzFb8qKykrA5qkZBa+ZmT9ZdFZe+OEzNuYXDjFsg7pPDDvJOHfjWQ==</DP><DQ>1LZXnc9TtKCoi7uz4k9zPW2Ej4c28iGxXfsl0VFHpGIcFOmYQfKvXdStDD3oCG3i7zxJRQMDLFQsuPaVrZyOeJxlEnwbay4eEC56kxw3u6LJKzac4PHZgC+ewM2NWzBtoBtzsbOVnzo2cLR7Pk3qFbh2gA0ILWv6mMRw706Ss1R1/mS2Eho0QtJMcOJm0+U71g9Dz6bdVz5vV1kpU/EJ9Mi89bLgeUnU1fijjFG+zxiZKlQKztoJxGLeTGuPfE9CJps8XDQS8Wbz/y0Z6QKPeW35hO5cUlAwPUu6QF9dYdAdfxncBt4xTrDS7TTzA4454TpK0FBwlEfqzklfJDBdPQ==</DQ><InverseQ>KrqeLyuPFVeAb30HGnirKGLnAJ5SDNACcLnoqscbMC6huenwLL9E18EJQpmVFJLgqyXJTslfmyCcHYsnErKif21VqfQ9QnJrjS5Q2pDrUria7E1opwrhYAKWM/U2MXllCGMurOTLi4sfpkwiUQ8ld8jUJxrIk9XLSAOB+PBN1HZ5UTvzfzPtotms9Q1XBHIHwk1n0jT9z0kg91vVXwFv9JOqLOnDh5RX0JKOZw357bf7toZ2n5aLoAwdzTG6Gqdf/fkUQXOLmH5eaIxwLOJfmjrKHGzyOpbNqYyjjM+N/g3J1vBkDFhdSAibz35neFsJgMyHETSsFaWKZG3PzR80uQ==</InverseQ><D>gDzC1XPxGVUjS2e2XDe7n7GwNIgmH6pWk0OcQf3DQ12/hVtE/DVSrUJ5VDmtAOJc+njwyrzqJ9tpWL4rod6iO7EpHTvlqyq3mLwPVXgKVqYhyyN4xyJH1P5EPPSoVj7L4s2kDlaMhtoX9oNuLojnKIosauGhvs/esXmSd/1udqqEcdWdJIaKeR6f0KZ3wvY/L7UoOna7VW3MKrDrm1EKYQVAWu+1ujfglnBohFkGaDGuapxkRhcXtCqrQSVbT8zWqa6yFL+TbzR6JSu7euwI0/43Cpd6Rg0vm5Z64KK1hNPMP2+ablLCRm3GzeAXZ/F7MG6n/KZ7sYam3Ez56q5eDzO66UL2Ksi81FLTtq/NNegWnELZ79bJy9D1i2R6GmtDG/Lm6BWYHz4m7e7+DMmBJcQbE3cky6iKVyhYVgJ74cREetbXER6Ifriuv9RqumcE1+dN/9yBkinJqa9jFblIGwJMAK9u5fC/Y7Ddba1DS43TYGvnGuYO26QBZPwlyd78t3utHW+Fn38uN/XfYlxMcxlxUHo6eC//zWxRMga+U7AWAZZWLDgHZ+Jp+7cfYypBsIryDiZMUQSkH3o87SMVE9lhT7tO/waJ8mjcdwfAXmCXLhKkFU9CorsU5bIpGkQgGIXNswAGOh8hDvvW1GHLSruGMhoOM9TqHsoO66Ow77E=</D></RSAKeyValue>";
  }
}
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40
0

Credit to Michael Fehr from the comments.

Apparently C# cannot decrypt using a public key, at least with the libraries I'm using. So the solution is to give the C# code the private key and decrypt all messages from the PHP server with the public key.

It's my understanding that this won't be a security issue, despite the fact that the "private" key is being sent into the wild, because both keys are designed for this exact purpose.

Bobdabiulder
  • 160
  • 3
  • 12
  • Kindly do not mix "encryption" with "signature" - the **encryption** is done with a **public key** and the **decryption** is done with the **private key**. Turning to digital signing, the **signing** is done with the **private key** and the **verification** with the **public key**. As already commented: some libraries do allow working the other way round but Java and C# do not allow the "wrong way" (what is good in my opinion). – Michael Fehr Dec 29 '20 at 15:39
  • I'm not sure what you mean. In this case encryption is what's being done: PHP is calling openssl_public_encrypt() on a string with the public key, and the C# code is calling rsa.Decrypt() on the ciphertext PHP sends to it. The original string is returned from the RSA function, hence, it is encrypted. As Wikipedia explains, "Alice can recover message from the ciphertext by using her private key". In this case, C# is Alice and PHP is Bob. – Bobdabiulder Dec 29 '20 at 16:30
  • 1
    You wrote in your answer "sign all messages" - that's why I wrote don't mix signing with encrypting. – Michael Fehr Dec 29 '20 at 18:12
  • Oh, oops. I've fixed it now. My mistake. – Bobdabiulder Dec 29 '20 at 18:38