OpenSSL无法验证ECDSA签名c++, c#验证正确

时间:2022-07-12 18:25:54

I am passing in the following(The digest/hash is SHA1):

我传入以下内容(摘要/散列是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#.

但是,我无法验证OpenSSL中的签名(verify_success是0),即使在c#中也验证了完全相同的数据。

Any ideas as to why or what I am doing wrong?

你知道我为什么做错了什么吗?

The public key in C# is:

c#中的公钥是:

4543533120000000C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A

I'm assuming that 4543533120000000 is .NET specific stuff so i just pre-pended 04 to the rest of it.

我假设4543533120000000是。net特定的东西,所以我只是预写了04。

Here is the C# code used to verify the signature and it does so successfully(SHA1 of dataBytes is identical across both programs)..

下面是用于验证签名的c#代码,它成功地验证了签名(dataBytes的SHA1在两个程序中都是相同的)。

        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.

任何帮助表示赞赏。

1 个解决方案

#1


1  

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)

我发现这个问题,我使用。net函数ECDSA。SignData,但这实际上是在输入之前对数据进行散列处理(使用ECDsaCng.HashAlgorithm),我假设它将散列作为输入,但正确的函数是ECDSA。SignHash,我切换到SignHash,我的新签名/消息验证正确。(注意,这可能与您的。net版本不同,请务必检查您的版本的API)

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;
    }

#1


1  

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)

我发现这个问题,我使用。net函数ECDSA。SignData,但这实际上是在输入之前对数据进行散列处理(使用ECDsaCng.HashAlgorithm),我假设它将散列作为输入,但正确的函数是ECDSA。SignHash,我切换到SignHash,我的新签名/消息验证正确。(注意,这可能与您的。net版本不同,请务必检查您的版本的API)

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;
    }