I am working with a friend to decode an RSA-encrypted message. We have a sample working in Go lang but need to do the same in .NET 6+.
package main
import (
"crypto/rand"
cryptorsa "crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"log"
)
type RSA struct {
publicKey *cryptorsa.PublicKey
privateKey *cryptorsa.PrivateKey
}
func NewRSA() (*cryptorsa.PrivateKey, error) {
const pubPEM = `-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----`
key, err := ParsePrivateKey([]byte(pubPEM))
if err != nil {
return nil, err
}
return key, nil
}
func ParsePrivateKey(privateKeyPEM []byte) (*cryptorsa.PrivateKey, error) {
block, _ := pem.Decode(privateKeyPEM)
if block == nil {
return nil, errors.New("decoding private key failed")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errors.New("parsing private key failed")
}
return privateKey, nil
}
func main() {
rsa, err := NewRSA()
if err != nil {
log.Fatal(err)
}
const encryptedMessage = `
-----BEGIN CVV2 MESSAGE-----
khA8wKKZEvf5uZKacdhhkQIS8u30RaoaASMEkOXCB+BcWx5lAE3KpKNDpppA01f8
Lr84opHTBdj24BUW3zAUbulX4dwX+COipbvOAgT06ndwGe2j2vTj5QP7t4YE/cag
Wnj1jxpN+0XdCW9lFZmmfMry1YanIbMabDyGMuSeF3QkqQKZID9VKX0F5HxG7hsF
GhBoJJ7s8qCoVH41kfJD/xImfHgzPcLrXg56vg+AHgHo/So4dUJUuClcsayclsTP
mRqkQCOqHa2rrB7K15h8B4gVGEQUT4UdAR7dYjoj7tOCYQeT0n27pUAQ/+YK1fGa
rqnHDNgO8CH8LPo3C58uhJ0gR3HmwfY613yg28/iSw1v+UWQiFaxSPJ5DI+2cRji
ZPoWjCj/346r/8mV2tDZCGKKPUXAXIUyYrjGEx/t+jJirX69aoaLC17HBxLGvcUK
cFh/1nXGEbemaOfqWni+vF6qkeeWnDvaYJAcxuivhM/r49aTDYxQ3Pi8o1xWH6Gk
S+8wwKqhUlgD8mF0OWWwMAmb7OBd4e6nYwEIjUcEt6MiOaltpzxMY0biuIUY31WNlnO+M5yh3cZT76fPDzgkSlmVFDVehxK+Tg+N6bk23VYNcKJ8QVpOPAF3zZyu30IT emtx0w+9EqM1KX1sTRgkqIMGeajbfSHFGc07WZobkXk=
-----END CVV2 MESSAGE----- `
block, _ := pem.Decode([]byte(encryptedMessage))
fmt.Println("this is block:", block)
decryptedMessage, err := cryptorsa.DecryptOAEP(sha256.New(), rand.Reader, rsa, block.Bytes, []byte("CVV2"))
if err != nil {
log.Fatal(err)
}
log.Println(fmt.Sprintf("Decrypted number: %s", decryptedMessage))
}
We've tried following for .NET 6 and .NET 7 but it's not working:
using EncryptDecryptRSA;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleTester
{
class Program
{
static void Main(string[] args)
{
string cyphertext = @"khA8wKKZEvf5uZKacdhhkQIS8u30RaoaASMEkOXCB+BcWx5lAE3KpKNDpppA01f8
Lr84opHTBdj24BUW3zAUbulX4dwX+COipbvOAgT06ndwGe2j2vTj5QP7t4YE/cag
Wnj1jxpN+0XdCW9lFZmmfMry1YanIbMabDyGMuSeF3QkqQKZID9VKX0F5HxG7hsF
GhBoJJ7s8qCoVH41kfJD/xImfHgzPcLrXg56vg+AHgHo/So4dUJUuClcsayclsTP
mRqkQCOqHa2rrB7K15h8B4gVGEQUT4UdAR7dYjoj7tOCYQeT0n27pUAQ/+YK1fGa
rqnHDNgO8CH8LPo3C58uhJ0gR3HmwfY613yg28/iSw1v+UWQiFaxSPJ5DI+2cRji
ZPoWjCj/346r/8mV2tDZCGKKPUXAXIUyYrjGEx/t+jJirX69aoaLC17HBxLGvcUK
cFh/1nXGEbemaOfqWni+vF6qkeeWnDvaYJAcxuivhM/r49aTDYxQ3Pi8o1xWH6Gk
S+8wwKqhUlgD8mF0OWWwMAmb7OBd4e6nYwEIjUcEt6MiOaltpzxMY0biuIUY31WNlnO+M5yh3cZT76fPDzgkSlmVFDVehxK+Tg+N6bk23VYNcKJ8QVpOPAF3zZyu30ITemtx0w+9EqM1KX1sTRgkqIMGeajbfSHFGc07WZobkXk=";
string privateKey = @"
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";
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
byte[] b = Encoding.UTF8.GetBytes(cyphertext);
byte[] byteData = Convert.FromBase64String(cyphertext);
Decryption(byteData, privateKey, true);
}
static public void Decryption(byte[] Data, string key, bool DoOAEPPadding)
{
try
{
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
RSA.ImportFromPem(key);
// tried with both false and true
// also provided OEAP padding but no luck
var decryptedData = RSA.Decrypt(Data, false);
}
}
catch (CryptographicException e)
{
Console.WriteLine(e.ToString());
}
}
}
}
With RSA.Decrypt(Data, false)
we get:
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The parameter is incorrect.'
With RSA.Decrypt(Data, true)
, we get:
System.Security.Cryptography.CryptographicException: 'Cryptography_OAEPDecoding'
Went through a lot of other questions, but didn't work for us.
OAEP has several parameters that must be specified if they differ from the default values of the library used: the content digest, the MGF1 digest and a label. The default values from RFC8017 are SHA-1 for both digests and an empty label (these do not necessarily have to correspond to those of the library used).
Usually, the label is left empty. This is so common that some libraries do not even provide the option to use a different value, as in the case of the native .NET methods, which uses fixed an empty label. Since a different label was applied during encryption, namely CVV2
, decryption with the native .NET methods is not possible. Instead, C#/BouncyCastle must be used:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
...
byte[] ciphertext = Convert.FromBase64String(ciphertextB64);
PemReader pemReader = new PemReader(new StringReader(privatePkcs1PEM));
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
Sha256Digest sha256 = new Sha256Digest();
OaepEncoding oaepEncoding = new OaepEncoding(new RsaEngine(), sha256, sha256, Encoding.UTF8.GetBytes("CVV2"));
oaepEncoding.Init(false, keyPair.Private);
byte[] plaintext = oaepEncoding.ProcessBlock(ciphertext, 0, ciphertext.Length);
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // 447
This returns 447
as plain text.
Note that in the above example privatePkcs1PEM
must be a PEM encoded private RSA key in PKCS#1 format. privateKey
used in your code is missing header and footer (-----BEGIN/END RSA PRIVATE KEY-----
), so this is not a valid PEM key (and therefore ImportFromPem()
should fail).
If an empty label is applied during encryption, the native .NET methods can be used for decryption: RSA.Decrypt(Byte[], RSAEncryptionPadding)
with RSAEncryptionPadding.OaepSHA256
. This applies SHA-256 for both digests and an empty label.