使用 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;
我是一个相当新手的 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;