I am passing in the following(The digest/hash is SHA1):
hash = HexToBytes("9E712647173B435CF691537A76C2F1423E4A18ED");
signature = Base64ToBytes("ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==");
pubKey16 = "04C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A";//Actual key lacks 04 at front but I'm guessing OpenSSL needs this?
To this function:
int Misc::verify_signature(unsigned char* hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
//Load our public key
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
//Set S
if (NULL == BN_bin2bn(&signature[0] + 32, 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
int sig_size = i2d_ECDSA_SIG(ec_sig, NULL);
unsigned char *sig_bytes =(unsigned char *) malloc(sig_size);
unsigned char *p;
printf("Orig Sig Size: %d\n", sig_size);
p = sig_bytes;
int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
printf("New Sig Size: %d\n", new_sig_size);
int verify_status = ECDSA_do_verify(hash, 20, ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if(verify_status==-1)handleErrors();
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}
But I cannot verify the signature in OpenSSL(verify_success is 0), even though the exact same data verifies successfully in C#.
Any ideas as to why or what I am doing wrong?
The public key in C# is:
4543533120000000C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A
I'm assuming that 4543533120000000 is .NET specific stuff so i just pre-pended 04 to the rest of it.
Here is the C# code used to verify the signature and it does so successfully(SHA1 of dataBytes is identical across both programs)..
HashAlgorithm hashMan2 = new SHA1Managed();
byte[] dataBytes = hashMan2.ComputeHash(Encoding.ASCII.GetBytes("H4sIAAAAAAAEADPQMQBCQzBJDsSm0xCMDTFUYYpQAjFNAIsAAOvFhT3RAAAA"));
String sig = "ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==";
byte[] readPublicKey2 = Convert.FromBase64String("RUNTMSAAAADC0Khow19HUgi2wzpY1KwnUZDxqdWARFb/B8QmBXFu90j7T9JGFj6FHb6alCVpdB9UNBp8hfOUsgd3q3/lJglq");
Console.WriteLine("Public key file is read as:");
Console.WriteLine(Convert.ToBase64String(readPublicKey2));
using (ECDsaCng ecsdKey = new ECDsaCng(CngKey.Import(readPublicKey2, CngKeyBlobFormat.EccPublicBlob)))
{
if (ecsdKey.VerifyData(dataBytes, Convert.FromBase64String(sig)))
{
Console.WriteLine("Data and Signature have been verified.");
}
else
{
Console.WriteLine("Data and Signature could not be verified!");
}
}
Any help appreciated.
I found the issue, I was using the .NET function ECDSA.SignData, but this actually hashes the data prior to input(with ECDsaCng.HashAlgorithm), i was assuming it was taking a hash as an input but the correct function for that is ECDSA.SignHash, I've switched to SignHash and my new signature/message verifies correctly. (Note that this may be different for you depending on your version of .NET, be sure to check the API for YOUR VERSION)
In case it will help someone, here is my draft-working function(There is some extra/unneeded stuff in here as well that may help you):
int Misc::verify_signature(std::vector<unsigned char> hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
printf("Raw PubKey Bytes: \n");
for (unsigned char t : pubKeyVC) {
printf("%d\n", t);
}
printf("Raw PubKey Length:%d \n", pubKeyVC.size());
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
////Try to pad S
//std::vector<unsigned char> sPadded = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
//sPadded.insert(sPadded.begin(), '0');
//sPadded.insert(sPadded.begin(), '0');
//Set S
if (NULL == BN_bin2bn(&signature[32], 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
std::vector<unsigned char> rValue = std::vector<unsigned char>(&signature[0], &signature[0] + 32);
std::vector<unsigned char> sValue = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
std::vector<unsigned char> derEncoded = std::vector<unsigned char>();
derEncoded.push_back(0x30);
//Push payload length into this position later
//Seperator
derEncoded.push_back(0x02);
//Length of rValue
if (rValue.at(0) >= 0x80) {
derEncoded.push_back(rValue.size() + 1);
}
else {
derEncoded.push_back(rValue.size());
}
//Push rValue bytes in
int c = 0;
for (unsigned char b : rValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Seperator
derEncoded.push_back(0x02);
//Length of sValue
if (sValue.at(0) >= 0x80) {
derEncoded.push_back(sValue.size() + 1);
}
else {
derEncoded.push_back(sValue.size());
}
//Push sValue bytes in
c = 0;
for (unsigned char b : sValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Insert payload length in
int len = derEncoded.size() - 1;
derEncoded.insert(derEncoded.begin() + 1, len);
printf("Encoded Sig Len: %d\n", derEncoded.size());
printf("Encoded Sig64: %s\n", Misc::base64_encode_d(&derEncoded).c_str());
//unsigned char *p = (unsigned char*)malloc(ECDSA_size(eckey));
//int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
//printf("New Sig Size: %d\n", new_sig_size);
//for (int x = 0; x < new_sig_size; x++) {
// printf("%d\n", p[x]);
//}
//Dump DER encoded sig
//printf("DER encoded signature\n");
//const unsigned char* pp = (unsigned char*) malloc(new_sig_size);
//d2i_ECDSA_SIG(&ec_sig, &pp, new_sig_size);
//std::vector<unsigned char> ppVC = std::vector<unsigned char>(pp, pp+new_sig_size);
//printf("Base64: %s\n", Misc::base64_encode_d(&ppVC).c_str());
//ECDSA_SIG *signature = ECDSA_do_sign(hash, 20, eckey);
//ECDSA_size(eckey);
int verify_status = ECDSA_verify(0, hash.data(), hash.size(), derEncoded.data(), derEncoded.size(), eckey);//ECDSA_do_verify(hash.data(), hash.size(), ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if (verify_status == -1)
{
handleErrors();
}
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}