RSA encrypt by PHP and decrypt by C# - c#

I'm trying to encryption and decrpytion process between C# and PHP.
My C# code:
this.EncryptData("123456", 1024, this.PublicKey);
public byte[] Encrypt(byte[] byte_0, int keysize, string publicKey)
{
byte[] numArray;
using (RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider(keysize))
{
rSACryptoServiceProvider.FromXmlString(publicKey);
numArray = rSACryptoServiceProvider.Encrypt(byte_0, this.bool_0);
}
return numArray;
}
public string EncryptData(string string_0, int keysize, string publicKey)
{
string base64String;
try
{
byte[] numArray = this.Encrypt(Encoding.UTF8.GetBytes(string_0), keysize, publicKey);
base64String = Convert.ToBase64String(numArray);
}
catch (Exception exception)
{
base64String = string.Empty;
}
return base64String;
}
public byte[] Decrypt(byte[] byte_0, int keysize, string Key)
{
byte[] numArray;
using (RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider(keysize))
{
rSACryptoServiceProvider.FromXmlString(Key);
numArray = rSACryptoServiceProvider.Decrypt(byte_0, this.bool_0);
}
return numArray;
}
public string DecryptData(string string_0, int keysize, string Key)
{
string str;
try
{
byte[] numArray = this.Decrypt(Convert.FromBase64String(string_0), keysize, Key);
str = Encoding.UTF8.GetString(numArray);
}
catch (Exception exception)
{
str = string.Empty;
}
return str;
}
and my PHP: lib(https://github.com/membersuite/sdk-php/blob/master/APISample/SSOWithSDK/phpseclib/Crypt/RSA_XML.php)
$rsa = new Crypt_RSA_XML();
$plaintext = '123456';
$rsa->loadKeyfromXML($publicKey);
$ciphertext = base64_encode(strrev($rsa->encrypt($plaintext)));
code after PHP encrypt cant decrypt by c# code. anyone can help?

Although Microsoft uses little endian notation for numbers, RSA has been defined by PKCS#1 / RFC 3447. That explicitly defines how to do padding and such, but it also defines how the resulting octet string should look like using I2OSP or the integer-to-octet-string primitive. This primitive specifies the output as fixed size (key size) byte array in big endian format. This is also the encoding that PHP uses, so you should not reverse the output.
In other words, you should not have to use strrev.

Related

C#: Error while decoding OAEP padding weird issue

I'm currently working on a class that encrypts large amounts of text with a randomly generated encryption key encrypted by a X509 certificate from a smart card, using a RSACryptoServiceProvider to perform the master key encryption and decryption operations. However, when I have the fOEAP padding option set to true, I have the "Error while decoding OAEP padding" error on decryption every time. I've checked the key size and it is within acceptable limits. And I've gone through breakpoints to make sure that the Base64 string that gets returned from the encryption function is the exact same as the encrypted Base64 string that gets passed back to the decryption function when the file gets loaded again.
The key pair is definitely correct, since it works fine without OAEP. And I've checked the text encoding too.
EDIT: It turns out this could be a smart card specific issue, when I tried decryption with a local X509 certificate the decryption succeeded.
EDIT: This is the decryption code that fails:
string TestString = "Hello World!";
X509Certificate2 cert = DRXEncrypter.GetCertificate("Select a test certificate", "Select a certificate to use for this test from the local store.");
string key = DRXEncrypter.GenerateEncryptionKey(214);
Console.WriteLine("Encryption Key: " + key);
string encrypted = DRXEncrypter.EncryptBody(TestString, key);
Console.WriteLine("Encrypted Body: " + encrypted);
string cryptokey = DRXEncrypter.EncryptWithCert(cert, key);
Console.WriteLine("Encrypted Decryption Key: " + cryptokey);
string decrypted = DRXEncrypter.DecryptBody(encrypted, cryptokey, cert);
Console.WriteLine("Decrypted Body: " + decrypted);
Console.WriteLine("Output String: " + decrypted + ".");
Here is the code from the crypto provider class I've written. I've been stuck on this issue for hours, so it would be great if someone could help me out.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace CoreDRXEditor
{
public class DRXEncrypter
{
private byte[] Salt = Encoding.ASCII.GetBytes("81PO9j8I1a94j");
private string EncryptionKey;
private const bool UseOAEP = true;
public DRXEncrypter(string EncryptionKey)
{
this.EncryptionKey = EncryptionKey;
}
public static string EncryptBody(string body, string encryptionkey)
{
// Use the plaintext master key to encrypt the body.
DRXEncrypter enc = new DRXEncrypter(encryptionkey);
// Encrypt the body.
return enc.Encrypt(body);
}
public static int GetMaxKeySize(X509Certificate2 cert)
{
RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;
return csp.KeySize;
}
public static string DecryptBody(string body, string encryptionkey, X509Certificate2 cert)
{
// Decrypt the encrypted encryption key with the certificate.
string DecryptedKey = Convert.ToBase64String(DecryptWithCert(cert, encryptionkey));
// Create a new DRXEncrypter using the decrypted encryption key to decrypt the body.
DRXEncrypter enc = new DRXEncrypter(DecryptedKey);
// Return the decrypted body.
return enc.Decrypt(body);
}
public static string GenerateEncryptionKey(int KeyLength)
{
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] CryptoBytes = new byte[KeyLength];
rng.GetBytes(CryptoBytes);
return Convert.ToBase64String(CryptoBytes);
}
}
public static X509Certificate2 GetCertificate(string title, string message)
{
X509Store cstore = new X509Store(StoreLocation.CurrentUser);
cstore.Open(OpenFlags.ReadOnly);
X509CertificateCollection certs = X509Certificate2UI.SelectFromCollection(cstore.Certificates, title, message, X509SelectionFlag.SingleSelection);
if (certs.Count == 1)
{
X509Certificate2 mcert = certs[0] as X509Certificate2;
return mcert;
}
else
{
return null;
}
}
public static string EncryptWithCert(X509Certificate2 cert, string PlainText)
{
RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;
byte[] PlainBytes = Convert.FromBase64String(PlainText);
// This converts the plain text into a byte array and then encrypts the raw bytes.
byte[] CryptoBytes = csp.Encrypt(PlainBytes, UseOAEP);
// This converts the encrypted bytes into a Base64 string.
string ReturnString = Convert.ToBase64String(CryptoBytes);
return ReturnString;
}
public static byte[] DecryptWithCert(X509Certificate2 cert, string EncryptedText)
{
RSACryptoServiceProvider csp = cert.PrivateKey as RSACryptoServiceProvider;
//CspParameters csps = new CspParameters();
byte[] EncryptedBytes = Convert.FromBase64String(EncryptedText);
// This converts the encrypted, Base64 encoded byte array from EncryptWithCert() to a byte[] and decrypts it.
byte[] CryptoBytes = csp.Decrypt(EncryptedBytes, UseOAEP);
return CryptoBytes;
}
public string Encrypt(string PlainText)
{
RijndaelManaged Algorithm = null;
string Output = null;
try
{
Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);
Algorithm = new RijndaelManaged();
Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
Algorithm.Padding = PaddingMode.PKCS7;
ICryptoTransform Encryption = Algorithm.CreateEncryptor(Algorithm.Key, Algorithm.IV);
using (MemoryStream msa = new MemoryStream())
{
msa.Write(BitConverter.GetBytes(Algorithm.IV.Length), 0, sizeof(int));
msa.Write(Algorithm.IV, 0, Algorithm.IV.Length);
using (CryptoStream csa = new CryptoStream(msa, Encryption, CryptoStreamMode.Write))
{
using (StreamWriter swa = new StreamWriter(csa))
{
swa.Write(PlainText);
}
}
Output = Convert.ToBase64String(msa.ToArray());
}
}
finally
{
if (Algorithm != null)
{
Algorithm.Clear();
}
}
return Output;
}
public string Decrypt(string EncryptedText)
{
RijndaelManaged Algorithm = null;
string Output = null;
try
{
Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);
byte[] KeyBytes = Convert.FromBase64String(EncryptedText);
using (MemoryStream msb = new MemoryStream(KeyBytes))
{
Algorithm = new RijndaelManaged();
Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
Algorithm.IV = ReadByteArray(msb);
Algorithm.Padding = PaddingMode.PKCS7;
ICryptoTransform Decryption = Algorithm.CreateDecryptor(Algorithm.Key, Algorithm.IV);
using (CryptoStream csb = new CryptoStream(msb, Decryption, CryptoStreamMode.Read))
{
using (StreamReader srb = new StreamReader(csb))
{
Output = srb.ReadToEnd();
}
}
}
}
finally
{
if (Algorithm != null)
{
Algorithm.Clear();
}
}
return Output;
}
public static string Sha512(string ToHash)
{
using (SHA512 SHA = new SHA512Managed())
{
byte[] HashByte = Encoding.UTF8.GetBytes(ToHash);
byte[] HashBytes = SHA.ComputeHash(HashByte);
string Hash = System.Text.Encoding.UTF8.GetString(HashBytes, 0, HashBytes.Length);
return Hash;
}
}
public static string Base64Encode(string data)
{
byte[] str = Encoding.UTF8.GetBytes(data);
return Convert.ToBase64String(str);
}
public static string Base64Decode(string data)
{
byte[] str = Convert.FromBase64String(data);
return Encoding.UTF8.GetString(str);
}
private byte[] ReadByteArray(Stream st)
{
byte[] Length = new byte[sizeof(int)];
st.Read(Length, 0, Length.Length);
byte[] Buffer = new byte[BitConverter.ToInt32(Length, 0)];
st.Read(Buffer, 0, Buffer.Length);
return Buffer;
}
}
}
I've been arguing with this today with smartcards (or more accurately, a Yubikey Neo with the smartcard PIV applet enabled); using this code:
var encryptor = (RSACryptoServiceProvider)c.PublicKey.Key;
var decryptor = (RSACryptoServiceProvider)c.PrivateKey;
var encrypt = encryptor.Encrypt(bytes, RSAEncryptionPadding.Pkcs1);
var decrypt = decryptor.Decrypt(encrypt, RSAEncryptionPadding.Pkcs1);
I've found that it matters what padding algo I use. If I use PKCS1 padding, everything works. If I use OaepSHA1, I get the Error while decoding [...] error. If I use anything else (e.g., OaepSHA256) I get a Not supported error.
I can only conclude that my smartcard doesn't properly support OAEP SHA1, but padding with PKCS#1 everything is good.
Even if this answers what you already know, it may be useful as another datapoint for anyone else coming along using smartcards.
Make sure your key size is not too small or too large.
See comments from MSDN
The RSACryptoServiceProvider supports key sizes from 384 bits to 16384
bits in increments of 8 bits if you have the Microsoft Enhanced
Cryptographic Provider installed. It supports key sizes from 384 bits
to 512 bits in increments of 8 bits if you have the Microsoft Base
Cryptographic Provider installed.
So you might need to pad short key strings with some bytes to get the minimum key length
Ok, I managed to check this and from what I can see, I have problems with some certificates. I am not sure why some certificates work while others don't. It would be good to know why some certificates fail in this case?
Anyway, I created a new self signed certificate using windows "Manage File Encryption Certificates" and used this certificate, and all seems to work.
The out put from your code.
Encryption Key: aUc/GXWDoh2LktaEGeCJfju1dHP118yD/fzfT0iJLuhOq2QeyGpG6m3aBHaxvdH0ufeXRHbMjmlmPgIL/bhABzkT2C5Oa6ZhY3IFXb5t7JXZ3AtUunvtNAnRyFJ7MzklrSZGgQ
vF67DSNfIVE17doKt6j6mkCpSco56ooZCrOs2Mp3vSXqNjvjiwMEfQbk41aYUNVNVNlBGhdNQCIZIAKezQCUpWqzn2II27FIDfqDIEW4ieyzpXC05GzUlGXDxFOiFUPk3n0Y94vgeF8AlCD74eyZtz
WQ==
Encrypted Body: EAAAANS/W7+GGRbT1q5NCYvZlDZYtxaA8g55HzUqP5qxhenn
Encrypted Decryption Key: vc/tcsApmY1503BFi7oSu/RDvZivA1Ed58KJuLoEC6eE8q0BIa6ye2JvtXyxkVbzzL0MA51pZ2ZhMIsfCnBsEDjCgy+JLTZTGM1Mv+em9frFUKb0zHbICnPUa/3H
yd1yOWsdn5ws19QN2dzC6eau+ExhT2T/vyZO4Nf9NdHKnB8n2yB1rrQ/T+N2EYCNH/AVPDAsme6JG7k9Od2XIipBXMyCgXgWYZmQusq+JQjA9d3c4CrQYcg/ERF+K3oZv/gPicBkAR5taxwSxAajGg
bpkJNsbhTMHTN9bOn333qZ6ojlo5e882baZXuZWPr9qtj1b7ONoOyuSx/OvGKjt93BQg==
Decrypted Body: Hello World!
Output String: Hello World!.
Hope that helps

