I am creating a crypto class to use in both my chat client(Windows, .NET Framework) & server(Linux, .NET Core).
I figured I'd use BouncyCastle since its "Well documented", and since i need cross-platform which default lib doesn't support (CNG classes). So i got the key generation working (Haven't tested it cross-platform yet), but the encryption and decryption is not working dues to invalid key size.
I cant find any docs on this and have been stuck here for a while. Please point out if there's anything I'm doing horribly wrong
(I'm very new to crypto, C#... Well programming in general, been learning for 1 year)
I really hope my code isn't a total mess:
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Text;
namespace ChatClient
{
public class Crypto
{
private bool _ready = false;
private X9ECParameters m_x9EC;
private ECPublicKeyParameters m_myPubKey;
private AsymmetricKeyParameter m_myPrivKey;
private byte[] m_sharedSecret = null;
public Crypto()
{
// Get curve
m_x9EC = NistNamedCurves.GetByName("P-521");
ECDomainParameters ecDomain = new ECDomainParameters(m_x9EC.Curve, m_x9EC.G, m_x9EC.N, m_x9EC.H, m_x9EC.GetSeed());
// Create generator
ECKeyPairGenerator g = (ECKeyPairGenerator)GeneratorUtilities.GetKeyPairGenerator("ECDH");
g.Init(new ECKeyGenerationParameters(ecDomain, new SecureRandom()));
// Generate keypair
AsymmetricCipherKeyPair keyPair = g.GenerateKeyPair();
// Set keys
m_myPubKey = (ECPublicKeyParameters)keyPair.Public;
m_myPrivKey = keyPair.Private;
}
public void GenPrivateKey(byte[] key)
{
// Why whould anyone even...
if (key == null)
throw new ArgumentNullException();
// Split up the Base64-Encoded, comma-seperated crypto-coords of the server/client
string str = Encoding.UTF8.GetString(key);
string[] elements = str.Split(',');
if (elements.Length != 2)
throw new ArgumentException();
// Generate a key out of the coordinates
ECPoint point = m_x9EC.Curve.CreatePoint(
new BigInteger(Convert.FromBase64String(elements[0])),
new BigInteger(Convert.FromBase64String(elements[1]))
);
// Get public key
ECPublicKeyParameters remotePubKey = new ECPublicKeyParameters("ECDH", point, SecObjectIdentifiers.SecP521r1);
// Generate shared secret key
IBasicAgreement aKeyAgree = AgreementUtilities.GetBasicAgreement("ECDH");
aKeyAgree.Init(m_myPrivKey);
m_sharedSecret = aKeyAgree.CalculateAgreement(remotePubKey).ToByteArray();
// Debugging...
Console.WriteLine("Key: {0}", Convert.ToBase64String(m_sharedSecret));
// Authentication is done, class can now be used for encryption/decryption
_ready = true;
}
public byte[] GetPublicKey()
{
// Assemble the Base64-Encoded, comma-seperated crypto-coords to send to the other client/server
string str = string.Format(
"{0},{1}",
Convert.ToBase64String(m_myPubKey.Q.AffineXCoord.ToBigInteger().ToByteArray()),
Convert.ToBase64String(m_myPubKey.Q.AffineYCoord.ToBigInteger().ToByteArray())
);
// Return it
return Encoding.UTF8.GetBytes(str);
}
public byte[] Encrypt(byte[] unencryptedData)
{
// Keys need to be generated before we can start encrypting/decrypring, And please dont pass null into here...
if (!_ready || unencryptedData == null)
return null;
using (MemoryStream ms = new MemoryStream())
{
using (System.Security.Cryptography.AesManaged cryptor = new System.Security.Cryptography.AesManaged())
{
// Set parameters
cryptor.Mode = System.Security.Cryptography.CipherMode.CBC;
cryptor.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
// Get iv
byte[] iv = cryptor.IV;
// Encrypt the data
using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateEncryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))
cs.Write(unencryptedData, 0, unencryptedData.Length);
// Get stuff that was encrpyted
byte[] encryptedContent = ms.ToArray();
// Create a new array for the data + iv
byte[] result = new byte[iv.Length + encryptedContent.Length];
//copy both arrays into one
System.Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
System.Buffer.BlockCopy(encryptedContent, 0, result, iv.Length, encryptedContent.Length);
// Aaaand return it
return result;
}
}
return null;
}
public byte[] Decrypt(byte[] encryptedData)
{
// Keys need to be generated before we can start encrypting/decrypring, And please dont pass null into here...
if (!_ready || encryptedData == null)
return null;
// New arrays for iv and data
byte[] iv = new byte[16];
byte[] dat = new byte[encryptedData.Length - iv.Length];
// Get iv and data
System.Buffer.BlockCopy(encryptedData, 0, iv, 0, iv.Length);
System.Buffer.BlockCopy(encryptedData, iv.Length, dat, 0, dat.Length);
using (MemoryStream ms = new MemoryStream())
{
using (System.Security.Cryptography.AesManaged cryptor = new System.Security.Cryptography.AesManaged())
{
// Set parameters
cryptor.Mode = System.Security.Cryptography.CipherMode.CBC;
cryptor.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
// Decrypt the data
using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateDecryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))
cs.Write(encryptedData, 0, encryptedData.Length);
// Aaaand return it
return ms.ToArray();
}
}
return null;
}
}
}
Where this part:
// Encrypt the data
using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(ms, cryptor.CreateEncryptor(m_sharedSecret, iv), System.Security.Cryptography.CryptoStreamMode.Write))
Throws this error:
Exception thrown: 'System.ArgumentException' in System.Core.dll
An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll
The specified key is not a valid size for this algorithm.
Closing this question, it was answered by Topaco in the comments
In the code the shared secret is used directly as symmetric key, which causes the error due to the different sizes. Instead, the symmetric key (with the appropriate size) is usually derived from the shared secret, here and here.