Panos Antoniou Panos Antoniou - 3 months ago 20
C# Question

Processing BigInteger issues

Assume the following Diffie-Hellman info which can also be found on this page

1)P

string givenp = "00e655cc9e04f3bebae76ecca77143ef5c4451876615a9f8b4f712b8f3bdf47ee7f717c09bb5b2b66450831367d9dcf85f9f0528bcd5318fb1dab2f23ce77c48b6b7381eed13e80a14cca6b30b5e37ffe53db15e2d6b727a2efcee51893678d50e9a89166a359e574c4c3ca5e59fae79924fe6f186b36a2ebde9bf09fe4de50453";
BigInteger p = new BigInteger(HexToBytesv2(givenp));


2)G

BigInteger g = new BigInteger(2);


3)Merchant private key

string merchantPrivateKeyHEX = "48887dfd090d175e33beea29e7b38334299289069f9ab492b67807905faa98d96d22d79205bef03f14af093f1797b904734132c34a388fdc79e20497bfa1465fec2aac4fabdf3bb0c9be8685d20f7bfe0346a9abdf7fa89838c3fa9ca6abdb70bea66795ab6699cc154db59490e4159f142f7bddff603c1d3d6c4fff8177e11d";
BigInteger a = new BigInteger(HexToBytesv2(merchantPrivateKeyHEX));


Using the formula
publickey = g ^ a mod p
I should get the public key provided in the initial link, yet when executing

BigInteger A = BigInteger.ModPow(g, a, p);
ToHex(A.ToByteArray())


the result I get is

00f85c41e84446ecfe43c9911df31d3cf60d83642afd496b741363290139badf75f8b8c5c010dda2446dd483dc553b6c2698c16c9d082391677785f81d54bc9c7c45f8b6d5bdb3e49fec7f5522b880c8c753fb7d3ff2c81e47dcb27d52842def40a812dc95cc679575baf237a955ee9944bd0797326f2a0a58c6c087f9b0b9e82c


instead of

00d9abd78c93dfddeb920d57d6513126d8f1118c9237a45101408dbffe6cfd95b011a016e4e0ab8aef0601e836a452b8bb88be7ca71e4f22f97aa65f8358ee69348d1227d65db6e53641d1a6542aa4be4b4adc75fac816af79a8e3f5097f8313e7b725df37eadc8c774e2033dfa99c95ccef333bf402b066198c30481e2a83875c


Any ideas? I must be missing pretty obvious but I am not sure what that might be.

P.S. Adding the function being used:

public static byte[] HexToBytesv2(this string hex)
{
if (hex.Length % 2 == 1)
hex = '0' + hex;

byte[] ret = new byte[hex.Length / 2];

for (int i = 0; i < ret.Length; i++)
ret[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);

return ret;

}

public static string ToHex(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();

}

Answer

It's an endian problem.

I've adjusted your encoding and decoding and now get the answer you're looking for:

public static byte[] HexToBytesv2(string hex)
{
    if (hex.Length % 2 == 1)
        hex = '0' + hex;

    byte[] ret = new byte[hex.Length / 2];

    for (int i = 0; i < ret.Length; i++)
        ret[i] = Convert.ToByte(hex.Substring(hex.Length - (i+1) * 2, 2), 16);

    return ret;

}

public static string ToHex( byte[] bytes)
{
    var sb = new StringBuilder();
    foreach (var b in bytes.Reverse())
    {
        sb.AppendFormat("{0:x2}", b);
    }
    return sb.ToString();
}

FYI I used LinqPad and the main method is your code from the question (as adjusted) with checks that the data has not lost anything on the way:

void Main()
{
    string givenp = "00e655cc9e04f3bebae76ecca77143ef5c4451876615a9f8b4f712b8f3bdf47ee7f717c09bb5b2b66450831367d9dcf85f9f0528bcd5318fb1dab2f23ce77c48b6b7381eed13e80a14cca6b30b5e37ffe53db15e2d6b727a2efcee51893678d50e9a89166a359e574c4c3ca5e59fae79924fe6f186b36a2ebde9bf09fe4de50453";
    BigInteger p = new BigInteger(HexToBytesv2(givenp));
    (ToHex(p.ToByteArray()) == "00e655cc9e04f3bebae76ecca77143ef5c4451876615a9f8b4f712b8f3bdf47ee7f717c09bb5b2b66450831367d9dcf85f9f0528bcd5318fb1dab2f23ce77c48b6b7381eed13e80a14cca6b30b5e37ffe53db15e2d6b727a2efcee51893678d50e9a89166a359e574c4c3ca5e59fae79924fe6f186b36a2ebde9bf09fe4de50453").Dump();
    BigInteger g = new BigInteger(2);
    string merchantPrivateKeyHEX = "48887dfd090d175e33beea29e7b38334299289069f9ab492b67807905faa98d96d22d79205bef03f14af093f1797b904734132c34a388fdc79e20497bfa1465fec2aac4fabdf3bb0c9be8685d20f7bfe0346a9abdf7fa89838c3fa9ca6abdb70bea66795ab6699cc154db59490e4159f142f7bddff603c1d3d6c4fff8177e11d";
    BigInteger a = new BigInteger(HexToBytesv2(merchantPrivateKeyHEX));
    (ToHex(a.ToByteArray()) == "48887dfd090d175e33beea29e7b38334299289069f9ab492b67807905faa98d96d22d79205bef03f14af093f1797b904734132c34a388fdc79e20497bfa1465fec2aac4fabdf3bb0c9be8685d20f7bfe0346a9abdf7fa89838c3fa9ca6abdb70bea66795ab6699cc154db59490e4159f142f7bddff603c1d3d6c4fff8177e11d").Dump();
    BigInteger A = BigInteger.ModPow(g, a, p);
    (ToHex(A.ToByteArray()) == "00f85c41e84446ecfe43c9911df31d3cf60d83642afd496b741363290139badf75f8b8c5c010dda2446dd483dc553b6c2698c16c9d082391677785f81d54bc9c7c45f8b6d5bdb3e49fec7f5522b880c8c753fb7d3ff2c81e47dcb27d52842def40a812dc95cc679575baf237a955ee9944bd0797326f2a0a58c6c087f9b0b9e82c").Dump();
    (ToHex(A.ToByteArray()) == "00d9abd78c93dfddeb920d57d6513126d8f1118c9237a45101408dbffe6cfd95b011a016e4e0ab8aef0601e836a452b8bb88be7ca71e4f22f97aa65f8358ee69348d1227d65db6e53641d1a6542aa4be4b4adc75fac816af79a8e3f5097f8313e7b725df37eadc8c774e2033dfa99c95ccef333bf402b066198c30481e2a83875c").Dump();
}

Before I swapped the ordering, and included the .Concat(new byte[] { 0 }).ToArray() from your original question, the output was:

True
True
True
False

And now it's:

True
True
False
True

The other issue you're seeing is BigInteger.Parse and the Byte[] constructor always expect the top bit of the first nibble or last byte respectively to be the sign bit. So you need to include the extra 0 character or byte respectively to avoid that.