C# Signing and verifying signatures with RSA. Encoding issue

My question is pretty similar to the one form 2011, Signing and verifying signatures with RSA C#. Nevertheless, I also get false when I compare the signed data and the original message. Please point on my mistake.
Code:
public static void Main(string[] args)
{
//Generate a public/private key pair.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Save the public key information to an RSAParameters structure.
RSAParameters RSAPublicKeyInfo = RSA.ExportParameters(false);
RSAParameters RSAPrivateKeyInfo = RSA.ExportParameters(true);
string message = "2017-04-10T09:37:35.351Z";
string signedMessage = SignData(message, RSAPrivateKeyInfo);
bool success = VerifyData(message, signedMessage, RSAPublicKeyInfo);
Console.WriteLine($"success {success}");
Console.ReadLine();
}
Signing method:
public static string SignData(string message, RSAParameters privateKey)
{
ASCIIEncoding byteConverter = new ASCIIEncoding();
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
// Write the message to a byte array using ASCII as the encoding.
byte[] originalData = byteConverter.GetBytes(message);
try
{
// Import the private key used for signing the message
rsa.ImportParameters(privateKey);
// Sign the data, using SHA512 as the hashing algorithm
signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
finally
{
// Set the keycontainer to be cleared when rsa is garbage collected.
rsa.PersistKeyInCsp = false;
}
}
// Convert the byte array back to a string message
return byteConverter.GetString(signedBytes);
}
Verification method:
public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
ASCIIEncoding byteConverter = new ASCIIEncoding();
byte[] bytesToVerify = byteConverter.GetBytes(originalMessage);
byte[] signedBytes = byteConverter.GetBytes(signedMessage);
try
{
rsa.ImportParameters(publicKey);
success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
return success;
}
Basically the problem is with string to byte[] encoding. I get the same problem with ASCIIEncoding and with UTF8Encoding.
Thank you in advance!
You cannot use ASCIIEncoding on the encoded message because it contains bytes which are invalid ASCII characters. The typical way you would store the encoded message is in a base64 string.
In SignData, use the following to encode the byte array into a string:
return Convert.ToBase64String(signedBytes);
and in VerifyData, use the following to decode the string back to the same byte array:
byte[] signedBytes = Convert.FromBase64String(signedMessage);

