使用 boost 和 openssl 错误的 C++ 中的 JWT(JSON Web 令牌)

JWT (JSON Web Token) in C++ using boost and openssl bug

我是一个相当新手的 c++ 程序员(我还在上大学,所以我想我是一个相当新手的程序员),我正在尝试用 c++ 生成 JWT。我能够生成和编码 header 和有效载荷,但是当我在 jwt.io 上检查时,我使用 openssl 的 hmac 库生成的签名似乎无效。我有一种感觉,这是由于我只是没有看到的一些细微的错误。如果您能找到我的错误,我将不胜感激。如果您也有关于如何改进我的代码的建议,我们也将不胜感激。注意我必须为 json.

使用 openssl 和 boost

****更新****

我发现添加了额外的新行,这导致身份验证期间出错。目前我正在调用 str.erase 来删除它们,但如果有人能让我知道它们的去向,我将不胜感激,这样我就可以避免操纵字符串


char* Auth::genJWT()
{
    ptree pt;
    std::stringstream jwt;

    //Make and encode header
    pt.put("typ","JWT");
    pt.put("alg","HS256");
    std::ostringstream buf;
    write_json(buf, pt, false);
    std::string header = buf.str();
    jwt <<  base64_encode((unsigned char*)header.c_str(), (int) header.length());

    //clear buffer
    pt.clear();
    buf.clear();
    buf.str("");

    //make pay load
    pt.put("org_guid", Config::organization_guid);
    pt.put("agent_guid", Config::agent_guid);
    write_json(buf, pt, false);
    std::string payload = buf.str();
    jwt << '.' <<  base64_encode((unsigned char*)payload.c_str(), (int) payload.length());

    //sign data
    std::string signed_data = signToken(jwt.str());
    jwt << '.' <<  base64_encode((unsigned char*) signed_data.c_str(), (int) signed_data.length());

    //jwt made
    std::cout << "JWT Token: " << jwt.str() << std::endl;

    //Note I am testing by copying the token into an online debugger 
    //returning "" is not my bug 
    return "";
}

这是我用来签署令牌的方法

std::string Auth::signToken(std::string to_sign)
{
    //std::string key = Config::secret;
    std::string key = "foo";

    unsigned int len = 32;
    unsigned char signed_data[32];

    HMAC_CTX ctx;
    HMAC_CTX_init(&ctx);

    HMAC_Init_ex(&ctx, &key[0], (int) key.length(), EVP_sha256(), NULL);
    HMAC_Update(&ctx, (unsigned char*)&to_sign[0], to_sign.length());
    HMAC_Final(&ctx, signed_data, &len);
    HMAC_CTX_cleanup(&ctx);

    std::stringstream ss;
    ss << std::setfill('0');

    for(unsigned int i = 0; i < len; i++)
    {
        ss << signed_data[i];
    }

    return (ss.str());
}

这就是我在 base64Url 中对数据进行编码的方式

std::string Auth::base64_encode(const unsigned char* input, int length)
{
    BIO *bmem, *b64;
    BUF_MEM *bptr;

    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    std::stringstream ss;

    //make URL safe
    for(unsigned int i = 0; i < bptr->length - 1; i++)
    {
        switch(bptr->data[i])
        {
            case '+':
                ss << '_';
                break;
            case '/':
                ss << '-';
                break;
            case '=':
                //don't add in padding
                break;
            default:
                ss << bptr->data[i];
                break;
        }
    }

    BIO_free_all(b64);
    return (ss.str());
}

OpenSSL 在每个第 64 个字符后插入一个换行符,您可以使用以下方法禁用此功能:

BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

请参阅此处 the documentation

在测试您的代码时,我还发现了以下 2 个问题:

1) 使缓冲区 URL 安全时跳过缓冲区的最后一个字符。您应该删除 - 1.

for(unsigned int i = 0; i < bptr->length ; i++)

2) 在使 Base64 输出 URL-safe 时,您需要将“+”替换为“-”,将“/”替换为“_”。您的代码恰恰相反。所以:

case '+':
    ss << '-';
    break;
case '/':
    ss << '_';
    break;