This question already has an answer here:
RSA Encryption and Decryption successed in JS, but Decryption fails in C#
(1 answer)
Closed 3 years ago.
I have the following implementation for decrypting a value. This implementation works, but in some small case the values I'm attempting to decrypt are throwing a Cryptographic Exception, with the message 'The Parameter is incorrect'. This occurs where I call the Decrypt method on the private key below.
All values are encrypted with the public key, base64 encoded in transit, and passed along to this method the same way, so I don't understand why it would be blowing up sometimes. If I re-encrypt with the public key, that new value can usually be unencrypted successfully.
Certificate in this case below is the X509Certificate2
public string Decrypt(CertificateType certificateType, byte[] encryptedString)
{
string result = null;
var certificate = GetCertificate(certificateType);
var privateKey = certificate?.GetRSAPrivateKey();
if (privateKey != null)
{
var decryptedBytes = privateKey.Decrypt(encryptedString, RSAEncryptionPadding.Pkcs1);
result = Encoding.Default.GetString(decryptedBytes);
}
return result;
}
Any ideas as to what would cause that exception?
You should run it 1000x and log all the errors, see what those base64 input strings have in common and what the passing ones do - my guess is that it's padding related with the base64 encoding (Some encoders will pad with equal signs at the end of the strings and some won't, then some decoders expect the padding and some are able to handle it). When it lines up right, the decrypt works, when it doesn't (the length of the base64 string) the method throws an exception.
Other alternative is that instead of base64, you should be doing base64url encoding. When it works, it's because base64(x) == base64url(x) and when it doesn't it's because base64(x) != base64url(x)
Looks like it is with the Base64 encoding. Thank you for the help!
The Answer on this post did remove the decrypt issue I was having:
RSA Encryption and Decryption successed in JS, but Decryption fails in C#
Related
I am hoping that this question can be met with some guidance for someone who is beginning to work with encryption/decryption in C#. There are existing examples on the web regarding this, but I am truthfully struggling to put it all into practice for my given situation.
If given a text file that has been encrypted using OpenPGP with RSA, what is the best method to decrypt this in C#?
This is what I am attempting:
Using Kleopatra OpenPGP, I am generating a key pair using 2048bit RSA. This generates a private and public key.
I am then encrypting/signing a text file with a few word in it as a test.
In C#, I want to decrypt this text file.
Current code:
byte[] encryptedData = File.ReadAllBytes("C:\\PGP Encryption\\test.txt.gpg"); // The encrypted text file generated by Kleopatra.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
return decryptedData;
Unfortunately, the RSA.Decrypt() call throws an exception that reads "The data to be decrypted exceeds the maximum for this modulus of 128 bytes."
I also do not believe that my private key is actually being loaded, as I'm not explicitly stating where the key is. But I don't see how the RSAParameters object is supposed to get populated otherwise.
If anyone can point me in the right direction to decrypt a file in this way, thank you in advance for your time and information.
It's looks like you need this library (see Decrypt section) https://github.com/mattosaurus/PgpCore
I am getting an error decrypting a message in go that was encrypted in C# (using corresponding public/private keys)
My client is written in C# and my server is written in Go. I generated a private and public key via go's crypto/rsa package (using rsa.GenerateKey(random Reader, bits int)). I then store the public key file generated where the client can access it and the private key where the server can access it. I encrypt on the client with the following code (using bouncy castle):
public static string Encrypt(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PemReader pr = new PemReader(
new StringReader(m_publicKey)
);
RsaKeyParameters keys = (RsaKeyParameters)pr.ReadObject();
// PKCS1 OAEP paddings
OaepEncoding eng = new OaepEncoding(new RsaEngine());
eng.Init(true, keys);
int length = plainTextBytes.Length;
int blockSize = eng.GetInputBlockSize();
List<byte> cipherTextBytes = new List<byte>();
for (int chunkPosition = 0; chunkPosition < length; chunkPosition += blockSize)
{
int chunkSize = Math.Min(blockSize, length - chunkPosition);
cipherTextBytes.AddRange(eng.ProcessBlock(
plainTextBytes, chunkPosition, chunkSize
));
}
return Convert.ToBase64String(cipherTextBytes.ToArray());
}
The go server parses this string from the header and uses the private key to decrypt:
func DecryptWithPrivateKey(ciphertext []byte, priv *rsa.PrivateKey) []byte {
hash := sha512.New()
plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
}
return plaintext
}
The decryption function throws crypto/rsa: decryption error. If I try pasting the cipher text directly into go (rather then sending from the client), the same error occurs.
NOTE: in order to get the public key to load, I needed to change the header from:
-----BEGIN RSA PUBLIC KEY-----
...
to
-----BEGIN PUBLIC KEY-----
...
and the same for the footer. I am assuming this is a formatting issue but not sure how to go about solving.
EDIT: it seems that golang OAEP uses sha256 and bouncy castle uses SHA-1. Go's documentation specifies that the hash for encryption and decryption must be the same. This seems likely to be the issue? If it is, how can I change the hashing algorithm used by either go or C#?
Yes, you need to match the hash. In GoLang you've already set it to SHA-512 if I take a look at your code. Using SHA-256 at minimum should probably be preferred, but using SHA-1 is relatively safe as the MGF1 function doesn't rely on the collision resistance of the underlying hash. It's also the default for most runtimes, I don't know why GoLang decided against that.
Probably the best is to set SHA-512 for both runtimes, so here is the necessary constant for .NET.
Note that the underlying story is even more complex as OAEP uses a hash over a label as well as a hash within MGF1 (mask generation function 1, the only one specified). Both need to be specified in advance and generally the same hash function is used, but sometimes it is not.
The label is generally empty and most runtimes don't even allow setting it, so the hash value over the label is basically a hash-function specific constant that doesn't matter for security. The constant just manages to make things incompatible; "More flexible" isn't always a good thing.
I've been working on this for a while and I don't know much about either PHP or C#. We are building an application that is using AES 128 CBC mode encryption to store things in the database. One part is PHP and JS, the other is C# .NET WPF.
The person who wrote the PHP used the Mcrypt library to crypt/decrypt. I'm using the Chilkat library to encrypt/decrypt. Chilkat had a default C# example that is supposed to mimic the PHP Mcrypt.
Currently I can symmetrically encrypt/decrypt things on .Net, and .Net can decrypt anything from PHP. However the PHP side cannot decrypt anything that I encrypt to the database from the .Net side.
I've narrowed at least part of down to encoding issues, but I'm not sure how to fix it. The decryption scheme on PHP side usually decrypts to ASCII, but for things that I send it decrypts to UTF-8. I've tried to decrypt it then encode from UTF-8 to ASCII to no avail.
I'll show you the in/outputs and the functions. The IV is being set to 16 ASCII 0s to help my debugging along even though it shouldn't really matter.
input from .Net to mcrypt_encrypt func: string "1220"
output: 3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8=
Parameter input into database = 'same as above'
input of PHP side decrypt func = 3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8= 'same as above'
output of the mcrypt_decrypt function = ��J���{$�Z'?�u 'iconv says utf-8 encoding'
Ask for anything else and I'll get it if it would help. I'm sure this is some stupid easy problem I can't see.
PHP side - if it matters the PHP charset is set to UTF-8
function encrypt($input)
{
$this->iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
/*No longer using random iv :(
$this->iv = mcrypt_create_iv($this->iv_size, MCRYPT_RAND);*/
//use 16 zeros
$this->iv = '0000000000000000';
$encrypted = $this->iv .mcrypt_encrypt(MCRYPT_RIJNDAEL_128, KEY, $input, MODE, $this->iv);
//Finally encode this as base 64, see http://php.net/manual/en/function.base64-encode.php
$encrypted = base64_encode($encrypted);
return $encrypted;
}
function decrypt($input)
{
/*Get our message back!!!
First decode the base 64 string. Note, de/encoding bas 64 != encryption*/
$ciphertext_dec = base64_decode($input);
//Get the iv back out for decryption
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
//$iv_dec = substr($ciphertext_dec, 0, $iv_size);*/
$iv_dec = '0000000000000000';
//Now get the text of encrypted message (all but the iv in front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);
//Now decrypt the message
$plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, KEY, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
//Test
//test
//$plaintext_dec = iconv("UTF-8", "ASCII", $plaintext_dec);
//echo mb_detect_encoding($plaintext_dec, "auto");
//echo $plaintext_dec;
/*However, we might now have blank space # end of output b/c
remember we de/encrypt via block, so a 10 char long message
could be padded to 16 char long with blank spaces. Get rid of those.*/
$plaintext_dec = trim($plaintext_dec);
//return so we can compare to, i.e., original input
return $plaintext_dec;
}
.NET C#
public string mcrypt_encrypt(string plainText)
{
plainText = Encoding.ASCII.GetString(crypt.IV) + plainText;
byte[] myText = Encoding.ASCII.GetBytes(plainText);
// Do 128-bit AES encryption:
byte[] cipherText = crypt.EncryptBytes(myText);
return Convert.ToBase64String(cipherText);
}
public string mcrypt_decrypt(string cipher_text)
{
byte[] cipher_dec = Convert.FromBase64String(cipher_text);
byte[] plainBytes = crypt.DecryptBytes(cipher_dec);
string decrypted = Encoding.ASCII.GetString(plainBytes);
string plain_text = decrypted.Substring(16, decrypted.Length - 16);
return plain_text.TrimEnd('\0');
}
C# Chilkat init:
// AES is also known as Rijndael.
crypt.CryptAlgorithm = "aes";
// CipherMode may be "ecb" or "cbc"
crypt.CipherMode = "cbc";
// KeyLength may be 128, 192, 256
crypt.KeyLength = 128;
// Pad with NULL bytes (PHP pads with NULL bytes)
crypt.PaddingScheme = 3;
// EncodingMode specifies the encoding of the output for
// encryption, and the input for decryption.
// It may be "hex", "url", "base64", or "quoted-printable".
crypt.EncodingMode = "hex";
// The secret key must equal the size of the key. For
// 256-bit encryption, the binary secret key is 32 bytes.
// For 128-bit encryption, the binary secret key is 16 bytes.
string keyAscii = #"&=*FS6wksG#Zs3qG";
crypt.SecretKey = Encoding.UTF8.GetBytes(keyAscii);
crypt.Charset = "ASCII";
crypt.SetEncodedIV("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "ascii");
You have a variety of different problems here. The issue causing the problem you seeing in not being able to decrypt in PHP data that was encrypted in .NET is that in the PHP version you're performing a substr on the ciphertext prior to decryption. The comments in the code indicate that you're removing the IV, except that that seems to have only been relevant in a prior version of your code when you were (correctly) using a random IV each time - now you're just discarding the first 16 bytes of ciphertext which due to the mode of operation corrupts the subsequent data block.
The other problem (though masked by the fact that you're discarding the first 16 bytes of the plaintext data when decrypting in .NET) is that the IV you're using in .NET (16 bytes of 0x00) is not the same as the IV you're using in PHP (16 '0' characters = 16 bytes of 0x30).
I would suggest reverting to using a random IV for every encryption and prepending the IV to the ciphertext after encryption When decrypting, read the IV from the first bytes of the ciphertext then decrypt the remainder. This is much more secure than having a static IV, especially when the data being encrypted is likely to often be the same.
It looks like it's a simple character encoding issue. Your C# code is getting the ASCII representation of your string and encrypting that, but your PHP code is decrypting it and expecting it to be UTF-8.
Try swapping your Encoding.ASCII calls for Encoding.UTF8 and make sure your crypt.Charset and crypt.SetEncodedIV are UTF8 as well
We encrypt PDFs using AESManaged algorithm implemented in .NET framework. I used the example explained in here to implement C# code to encrypt the file. Now I need to decrypt that file using an iPhone application.(That is the requirement). So I use the this code to do that but decryption failed by returning an error.
'Error Domain=CommonCryptoErrorDomain Code=-4304 "Decode Error"
UserInfo=0x127356c0 {NSLocalizedFailureReason=Input data did not
decode or decrypt correctly, NSLocalizedDescription=Decode Error'
Can some one help me to resolve this issue.
We use 12345678 as encryption key.
Most likely the problem is in the deriving actual key from the password (12345678 cannot be the AES key directly - it is only 8 bytes).
Technically this should work though I've never tested it, both methods uses the same ad-hoc format.
Encrypt using my authenticated encryption example.
//use your secret data you want to encrypt instead.
String secretMessage = "Message";
var rnCryptorHeader = new Byte[]{
2, //RNCryptor Format version 2
0 //RNCryptor Uses password
};
//encryptedString is base64 encoded
var encryptedString = AESThenHMAC.SimpleEncryptWithPassword(secretMessage,
password:"1234567891011",
nonSecretPayload:rnCryptorHeader);
Then Decrypt using RNCryptor and NSData+Base64 for IOS
//This is the encrypted data passed from .net
NSString *encryptedString = #"AgE8C9E7gsfyOAmSotIOgyLQ0O6mdcuMXXjN/iZa3azym4KVWZAkfykIP6mqMt/qkpfftdB3XQhMkoxtQEM+rA0iHxOvZiNlmA2KJtg6BOnmlg==";
NSData *encryptedData = [NSData dataFromBase64String: encryptedString];
NSError *error;
NSData *decryptedData = [RNDecryptor decryptData:encryptedData
withPassword:#"1234567891011"
error:&error];
NSString *secretMessage = [[[NSString alloc] initWithData:decryptedData
encoding:NSUTF8StringEncoding] autorelease];
Since you aren't dealing with strings and are dealing with bytes directly, just remove the Base64 and utf8 encoding/decoding from this objective-c example and the linked c# example, once you are sure this is working.
Edit.
I guess I had a problem with creating a correct input into the encrypt/decrypt methods: These lines of code do the trick:
string encrypted = en.Encrypt(stringBuilder.ToString(), "username", "password");
string decrypted = en.Decrypt(encrypted, "username", "password");
mainWindow.ChangeTextBox = encrypted + Environment.NewLine + decrypted;
I am just playing around and trying to figure out how encryption/decryption by using AES works. I am referring to this article (pretty much copy and paste :( Trying to learn).
There they give me complete encryption/decryption methods which I have tried to modify. I am trying to pass a list of entries created by ArrayList then binded with string builder. It seems like I am able to encrypt data but decryption causes error:
Length of the data to decrypt is invalid.
at line with code:
ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
How can I implement this code to be useful with my program and work correctly?
Regards.
I haven't looked through the code in detail, but it seems that the order of the parameters on the calls to Encrypt and Decrypt doesn't match the definition of the methods. In the calls to these methods you appear to have username, password, plain/cyphertext; whereas the methods have the signature plain/cyphertext, password, username.
Am I right it's caused for an empty or null string as plaintext? ;-)
"lol1" can not be decrypted as it's not a valid ciphertext length (padding!) not to say it wouldn't make any sense.