ArgumentOutOfRangeException when calling X509Certificate2

I tried create a X509Certificate2 object with a public rsa key for encryption in Unity with c#. I get the following exception:
> ArgumentOutOfRangeException: Cannot be negative.
> Parameter name: length
> System.String.Substring (Int32 startIndex, Int32 length) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/String.cs:348)
> Mono.Security.X509.X509Certificate.PEM (System.String type, System.Byte[] data) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/Mono.Security.X509/X509Certificate.cs:601)
.
static loadKey() {
//get rsa public key
byte[] data = GetBytes("MIIBIjANBgkqhk......EuH+zIXFzvirHQ2AxE/5wIDAQAB");
Debug.Log(data.Length);
X509Certificate2 x509certificate = new X509Certificate2(data);
//[...]
}
This is the GetBytes function
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
For the record: data.Length is 784
Any ideas?
Thanks for your help. A certificate is defenately not a key so i finally managed to get a working function to encrypt a string with bouncycastle:
static string Encrypt2(string publicKeyFileName, string inputMessage)
{
try
{
// Converting the string message to byte array
System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
byte[] inputBytes = enc.GetBytes(inputMessage);
AsymmetricKeyParameter publicKey = ReadAsymmetricKeyParameter(publicKeyFileName);
// Creating the RSA algorithm object
IAsymmetricBlockCipher cipher = new Pkcs1Encoding(new RsaEngine());
// Initializing the RSA object for Encryption with RSA public key. Remember, for encryption, public key is needed
cipher.Init(true, publicKey);
//Encrypting the input bytes
byte[] cipheredBytes = cipher.ProcessBlock(inputBytes, 0, inputBytes.Length);
return Convert.ToBase64String(cipheredBytes);
}
catch (Exception ex)
{
// Any errors? Show them
Debug.Log("Exception encrypting file! More info:");
Debug.Log(ex.Message);
}
return "";
}
public static AsymmetricKeyParameter ReadAsymmetricKeyParameter(string pemFilename)
{
var fileStream = System.IO.File.OpenText(pemFilename);
var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(fileStream);
var KeyParameter = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)pemReader.ReadObject();
return KeyParameter;
}

