在由 ECDSA_do_verify 生成的 signature->r 或 signature-s 上,BN_bn2bin 的结果大小有时是 31 而不是 32

问题描述

我使用 openssl 为 Apple 令牌端点生成 client_secret(JWT)。我发现帖子 https://developer.apple.com/forums/thread/123723 说我们应该解析 R、S 值并连接这两个值。

我使用以下代码为 client_secret 生成签名:

#include <string>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/ec.h>
#include <openssl/err.h>

using namespace std;

class ECDSA256
{
    public:
        ECDSA256(const string& private_key) : is_error_(false),err_str_(""),private_key_(NULL),private_key_bio_(NULL),evp_md_ctx_(NULL),ecdsa_sig_(NULL)
        {
            private_key_bio_ = BIO_new(BIO_s_mem());

            if ((size_t)BIO_write(private_key_bio_,private_key.data(),private_key.size()) != private_key.size())
            {
                is_error_ = true;
                err_str_.append("Failed to load private key: BIO_wirte Failed\n");
                return;
            }
            
            private_key_ = PEM_read_bio_ECPrivateKey(private_key_bio_,NULL,NULL);

            if (!private_key_)
            {
                is_error_ = true;
                err_str_.append("Failed to load private key: PEM_read_bio_ECPrivateKey Failed\n");
                return;
            }

            if (0 == EC_KEY_check_key(private_key_))
            {
                is_error_ = true;
                err_str_.append("Failed to load private key: EC_KEY_check_key Failed\n");
                return;
            }
        }

        virtual ~ECDSA256()
        {
            clear();
        }

        string sign(const string& data) 
        {
            const string hashed = hash(data);

            if (is_error_)
            {
                return "";
            }

            ecdsa_sig_ = ECDSA_do_sign((const unsigned char*)hashed.data(),hashed.size(),private_key_);

            if (!ecdsa_sig_)
            {
                is_error_ = true;
                err_str_.append("ECDSA_do_sign Failed\n");
                return "";
            }

            return bn2raw(ecdsa_sig_->r) + bn2raw(ecdsa_sig_->s);
        }

        void clear()
        {
            if (private_key_bio_)
            {
                BIO_free_all(private_key_bio_);
                private_key_bio_ = NULL;
            }
            if (private_key_)
            {
                EC_KEY_free(private_key_);
                private_key_ = NULL;
            }
            if (evp_md_ctx_)
            {
                EVP_MD_CTX_destroy(evp_md_ctx_);
                evp_md_ctx_ = NULL;
            }
            if (ecdsa_sig_)
            {
                ECDSA_SIG_free(ecdsa_sig_);
                ecdsa_sig_ = NULL;
            }

            is_error_ = false;
            err_str_.clear();
            log_oss_.str("");
        }

        bool is_error()
        {
            return is_error_;
        }

        string err_str()
        {
            return err_str_;
        }

        string log_str()
        {
            return log_oss_.str();
        }

    private:

        bool is_error_;
        string err_str_;
        ostringstream log_oss_;

        EC_KEY* private_key_;
        BIO* private_key_bio_;

        EVP_MD_CTX* evp_md_ctx_;

        ECDSA_SIG* ecdsa_sig_;

        string hash(const string& data)
        {
            evp_md_ctx_ = EVP_MD_CTX_create();

            if (0 == EVP_Digestinit(evp_md_ctx_,EVP_sha256()))
            {
                is_error_ = true;
                err_str_.append("EVP_Digestinit Failed\n");
                return "";
            }

            if (0 == EVP_DigestUpdate(evp_md_ctx_,data.data(),data.size()))
            {
                is_error_ = true;
                err_str_.append("EVP_DigestUpdate Failed\n");
                return "";
            }

            std::string result;
            
            unsigned int len = 0;

            result.resize(EVP_MD_CTX_size(evp_md_ctx_));

            if (EVP_DigestFinal(evp_md_ctx_,(unsigned char*)result.data(),&len) == 0)
            {
                is_error_ = true;
                err_str_.append("EVP_DigestFinal Failed\n");
                return "";
            }

            result.resize(len);

            return result;
        }

        string bn2raw(BIGNUM* bn)
        {
            string result;

            result.resize(BN_num_bytes(bn));
            log_oss_ << "\nresult size: " << result.size();

            BN_bn2bin(bn,(unsigned char*)result.data());

            log_oss_ << "\nresult size: " << result.size();
            log_oss_ << "\nresult char: " << result[0];
            log_oss_ << "\nresult hex : " << BN_bn2hex(bn);

            if (result.size() % 2 == 1 && result[0] == 0x00)
            {
                return result.substr(1);
            }
            log_oss_ << "\nresult size: " << result.size();

            return result;
        }
};

有时签名无效。苹果返回 invalid_client。我将client_secret粘贴到jwt.io,jwt.io也说无效signaure。当签名有效时,签名的长度是63而不是64。日志信息显示BN_bn2bin的结果大小有时在signature->r或signature-s上是31而不是32。

enter image description here

enter image description here

有人可以就这种情况提供一些建议吗?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)