I'm implementing the EMV issuer public key recovery in .net8.0 but cannot get this unit test to pass
// from javaemvreader
var modulus = Convert.FromHexString("be9e1fa5e9a803852999c4ab432db28600dcd9dab76dfaaa47355a0fe37b1508ac6bf38860d3c6c2e5b12a3caaf2a7005a7241ebaa7771112c74cf9a0634652fbca0e5980c54a64761ea101a114e0f0b5572add57d010b7c9c887e104ca4ee1272da66d997b9a90b5a6d624ab6c57e73c8f919000eb5f684898ef8c3dbefb330c62660bed88ea78e909aff05f6da627b");
var data = Convert.FromHexString("8b3901f6253048a8b2cb08974a4245d90e1f0c4a2a69bca469615a71db21ee7b3aa94200cfaedcd6f0a7d9ad0bf79213b6a418d7a49d234e5c9715c9140d87940f2e04d6971f4a204c927a455d4f8fc0d6402a79a1ce05aa3a526867329853f5ac2feb3c6f59ff6c453a7245e39d73451461725795ed73097099963b82ebf7203c1f78a529140c182dbbe6b42ae00c02");
var hash = Convert.FromHexString("ee1511cec71020a9b90443b37b1d5f6e703030f6");
var rid = Convert.FromHexString("A000000003");
var index = (byte)149;
byte[] exp = [0x03];
// validate ca
var ms = new MemoryStream();
ms.Write(rid);
ms.WriteByte(index);
ms.Write(modulus);
ms.Write(exp);
var toHash = ms.ToArray();
var hashed = Crypto.SHA1(toHash);
Assert.True(Enumerable.SequenceEqual(hash, hashed));
// test recovery algo
var recovered = Crypto.PerformRSA(data, exp, modulus);
Assert.Equal(0x6A, recovered[0]);
Assert.Equal(0xBC, recovered.Last());
Here is the implementation
`public static byte[] PerformRSA(byte[] dataBytes, byte[] expBytes, byte[] modBytes)
{
int inBytesLength = dataBytes.Length;
if (expBytes[0] >= (byte)0x80)
{
// Prepend 0x00 to exponent
byte[] tmp = new byte[expBytes.Length + 1];
tmp[0] = (byte)0x00;
Array.Copy(expBytes, 0, tmp, 1, expBytes.Length);
expBytes = tmp;
}
if (modBytes[0] >= (byte)0x80)
{
// Prepend 0x00 to modulus
byte[] tmp = new byte[modBytes.Length + 1];
tmp[0] = (byte)0x00;
Array.Copy(modBytes, 0, tmp, 1, modBytes.Length);
modBytes = tmp;
}
if (dataBytes[0] >= (byte)0x80)
{
// Prepend 0x00 to signed data to avoid that the most significant bit is interpreted as the "signed" bit
byte[] tmp = new byte[dataBytes.Length + 1];
tmp[0] = (byte)0x00;
Array.Copy(dataBytes, 0, tmp, 1, dataBytes.Length);
dataBytes = tmp;
}
System.Numerics.BigInteger exp = new(expBytes);
System.Numerics.BigInteger mod = new(modBytes);
System.Numerics.BigInteger data = new(dataBytes);
byte[] result = System.Numerics.BigInteger.ModPow(data, exp, mod).ToByteArray();
if (result.Length == (inBytesLength + 1) && result[0] == (byte)0x00)
{
// Remove 0x00 from beginning of array
byte[] tmp = new byte[inBytesLength];
Array.Copy(result, 1, tmp, 0, inBytesLength);
result = tmp;
}
return result;
}
I can't figure out why this is failing. The data is from the test in javaemvreader IssuerPublicKeyCertificate.java but my own card data also doesn't pass. Please any help will be appreciated.
Finally worked it out. the issue had to do with little endian vs big endian. Below is the working RSA Recovery Function in c#: