.net to mono RSACryptoServiceProvider not working - c#

I'm currently porting one of my library to mono to make it usable with MonoMac.
I use RSA in this library, and it's not working the same way : it's working well with .Net Framework 4 but with mono it's not working anymore...
I'm using RSA to exchange keys, here is the server side code :
private void SecureConnection()
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
NetStream.Write(RSAKeyInfo.Modulus, 0, RSAKeyInfo.Modulus.Length);
NetStream.Flush ();
byte[] keyReceived = new byte[128];
byte[] IvReceived = new byte[128];
int position = 0;
while (position < 127) {
position += NetStream.Read (keyReceived, position, 128 - position);
}
position = 0;
while (position < 127) {
position += NetStream.Read (IvReceived, position, 128 - position);
}
byte[] realKey = RSA.Decrypt(keyReceived, true);
byte[] realIv = RSA.Decrypt(IvReceived, true);
Rijndael = new RijndaelManaged { Key = realKey, IV = realIv };
Encrypt = Rijndael.CreateEncryptor();
Decrypt = Rijndael.CreateDecryptor();
FullName = WaitAndGetString();
Debug.WriteLine("[TCP] TcpHandler -> Connection to {0} ({1}) secured successfully", TcpClient.RemoteEndPoint, FullName);
}
And here is the client side implementation :
private ConnectedClient SecureConnection(TcpClient toSecure)
{
NetworkStream netStr = toSecure.GetStream();
ConnectedClient cc = new ConnectedClient
{
TcpClient = toSecure,
NetStream = netStr
};
byte[] buffer = new byte[128];
int position = 0;
while (position < 127)
{
position += netStr.Read (buffer, position, 128 - position);
}
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters
{
Exponent = new byte[] {1, 0, 1},
Modulus = buffer
};
RSA.ImportParameters(RSAKeyInfo);
Rijndael rjindael = new RijndaelManaged();
rjindael.GenerateKey();
rjindael.GenerateIV();
byte[] keyToSend = RSA.Encrypt(rjindael.Key, true);
byte[] IvToSend = RSA.Encrypt(rjindael.IV, true);
netStr.Write(keyToSend, 0, 128);
netStr.Flush();
netStr.Write(IvToSend, 0, 128);
netStr.Flush();
cc.Encrypt = rjindael.CreateEncryptor();
cc.Decrypt = rjindael.CreateDecryptor();
cc.Rijndael = rjindael;
Debug.WriteLine("[TCP] ConnectedClient -> Connection to " + toSecure.Client.RemoteEndPoint + " secured successfully");
cc.WriteOnStream(SharedGlobals.FullUsername);
return cc;
}
The problem is on the client side :
byte[] realKey = RSA.Decrypt(keyReceived, true);
Is throwing a CryptographicException with OAEP decoding error
This exception is only thrown with Mono, not with standard .Net
Any ideas why ?

The issue was caused because the exponent is not the same between .Net and Mono. .Net always generate a byte[]{1,0,1} (at least from what I saw). So, now I send the exponent with the public key, it's working well.

Related

Problem with encryption and decryption with AES-gcm 128 using C# .NET framework