Bad data exception when decrypting using RSA with correct private and public key pair in C#

This is my code for decryption process:
private RSACryptoServiceProvider _rsa;
private string _privateKey;
private string _publicKey;
public RsaLibrary()
{
//initialsing the RSA object taking the option of a 1024 key size
_rsa = new RSACryptoServiceProvider(1024);
_privateKey = _rsa.ToXmlString(true);
_publicKey = _rsa.ToXmlString(false);
}
public string Decrypt(string ciphertext, string privateKey_ = null)
{
if (String.IsNullOrEmpty(privateKey_))
{
return DecryptToBytes(ciphertext, _privateKey);
}
else
{
return DecryptToBytes(ciphertext, privateKey_);
}
}
private string DecryptToBytes(string ciphertext, string privateKey)
{
if (String.IsNullOrEmpty(privateKey))
{
throw new ArgumentNullException("Error: No key provided.");
}
if (ciphertext.Length<=0)
{
throw new ArgumentNullException("Error: No message to decrypt.");
}
byte[] plaintext;
byte[] ciphertext_Bytes = Encoding.Unicode.GetBytes(ciphertext);
_rsa.FromXmlString(privateKey);
plaintext = _rsa.Decrypt(ciphertext_Bytes, false);
return Encoding.Unicode.GetString(plaintext);
}
The encryption code:
private string EncryptToByte(string plaintext, string publicKey)
{
if (String.IsNullOrEmpty(publicKey))
{
throw new ArgumentNullException("Error: No key provided.");
}
if (plaintext.Length<=0)
{
throw new ArgumentNullException("Error: No message to incrypt");
}
byte[] ciphertext;
byte[] plaintext_Bytes = Encoding.Unicode.GetBytes(plaintext);
_rsa.FromXmlString(publicKey);
ciphertext = _rsa.Encrypt(plaintext_Bytes, false);
return Convert.ToBase64String(ciphertext);
}
I can not see where I am going wrong. I have made sure that the keys are correct. The public one which i extracted using this line in the constructor:
_publicKey = _rsa.ToXmlString(false);
This public key is displayed on the form that I created. The private i used the "true" instead of false.
Any ideas?
Ciphertext is very unlikely to be genuinely UTF-16-encoded text. Assuming that the encryption side had something like:
string encryptedText = Encoding.Unicode.GetString(encryptedBytes);
you've basically lost data. The result of encryption is not text - it's arbitrary binary data. If you want to convert that to text for some transport reason, you should use Base64, e.g.
string base64EncryptedText = Convert.ToBase64String(encryptedBytes);
Then use Convert.FromBase64String to recover the original encrypted binary data which is ready to decrypt.

