8

我是一个相当新手的 c++ 程序员(我还在上大学,所以我想我一般来说是一个相当新手的程序员),我正在尝试在 c++ 中生成一个 JWT。我能够生成和编码标头和有效负载,但是当我在 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());
}
4

1 回答 1

8

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

BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

请参阅此处的文档

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

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

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

2) 使 Base64 输出 URL 安全时,您需要将 '+' 替换为 '-' 和 '/' 替换为 '_'。你的代码正好相反。所以:

case '+':
    ss << '-';
    break;
case '/':
    ss << '_';
    break;
于 2015-12-28T00:12:41.413 回答