I am trying to work with Encryption and Decryption using AES-gcm 128 bits using C# .NET Framework. But I could not find any good solutions with this problem. I have just found on this website, someone recommends to use Bouncy castle library.
I have no ideas, if Bouncy castle is supported AES-gcm 128. And I sill don't know how to input this library in .NET framework.
Could anyone resolve this problem for me? I am a newbie so I really need your helps.
Thank you so much!!!
AES GCM 256 Runs in framework 5 and above... So, first write the program with framework 5 and above I have written a sample class to use it and the library using System.Security.Cryptography; I have added to this class
internal class class_Encrypt_Decrypt_AES_GCM
{
public byte[] Encrypt(String key, string payload, byte[] header_aad, byte[] tag)
{
byte[] cipherText = new byte[payload.Length];
try
{
//string key = "a05e19dc2682a72a8a9fe3d70707393e";Example
// string payload = "Hello";Example
// byte[] tag = new byte[16];Example
// byte[] header_aad = { 0x5a, 0xa5, 0x0, 0x6, 0x1a };Example
int payload_len = payload.Length;
byte[] bytes_key = Encoding.ASCII.GetBytes(key);
byte[] bytes_IV = new byte[16];
byte[] send_IV = new byte[12];
bytes_IV = IV_generator();
byte[] bytes_payload = new byte[payload.Length];
byte[] bytes_payload1 = Encoding.ASCII.GetBytes(payload);
Array.Copy(bytes_payload1, bytes_payload, bytes_payload1.Length);
Array.Copy(bytes_IV, send_IV, bytes_IV.Length - 4);
int num = payload.Length;
header_aad[3] = (byte)(num & 0xff);
//----------hi byte
header_aad[2] = (byte)(num >> 8 & 0xff);
//==========================================
AesGcm aesGcm = new AesGcm(bytes_key);
aesGcm.Encrypt(send_IV, bytes_payload, cipherText, tag, header_aad);
// String IV_STR = Convert.ToString(send_IV);
// String key_STR = Convert.ToString(bytes_key);
// String RES = Convert.ToString(cipherText);
//example for Decoder
// aesGcm.Decrypt(send_IV, cipherText, tag, bytes_payload, header_aad);
// string result = ASCIIEncoding.UTF8.GetString(bytes_payload);
}
catch (Exception e2)
{
int b = 0;
}
return cipherText;
}
private byte[] IV_generator()
{
byte[] byte_IV = new byte[16];
Random rnt = new Random(DateTime.Now.Day);
byte[] byte_time_rnd = new byte[16];
rnt.NextBytes(byte_time_rnd);
byte[] byet_sha384_date;
SHA384 shaM = new SHA384Managed();
byet_sha384_date = shaM.ComputeHash(Encoding.ASCII.GetBytes(DateTime.Now.Date.ToString()));
for (int i = 0; i < byte_time_rnd.Length; i++)
{
byte_IV[i] = (byte)(byet_sha384_date[i] ^ byte_time_rnd[i]);
}
return byte_IV;
}
In this class, there is also a function for making IV, in which I have used time parameters

Decrypt RSA encrypted value generated from .net in Android

I have gone through many posts here but did not find the right solution. I want to decrypt a value encrypted in c# .net from Android.
I have successfully decrypted in .net platform using the following code snippet
public static void Main()
{
string _privateKey = Base64Decode("myprivatekey");
var rsa = new RSACryptoServiceProvider();
string data = "198,47,144,175,154,47,194,175,242,41,212,150,220,177,198,161,236,36,197,62,18,111,21,244,232,245,90,234,195,169,141,195,139,199,131,163,26,124,246,50,102,229,73,148,18,110,170,145,112,237,23,123,226,135,158,206,71,116,9,219,56,96,140,19,180,192,80,29,63,160,43,127,204,135,155,67,46,160,225,12,85,161,107,214,104,218,6,220,252,73,252,92,152,235,214,126,245,126,129,150,49,68,162,120,237,246,27,25,45,225,106,201,251,128,243,213,250,172,26,28,176,219,198,194,7,202,34,210";
var dataArray = data.Split(new char[] { ',' });
byte[] dataByte = new byte[dataArray.Length];
for (int i = 0; i < dataArray.Length; i++)
{
dataByte[i] = Convert.ToByte(dataArray[i]);
}
rsa.FromXmlString(_privateKey);
var decryptedByte = rsa.Decrypt(dataByte, false);
Console.WriteLine(_encoder.GetString(decryptedByte));
}
Now I want to do the same process in Android app. Please can somebody guide me through this?
I have tried the following code but its throwing javax.crypto.IllegalBlockSizeException: input must be under 128 bytes exception
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decode(modulusString, DEFAULT);
byte[] exponentBytes = Base64.decode(publicExponentString, DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPrivateKeySpec rsaPubKey = new RSAPrivateKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey pubKey = fact.generatePrivate(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = result.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.decode(cipherData, DEFAULT).toString();

SecretKey generation for DESede/CBC/PKCS5Padding

I'm having problems when encrypting data to be sent to a SOAP service.
I suppose the problem is in the SecretKey generation, because the response is still the same with an incorrect password.
The current code is:
WServiceSoap ws = new WService().getWServiceSoap();
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest("12345678".getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
SecretKey opKey = new SecretKeySpec(keyBytes, "DESede");
byte[] opIV = { 0, 0, 0, 1, 2, 3, 4, 5 };
Cipher c = Cipher.getInstance("DESede/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, opKey, new IvParameterSpec(opIV));
byte[] encrypted = c.doFinal(
ClientDirectOperacionCTMS.DATOS_OPERACION.getBytes("UTF-8"));
String encryptedDatosOperacion= Base64.encodeBase64String(encrypted);
String result= ws.operacionCTMS(encryptedDatosOperacion);
System.out.println(result);
, and the exception is
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Server was unable to process request. ---> La operaciĆ³n de pago con tarjeta no ha sido satisfactoria. ---> Additional non-parsable characters are at the end of the string.
at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:111)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:108)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
I have also tried this code without success:
final byte[] keyBytes = Arrays.copyOf(Base64.decodeBase64("12345678"), 24);
SecretKey opKey = new SecretKeySpec(keyBytes, "DESede");
The decryption done by the server is
CheckKey(ref rgbKey);
cryptoProvider = new TripleDESCryptoServiceProvider();
cryptoTransform = cryptoProvider.CreateDecryptor(rgbKey, s_rgbIV);
memoryStream = new MemoryStream();
using (CryptoStream cryptoStream = new CryptoStream(
memoryStream, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(rgbEncryptedText, 0, rgbEncryptedText.Length);
}
originalText = Encoding.UTF8.GetString(memoryStream.ToArray());
, where CheckKey is
private static void CheckKey(ref Byte[] rgbKey)
{
if (rgbKey.Length > KEY_MAX_LENGTH)
{
Array.Resize(ref rgbKey, KEY_MAX_LENGTH);
}
else if (rgbKey.Length < KEY_MAX_LENGTH)
{
Byte fill = 0x41;
Int32 offset = rgbKey.Length;
Array.Resize(ref rgbKey, KEY_MAX_LENGTH);
for (Int32 index = offset; index < KEY_MAX_LENGTH; index++)
{
rgbKey[index] = fill++;
}
}
}

File Decryption Error: Bad Data

I am having some trouble getting a asp.net C# file encryption / decryption process to work. I can get the file uploaded and ecrypted, but cannot get the decryption to work.
I get the error: Exception Details: System.Security.Cryptography.CryptographicException: Bad Data. on the decryption line:
byte[] KeyDecrypted = rsa.Decrypt(KeyEncrypted, false);
Here is my encrypt function:
private void EncryptFile(string inFile)
{
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 256;
rjndl.Mode = CipherMode.CBC;
ICryptoTransform transform = rjndl.CreateEncryptor();
byte[] keyEncrypted = rsa.Encrypt(rjndl.Key, false);
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
int lKey = keyEncrypted.Length;
LenK = BitConverter.GetBytes(lKey);
int lIV = rjndl.IV.Length;
LenIV = BitConverter.GetBytes(lIV);
int startFileName = inFile.LastIndexOf("\\") + 1;
// Change the file's extension to ".enc"
string outFile = EncrFolder + inFile.Substring(startFileName, inFile.LastIndexOf(".") - startFileName) + ".enc";
lblDecryptFileName.Text = outFile;
using (FileStream outFs = new FileStream(outFile, FileMode.Create))
{
outFs.Write(LenK, 0, 4);
outFs.Write(LenIV, 0, 4);
outFs.Write(keyEncrypted, 0, lKey);
outFs.Write(rjndl.IV, 0, lIV);
using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
{
int count = 0;
int offset = 0;
int blockSizeBytes = rjndl.BlockSize / 8;
byte[] data = new byte[blockSizeBytes];
int bytesRead = 0;
using (FileStream inFs = new FileStream(inFile, FileMode.Open))
{
do
{
count = inFs.Read(data, 0, blockSizeBytes);
offset += count;
outStreamEncrypted.Write(data, 0, count);
bytesRead += blockSizeBytes;
}
while (count > 0);
inFs.Close();
}
outStreamEncrypted.FlushFinalBlock();
outStreamEncrypted.Close();
}
outFs.Close();
}
}
And here is the decrypt function where the error occurs.
private void DecryptFile(string inFile)
{
// Create instance of Rijndael for
// symetric decryption of the data.
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 256;
rjndl.Mode = CipherMode.CBC;
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
string outFile = DecrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".txt";
using (FileStream inFs = new FileStream(EncrFolder + inFile, FileMode.Open))
{
inFs.Seek(0, SeekOrigin.Begin);
inFs.Seek(0, SeekOrigin.Begin);
inFs.Read(LenK, 0, 3);
inFs.Seek(4, SeekOrigin.Begin);
inFs.Read(LenIV, 0, 3);
int lenK = BitConverter.ToInt32(LenK, 0);
int lenIV = BitConverter.ToInt32(LenIV, 0);
int startC = lenK + lenIV + 8;
int lenC = (int)inFs.Length - startC;
// Create the byte arrays for
// the encrypted Rijndael key,
// the IV, and the cipher text.
byte[] KeyEncrypted = new byte[lenK];
byte[] IV = new byte[lenIV];
// Extract the key and IV
// starting from index 8
// after the length values.
inFs.Seek(8, SeekOrigin.Begin);
inFs.Read(KeyEncrypted, 0, lenK);
inFs.Seek(8 + lenK, SeekOrigin.Begin);
inFs.Read(IV, 0, lenIV);
Directory.CreateDirectory(DecrFolder);
byte[] KeyDecrypted = rsa.Decrypt(KeyEncrypted, false);
ICryptoTransform transform = rjndl.CreateDecryptor(KeyDecrypted, IV);
using (FileStream outFs = new FileStream(outFile, FileMode.Create))
{
int count = 0;
int offset = 0;
int blockSizeBytes = rjndl.BlockSize / 8;
byte[] data = new byte[blockSizeBytes];
inFs.Seek(startC, SeekOrigin.Begin);
using (CryptoStream outStreamDecrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
{
do
{
count = inFs.Read(data, 0, blockSizeBytes);
offset += count;
outStreamDecrypted.Write(data, 0, count);
}
while (count > 0);
outStreamDecrypted.FlushFinalBlock();
outStreamDecrypted.Close();
}
outFs.Close();
}
inFs.Close();
}
}
Any help on this would be great! I am not an RSA encryption expert and have been reading a lot of posts but still not able to come up with a solution.
I have finally figured this out. The code worked well in a desktop application when I tried it there. It just didn't work in the asp.net 4 web application I was trying to write. The issue was the RSA object wasn't persisted through the session. So, the RSA object was created okay. The file was encrypted okay. But when I went to decrypt the file the RSA object was not there. The error message of System.Security.Cryptography.CryptographicException: Bad Data is misleading as that wasn't really the issue, the data was fine.
So, when creating the key and the RSA object I used the following:
rsa = new RSACryptoServiceProvider(cspp);
Session["rsa"] = rsa;
Next, when the decryption function is called I added in:
if (rsa == null)
rsa = (RSACryptoServiceProvider)Session["rsa"];
Of course, there is a little more code around this also so catch if there is no key for the RSA session, but this is the high level solution for the issue I was having.
If anyone is looking for this let me know and I can share more of the code.

C# RSA public Modulus/Exponent? (Bad Data)

I have tried and tried but I continue to get "Bad Data". How do you decrypt data using the RSACryptoServiceProvider with the public key's Exponent/Modulus?
public static byte[] Encrypt(byte[] b, byte[] mod, byte[] exp)
{
CspParameters csp = new CspParameters();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
RSACryptoServiceProvider.UseMachineKeyStore = false;
RSAParameters par = new RSAParameters();
par.Exponent = exp;
par.Modulus = mod;
rsa.ImportParameters(par);
return rsa.Encrypt(b, false);
}
public static byte[] Decrypt(byte[] b, byte[] pubexp, byte[] mod, byte[] priexp)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSACryptoServiceProvider.UseMachineKeyStore = false;
RSAParameters rp = new RSAParameters();
rp.Exponent = pubexp;
rp.D = priexp;
rp.Modulus = mod;
rsa.ImportParameters(rp);
return rsa.Decrypt(b, false);
}
static List<byte[]> Base2Array(string str)
{
byte[] b = Convert.FromBase64String(str);
List<byte[]> Bytes = new List<byte[]>();
int i = 0;
while (i < b.Length)
{
int size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(b, i));
i += 4;
byte[] b2 = new byte[size];
Array.Copy(b, i, b2, 0, size);
Bytes.Add(b2);
i += size;
}
return Bytes;
}
static void Main(string[] args)
{
List<byte[]> pub = Base2Array("AAAAB3NzaC1yc2EAAAABJQAAAIBMW4HxU1glv+CcZpJnvUKEyeNfFoKkyLOVLOOb/vNXQkrkGsNdpYAZkKKizij8fD3u3/iYT8UI+xkFoyonRYVipgCslirJB1VdvLivXs69Ht4vf7VAv2yJSUni3XsIHauMlfOkjJ7DpUW75ZkrxsGieICFWlXvRnAyDdqQrkZRZQ==");
List<byte[]> pri = Base2Array("AAAAgBSjHDNiojO3UXZg6Ux4VyrOx9SCn9mCWgykWTEUeR6Anp6DxhlPUw3UEEetVy97hlw8iGCEQxcvG4T7qocni9UtUTLdpuQzvr6718y2CP0ouKt/1hVKD9QssT08XUvJEBQnnl2yVZAbIqT/DGnUH36L0BnQE/2ombPakwHscfFFAAAAQQCSfQy2cP8Oa0IR0u0whxqGmuuXY/3tCD8NaaSCYm31ly0QBdxFdf2WkC51DNVaf5/1ErHceMapFN9Z3j+/6lA7AAAAQQCFcMoSA32f240nFBmMv/nn/mL1uSdAXOxjUHViJc6M8ntZvW2ZuP2qTvfA3mh1AK5K69piX/4T72xxqTA2tmrfAAAAQFxX1JunFI+fpobdienVCZcjibwbpDPf1MVTbwQhAXHqVBL3XXgkysS/67X/aWnv/+SdBDaXa1SnDpphSWOkxAQ=");
//pub[0] 7
//pub[1] 1
//pub[2] 128
//pri[0] 128
//pri[1] 65
//pri[2] 65
//pri[3] 64
byte[] pubmod = null;
byte[] primod = null;
byte[] pubexp = null;
byte[] priexp = null;
pubexp = pub[0];
pubmod = pub[2];
priexp = pri[0];
primod = pri[2];
byte[] bstr = Encoding.ASCII.GetBytes("Test");
bstr = Encrypt(bstr, pubmod, pubexp);
bstr = Decrypt(bstr, pubexp, pubmod, null);
string str = Encoding.ASCII.GetString(bstr);
}
i do something like that:
public byte[] PublicDecryption(byte[] encryptedData)
{
var encData = new BigInteger(encryptedData);
BigInteger bnData = encData.modPow(_exponent, _modulus);
return bnData.getBytes();
}
public byte[] PrivateDecryption(byte[] encryptedData)
{
var encData = new BigInteger(encryptedData);
d = new BigInteger(rsaParams.D);
BigInteger bnData = encData.modPow(d, _modulus);
return bnData.getBytes();
}
where BigInteger is this:
http://www.codeproject.com/KB/cs/biginteger.aspx
because microsoft implementation is partial and buggy.
I've never had a problem with this.
Hope this help
Few month ago I have tried to implement scenario with private key encryption and public key decryption. I spent a week trying doing this with RSACryptoServiceProvider and.. nothing. They support only two use cases:
Public key encryption , private (full) key decryption.
Signing with private key, verifying with public.
And they have done everything to not allow you do something else with their API.
Please, check out msdn forums:
I have found many answers like that one msdn, including answers from support development team. All they said: that this is prohibit by design. So my suggestion is don't even try to do that with RSACryptoServiceProvider, better use another implementation.

Categories

Resources