ECDH nodejs and C# key exchange - c#

I've lost my self and I need help to go in the right direction :)
I have nodejs server that have to exchange some critical data with the server that is written in C#, so in that case, I want my data to be encrypted somehow. I was thinking about AES, and to safely exchange keys i want to use ECDH but I don't know how to make it work correctly... If I'm thinking right way I can make my C# "allice" side like this :
ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP192t1);
var aliceKey = alice.PublicKey;
var ketyToExport = Convert.ToBase64String(aliceKey.ToByteArray());
//send ketyToExport to nodejs
//KEY FROM nodejs
var key1 = Convert.FromBase64String("BIzSJ1dmTXpSS8rqkVFISZ+vumhqXYMWfWoU5vB9GHhOtxxDInu/GZ92VJpqhrE3vw==").ToList();
var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x31 };
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
key1.RemoveAt(0);
key1 = keyType.Concat(keyLength).Concat(key1).ToList();
byte[] bobKeyBytes = key1.ToArray();
//and I have a problem with that line bellow, I do not know how to make it work
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
And nodejs "Bob" side like this:
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP192t1')
const bobKey = bob.generateKeys('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var tmp = "RUNLUBgAAAAR9C7kO2o+vxNT/UBvvEuJHNdI8NfU4fUxUT431ER1q3kJbeUVHepoG5SWUM2NHj8="
var aliceKeyBuffer = Buffer.from(tmp, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
bob.computeSecret(aliceKey);
//create aes
//get mesage and iv ...
Okay so I've made some adjustments to all of that but right now I don't know what to do about this line how to make it work...
var aliceSecret = alice.DeriveKeyMaterial(/*ECDiffieHellmanPublicKey bobKeyBytes*/);
#BIG EDIT
I got help in ECDiffieHellmanPublicKey from ByteArray (using ECDiffieHellman NamedCurves)
and now I have another problem -_-
my node js code didn't change from above but c# looks like:
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//NODEJS brainpoolP256r1 publickey
var key1 = Convert.FromBase64String("BASsbkule53ARqlMBA8hYysyyoRi3xGxGnSzIJ2fS5FlLniQD/zYiiGUVydmO/BBkQwVTUo5f4OMCxVNtQ/LuMQ=");
byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
ECParameters parameters = new ECParameters
{
Curve = ECCurve.NamedCurves.brainpoolP256r1,
Q =
{
X = keyX,
Y = keyY,
},
};
byte[] derivedKey;
using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
var aliceKey = Convert.ToBase64String(derivedKey);
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
and it is working but it gives me different secret keys ...
out of bob.computeSecret(aliceKey) i got iIoH9aJoWf3666QQ6X+kj4iUKrk9j+hbRuXbhgs7YzM=
and out of alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
I got wJ7O4Hm2Jxs1FcLx6KaMmENvqdTQJPZ/YNSs1+MQDOQ=
if I'm thinking correctly they should be equal. am I thinking wrong?
#EDIT DONE !!
So this adding this code on end of js file gave me what I needed.
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
hash.update(tt);
console.log(hash.digest('base64'));

##SOLUTION##
C#
class Program
{
static void Main(string[] args)
{
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//send alicePublicKey
var nodejsKey = ""; //NODEJS brainpoolP256r1 publickey base64
byte[] nodejsKeyBytes= Convert.FromBase64String(nodejsKey);
var aliceKey = Convert.ToBase64String(getDeriveKey(nodejsKeyBytes,alice));
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
}
static byte[] getDeriveKey(byte[] key1, ECDiffieHellman alice)
{
byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
ECParameters parameters = new ECParameters
{
Curve = ECCurve.NamedCurves.brainpoolP256r1,
Q =
{
X = keyX,
Y = keyY,
},
};
byte[] derivedKey;
using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
return derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
}
}
NODEJS
const crypto = require("crypto");
const bob = crypto.createECDH('brainpoolP256r1')
bob.generateKeys();
const bobKey = bob.getPublicKey('base64');
var bobLength = Buffer.from(bobKey, 'base64').length;
//send bobkey to c#
//recive alicekey
var alicePublicKey = "RUNLUCAAAAB/xP7JhSIhYIYAijyC2zHu7obB5CwfK/ynQPxcRAIhBI6OLRRcHyPo61AhfSZN3qA2vGDfWO2mrdWWvqqhVaDf";
var aliceKeyBuffer = Buffer.from(alicePublicKey, 'base64');
var aliceKey = Buffer.alloc(bobLength)
aliceKeyBuffer.copy(aliceKey, 1, 8);
aliceKey[0] = 4;
const hash = crypto.createHash('sha256');
var tt = bob.computeSecret(aliceKey);
var bobSecretKey = hash.update(tt).digest('base64');
big thanks for #bartonjs and #Maarten Bodewes

The above answer helped me a lot. I am using secp521r1/nistP521 to do the key generation in both NodeJS and C#. In my case, a call to alice.PublicKey.ToByteArray() would result in an exception:
Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.Security.Cryptography.ECDiffieHellmanImplementation.ECDiffieHellmanSecurityTransforms.ECDiffieHellmanSecurityTransformsPublicKey.ToByteArray()
According to an issue logged with the dotnet runtime team here:
ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific, and only works for NIST P-256 (secp256r1), NIST P-384 (secp384r1), and NIST P-521 (secp521r1).
To create a copy of a key you should use the ExportParameters() method to obtain the rich ECParameters object; which can then be sent through ECDiffieHellman.Create(ECParameters) and the PublicKey property of that object be read to get back a second instance of ECDiffieHellmanPublicKey.
Assuming I send my X and Y from Alice to Bob (I used hex instead of base64), the corresponding process to arrive at the same shared secret on the NodeJS side looks like this:
// Alice's public key X, Y coordinates from DotNet Core
const aliceHexX = '00248F624728B17196B22005742C13D80D3DFF75BCA74AF865195E5A29F41C013653B0931BC544245402EDD7922F38692F38DCCF780CF1A9E27D3CFB5E09E53883C0';
const aliceHexY = '0043517F3B2EF33ED70EFA2BC4163E9B99558A7C2ECB7C659A12EA4024633CFA8B9FC997F0A42D30759B4280FDADC13A67A3E7BB0227047C907FAAE92E7716E8A10D';
const alicePublicKeyXBytes = Buffer.from(aliceHexX, 'hex');
const alicePublicKeyYBytes = Buffer.from(aliceHexY, 'hex');
const alicePublicKey = Buffer.concat([Buffer.from([0x04]), alicePublicKeyXBytes, alicePublicKeyYBytes])
const bobSecret = bob.computeSecret(alicePublicKey);
const hash = crypto.createHash('sha256');
hash.update(bobSecret);
const sharedSecret = hash.digest('hex');
console.log(`Shared Secret: ${sharedSecret.toString('hex')}`);

Related

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();

AES/Rijndael-Decryption in C#, Decryption in JavaScript (Node)

I'm currently facing a problem, where I have to decode encrypted files in a Node-Backend. The encryption code is available in C# and looks in it's basics like
private static byte[] DeCrypt(byte[] encdata, string pw)
{
RijndaelManaged rd = new RijndaelManaged();
int rijndaelIvLength = 16;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] key = md5.ComputeHash(Encoding.UTF8.GetBytes(pw));
md5.Clear();
MemoryStream ms = new MemoryStream(encdata);
byte[] iv = new byte[16];
ms.Read(iv, 0, rijndaelIvLength);
rd.IV = iv;
rd.Key = key;
CryptoStream cs = new CryptoStream(ms, rd.CreateDecryptor(), CryptoStreamMode.Read);
byte[] data = new byte[ms.Length - rijndaelIvLength + 1];
int i = cs.Read(data, 0, data.Length);
cs.Close();
rd.Clear();
rd.Clear();
return data;
}
encdata are the bytes of the encrypted file, pw is a fixed password. My Node-function (including different attempts of encryption) looks like following:
const CryptoJS = require('crypto-js');
const fs = require('fs');
const converter = require('convert-string');
const Rijndael = require('rijndael-js');
const padder = require('pkcs7-padding');
const crypto = require('crypto');
async function decrypt(filePath, password) {
const encryptedFile = fs.readFileSync(filePath).buffer;
const chunks = [];
for await (let chunk of fs.createReadStream(filePath, { start: 0, end: 15 })) {
chunks.push(chunk);
}
const iv = Buffer.concat(chunks);
const hex = CryptoJS.MD5(password).toString();
const keyChunks = [];
for (var i = 0; i < hex.length; i += 2) {
keyChunks.push(parseInt(hex.substr(i, 2), 16));
}
const key = Buffer.from(keyChunks);
const decipher = new Rijndael(key, 'cbc');
const decryptedPadded = decipher.decrypt(encryptedFile, 128, iv);
//Remember to un-pad result
const decrypted = padder.unpad(decryptedPadded, 32);
const clearText = decrypted.toString('utf8');
const paddedText = decryptedPadded.toString('utf16le');
const plaidddnText = CryptoJS.enc.Utf16LE.stringify(decrypted);
const testttt = crypto.createCipheriv("aes128", key, iv);
const decrpyted = Buffer.concat([testttt.update(Buffer.from(encryptedFile)), testttt.final()]);
const testdec = decrypted.toString();
const bla = CryptoJS.AES.decrypt(encryptedFile, key, { keySize: 16, iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
const plainText = CryptoJS.enc.Utf16LE.stringify(bla);
}
When debugging both codes, I already manage that the byte arrays of the file (encdata in C#, encryptedFile in JS), and the byte arrays of iv and key are already the same, but somehow the final decryption is delivering different results. Anybody any ideas what I'm doing wrong in the final step?
Any help much appreciated! :)
As pointed out the solution is to change the unpadding to 16 bytes and decrypt from a Buffer:
const decryptedPadded = Buffer.from(decipher.decrypt(encryptedFile, 128, iv));
const decrypted = padder.unpad(decryptedPadded, 16);
const clearText = decrypted.toString('utf16le');
Now I only need to remove the first few bytes with the IV :)

ECDiffieHellmanPublicKey from ByteArray (using ECDiffieHellman NamedCurves)

I'm working on communication nodejs -> c# server.
I need to secure connection between them so I chode ECDiffieHellman as the key exchange mechanism (nodejs supports it). I had some problem with it... Just my lack of knowledge so I've made my lesson and now I can generate and export keys as base64 and nodejs have no problem with accepting c# key but on the other side c# ... won't even take his own key ...
error System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
Ye I know I'm making sth wrong but what?
using (ECDiffieHellman alice = ECDiffieHellman.Create(ECCurve.NamedCurves.brainpoolP256r1))
{
var alicePublicKey = Convert.ToBase64String(alice.PublicKey.ToByteArray());
//NODEJS brainpoolP256r1 publickey
var key1 = Convert.FromBase64String("BB92GQLod55fXEhgNxwQcPQFFvph7eIjnSzdNz2PhzUAOcaPEiLBPQR6AL5pqVLFram8OtPapoBGYZn2vaGl+/U=").ToList();
//test
var key2 = Convert.FromBase64String(alicePublicKey);
var keyType = new byte[] { 0x45, 0x43, 0x4B, 0x50 };
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
key1.RemoveAt(0);
key1 = keyType.Concat(keyLength).Concat(key1).ToList();
byte[] bobKeyBytes = key1.ToArray();
ECDiffieHellmanPublicKey k = ECDiffieHellmanCngPublicKey.FromByteArray(bobKeyBytes, new CngKeyBlobFormat("ECCPUBLICBLOB")); //error System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
ECDiffieHellmanPublicKey kk = ECDiffieHellmanCngPublicKey.FromByteArray(key2, new CngKeyBlobFormat("ECCPUBLICBLOB")); // error System.Security.Cryptography.CryptographicException: 'The parameter is incorrect.'
byte[] aliceKey = alice.DeriveKeyMaterial(k);
byte[] encryptedMessage = null;
byte[] iv = null;
// Send(aliceKey, "Secret message", out encryptedMessage, out iv);
}
you can find rest of the story there
ECDH nodejs and C# key exchange
You're asserting that the base64 contents that go into key1 are for brainpoolP256r1.
Decoding the value we see that it's a 65 byte payload starting with 04, which looks like an uncompressed point encoding for a curve with a 256-bit prime. So far so good.
You've even correctly used BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC, but you can't import a "generic named key blob" without specifying the import property that tells it which curve.
The easy way that you load the key from this point is
byte[] keyX = new byte[key1.Length / 2];
byte[] keyY = new byte[keyX.Length];
Buffer.BlockCopy(key1, 1, keyX, 0, keyX.Length);
Buffer.BlockCopy(key1, 1 + keyX.Length, keyY, 0, keyY.Length);
ECParameters parameters = new ECParameters
{
Curve = ECCurve.NamedCurves.brainpoolP256r1,
Q =
{
X = keyX,
Y = keyY,
},
};
byte[] derivedKey;
using (ECDiffieHellman bob = ECDiffieHellman.Create(parameters))
using (ECDiffieHellmanPublicKey bobPublic = bob.PublicKey)
{
derivedKey = alice.DeriveKeyFromHash(bobPublic, HashAlgorithmName.SHA256);
}
I've gone ahead and expanded the DeriveKeyMaterial method into what it means by default with an ECDiffieHellmanCng, since other types of ECDH don't support that method (due to its low specificity of behavior).

Cryptopp Ephemeral Diffie Hellman to C# Bouncy Castle

I'm trying to convert cryptopp DiffieHellman 2 key agreement method to c# bouncy castle library.
Here is the help page about cryptoo c++ library: http://www.cryptopp.com/wiki/Diffie-Hellman
I'm trying to implement RFC 5114's 1024-bit MODP group to C#.
But there are a lot of problems I couldn't solve.
When keyPair generates a key, it is 131 bytes, but it must be 128 bytes, because server sending to me 256 bytes key with static and ephemeral key. I must send 256 bytes too .But 1-, 2-, and 3-byte values are static in every key so I'm removing first 3 bytes in keys is it true?
Which secret key must I use for converting shared secret to other encryption system keys?
Example I have a secret key, how can I convert it for Twofish, RC6, xTEA, Serpent etc?
Here is my code:
public byte[] CreateaNewDiffieHellmanKey()
{
public static string Phex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371";
public static string Ghex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5";
public static string Qhex = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353";
BigInteger P = new BigInteger(Phex, 16);
BigInteger G = new BigInteger(Ghex, 16);
BigInteger Q = new BigInteger(Qhex, 16);
IAsymmetricCipherKeyPairGenerator staticKeyGen = GeneratorUtilities.GetKeyPairGenerator("DH");
IAsymmetricCipherKeyPairGenerator ephemeralKeyGen = GeneratorUtilities.GetKeyPairGenerator("DH");
DHParameters dhParams = new DHParameters(P, G, Q, 0, 160);
DHP = dhParams;
KeyGenerationParameters kgpSt = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
KeyGenerationParameters kgpEp = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
staticKeyGen.Init(kgpSt);
ephemeralKeyGen.Init(kgpEp);
AsymmetricCipherKeyPair staticKeyPayir = staticKeyGen.GenerateKeyPair();
staticKeyEgri = AgreementUtilities.GetBasicAgreement("DH");
staticKeyEgri.Init(staticKeyPayir.Private);
AsymmetricCipherKeyPair ephemeralKeyPair = ephemeralKeyGen.GenerateKeyPair();
ephemeralKeyEgri = AgreementUtilities.GetBasicAgreement("DH");
ephemeralKeyEgri.Init(staticKeyPayir.Private);
AsymmetricKeyParameter StaticPublicKey = staticKeyPayir.Public;
SubjectPublicKeyInfo StaticPublicKeyinfomuz = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(StaticPublicKey);
byte[] st1 = StaticPublicKeyinfomuz.PublicKeyData.GetBytes();
byte[] staticPublic = new byte[128];
Array.Copy(st1, 3, staticPublic, 0, staticPublic.Length);
AsymmetricKeyParameter EphPublicKey = staticKeyPayir.Public;
SubjectPublicKeyInfo EphPublicKeyinfomuz = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(EphPublicKey);
byte[] ep1 = StaticPublicKeyinfomuz.PublicKeyData.GetBytes();
byte[] ephemeralPublic = new byte[128];
Array.Copy(ep1, 3, ephemeralPublic, 0, ephemeralPublic.Length);
return Bot.Birleştir(staticPublic, ephemeralPublic); // Combine 2 byte arrays
}
public bool AgreeTheKey(byte[] buffer)
{
byte[] staticpublic = new byte[128];
byte[] ephemeralpublic = new byte[128];
Array.Copy(buffer, 0, staticpublic, 0, staticpublic.Length);
Array.Copy(buffer, staticpublic.Length, ephemeralpublic, 0, ephemeralpublic.Length);
ICipherParameters istatic = new KeyParameter(staticpublic);
ICipherParameters iphemeral = new KeyParameter(ephemeralpublic);
DHPublicKeyParameters dhkpST = new DHPublicKeyParameters(new BigInteger(staticpublic), DHP);
DHPublicKeyParameters dhkpEP = new DHPublicKeyParameters(new BigInteger(staticpublic), DHP);
SharedStatic = staticKeyEgri.CalculateAgreement(dhkpST).ToByteArray();
SharedEphemeral = ephemeralKeyEgri.CalculateAgreement(dhkpEP).ToByteArray();
byte[] Sharedkey = SharedStatic;
return true;
}
If you don't want to have the added encoding, you may always cast from AsymmetricKeyParameter to DHPublicKeyParameters and retrieve Y using getY(). Of course, after getting the value as an integer, you may still want to encode it to a fixed number of octets (bytes). In general, unsigned, left padded big endian encoding is used for the numbers.

.net to mono RSACryptoServiceProvider not working

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.

Categories

Resources