RijndaelEncryption with Java then Decryption with C# and EnterpriseLibrary 4.1

I believe when the EnterpriseLibrary tries to decrypt a RijndaelManaged encrypted string it expects the Initialization Vector to be prepended to the encrypted text. Currently with the code below. I can decrypt the message with out an exception, but I am getting weird characters like:
�猀漀椀搀㴀眀最爀甀戀攀㄀☀甀琀挀㴀㈀ ㄀ ⴀ㄀ ⴀ㈀㄀吀㄀㌀㨀㔀㈀㨀㄀㌀
What do I need to do to make this work? Any help is greatly appreciated. Here is some of the code I have...
I have a C# application that decrypts data using the EnterpriseLibrary 4.1 (encryption: RijndaelManaged).
string message = "This encrypted message comes from Java Client";
Cryptographer.DecryptSymmetric("RijndaelManaged", message);
The client encryptes the message, implemented in Java.
public String encrypt(String auth) {
try {
String cipherKey = "Key as a HEX string";
byte[] rawKey = hexToBytes(cipherKey);
SecretKeySpec keySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
String cipherIV = "xYzF5AqA2cKLbvbfGzsMwg==";
byte[] btCipherIV = Base64.decodeBase64(cipherIV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec (btCipherIV));
byte[] unencrypted = StringUtils.getBytesUtf16(auth);
byte[] encryptedData = cipher.doFinal(unencrypted);
String encryptedText = null;
byte[] entlib = new byte[btCipherIV2.length + encryptedData.length];
System.arraycopy(btCipherIV, 0, entlib, 0, btCipherIV.length);
System.arraycopy(encryptedData, 0, entlib, btCipherIV.length, encryptedData.length);
encryptedText = new String(encryptedData);
encryptedText = Base64.encodeBase64String(encryptedData);
return encryptedText;
} catch (Exception e) {
}
return "";
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(
str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
I found the answer. The problem in the above code:
StringUtils.getBytesUtf16(auth);
Instead the Enterprise Library is using Little Endian byte order. The function I was using doesn't. Instead I should have used:
StringUtils.getBytesUtf16Le(auth);
This solved my problem. Thanks for anyone who took a loot at this. I appreciate it!

Categories

Resources