I need to implement key exchange through an I2C interface with a microcontoller, with the keys transferred byte by byte. I am working in Visual Studio 2022, C#. Because I need access to the Secret Agreement (ECDH), I can't use the Visual Studio encryption library, so have turned to Bouncy Castle. I am able to get the two parts of the public key into Byte arrays to send to the micro - example code shown - but have not been able to figure out how to put the public key received from the micro into a Bouncy Castle public key object to use in generating a secret agreement.
public void TestBC()
{
String SecretString = "";
int j;
AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> InitiatorKey = GenerateECDHKeyPair();
AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> RecipientKey = GenerateECDHKeyPair();
String RecipPublicKeyX = RecipientKey.PublicKey.W.XCoord.ToString();
String RecipPublicKeyY = RecipientKey.PublicKey.W.YCoord.ToString();
MessageBox.Show(RecipPublicKeyW + "\r\n" + RecipPublicKeyX + "\r\n" + RecipPublicKeyY);
Byte[] SecretAgreement = GetSecretAgreement(InitiatorKey.PrivateKey, RecipientKey.PublicKey);
Byte[] publicX = ConvertHexStringToByteArray(RecipPublicKeyX);
Byte[] publicY = ConvertHexStringToByteArray(RecipPublicKeyY);
for(j=0; j<SecretAgreement.Length; j++)
SecretString += String.Format("{0:X2}",SecretAgreement[j]);
MessageBox.Show(SecretString);
}
public AsymmetricKeyPair<AsymmetricECPublicKey, AsymmetricECPrivateKey> GenerateECDHKeyPair()
{
FipsEC.KeyGenerationParameters KeyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
FipsEC.KeyPairGenerator KpGen = CryptoServicesRegistrar.CreateGenerator(KeyGenParameters, new Org.BouncyCastle.Security.SecureRandom());
return KpGen.GenerateKeyPair();
}
public static byte[] GetSecretAgreement(AsymmetricECPrivateKey InitiatorPrivate, AsymmetricECPublicKey RecipientPublic)
{
IAgreementCalculatorService agreeFact = CryptoServicesRegistrar.CreateService(InitiatorPrivate);
IAgreementCalculator<FipsEC.AgreementParameters> agreement = agreeFact.CreateAgreementCalculator(FipsEC.Cdh);
return agreement.Calculate(RecipientPublic);
}
public static byte[] ConvertHexStringToByteArray(string hexString)
{
if (hexString.Length % 2 != 0)
{
throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The hex string cannot have an odd number of digits: {0}", hexString));
}
byte[] data = new byte[hexString.Length / 2];
for (int index = 0; index < data.Length; index++)
{
string byteValue = hexString.Substring(index * 2, 2);
data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return data;
}
Visual Studio intellisense does not show any constructors, properties, or methods for the AsymmetricECPublicKey object that will allow me to put in the pbulic key from a Byte array (or string). And the agreement calculator appears to require a public key object. I found a couple of similar questions already posted, but they did not help me. Does anyone know how to use a Byte array or string to do this?
Preliminary note: The question and answer refer to the C#/BouncyCastle FIPS version and not the regular C#/BouncyCastle version.
If the public key is given as raw x and y coordinates, it should first be converted to the uncompressed format. The uncompressed format is given by the concatenation of a leading 0x04 byte, the x coordinate and the y coordinate. The last two are 32 bytes each for P-256 (if the x coordinate is smaller, it must be padded from the front with 0x00 to 32 bytes; the same applies to the y coordinate):
Byte[] publicX = ...;
Byte[] publicY = ...;
Byte[] uncompressedKey = new byte[1 + 32 + 32]; // pad too short values (i.e. values < 32 bytes) with leading 0x00
uncompressedKey[0] = 0x04;
Buffer.BlockCopy(publicX, 0, uncompressedKey, 1 + (32 - publicX.Length), publicX.Length);
Buffer.BlockCopy(publicY, 0, uncompressedKey, 1 + 32 + (32 - publicY.Length), publicY.Length);
The uncompressed key can then be imported as follows:
FipsEC.KeyGenerationParameters keyGenParameters = new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P256).For(FipsEC.Cdh);
AsymmetricECPublicKey publicKey = new AsymmetricECPublicKey(FipsEC.Alg, KeyGenParameters.DomainParameters, KeyGenParameters.DomainParameters.Curve.DecodePoint(uncompressedKey));
For completeness: It is also possible to convert the key to the compressed format and import it. The compressed format is the concatenation of a leading 0x02 byte (if y is even) or 0x03 byte (if y is odd) and the x coordinate (with this information the public key is uniquely determined, since from this information the y coordinate can be reconstructed).