我需要为google的OAuth身份验证服务获取一个访问令牌(用于服务帐户)。我尝试了几件事,并在网上研究了很多,但都没有成功。
基本上我都跟着https://developers.google.com/accounts/docs/OAuth2ServiceAccount
我所做的(VS2013):
int _tmain(int argc, _TCHAR* argv[])
{
Json::Value jwt_header;
Json::Value jwt_claim_set;
std::string jwt_b64;
std::time_t t = std::time(NULL);
Json::FastWriter jfw;
Json::StyledWriter jsw;
/* Create jwt header */
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
std::cout << jsw.write(jwt_header);
/* Create jwt claim set */
jwt_claim_set["iss"] = "myid@developer.gserviceaccount.com"; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/plus.me" /* scope of requested access token */;
jwt_claim_set["aud"] = "https://accounts.google.com/o/oauth2/token"; /* intended target of the assertion for an access token */
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
jwt_claim_set["exp"] = std::to_string(t+3600); /* expire time*/
std::cout << jsw.write(jwt_claim_set);
/* create http POST request body */
/* for header */
std::string json_buffer;
std::string json_buffer1;
json_buffer = jfw.write(jwt_header);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 = json_buffer1;
jwt_b64 += ".";
/* for claim set */
json_buffer = jfw.write(jwt_claim_set);
json_buffer = json_buffer.substr(0, json_buffer.size() - 1);
json_buffer = base64_encode(reinterpret_cast<const unsigned char*>(json_buffer.c_str()), json_buffer.length(), true); /* urlsafeBasic64 encode*/
json_buffer1.clear();
std::remove_copy(json_buffer.begin(), json_buffer.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
/* for signature */
std::string jwt_signature = jws_sign(jwt_b64, "key.p12");
if (!jwt_signature.empty())
{
jwt_b64 += ".";
json_buffer1.clear();
std::remove_copy(jwt_signature.begin(), jwt_signature.end(), std::back_inserter(json_buffer1), '=');
jwt_b64 += json_buffer1;
write2file("jwt.bat", jwt_b64); /* for test purpose calling with curl */
}
else
std::cout << "Error creating signature";
return 0;
}
int write2file(std::string filename, std::string data)
{
std::ofstream f(filename);
f << "%curl% -d "grant_type=urn%%3Aietf%%3Aparams%%3Aoauth%%3Agrant-type%%3Ajwt-bearer&assertion=";
f << data;
f << "" https://accounts.google.com/o/oauth2/token";
f.close();
return 0;
}
std::string jws_sign(std::string data, std::string pkcs12_path) {
SHA256_CTX mctx;
unsigned char hash[SHA256_DIGEST_LENGTH];
size_t hlen = SHA256_DIGEST_LENGTH;
const char *buf = data.c_str();
int n = strlen((const char*) buf);
SHA256_Init(&mctx);
SHA256_Update(&mctx, buf, n);
SHA256_Final(hash, &mctx);
std::string signature_b64;
unsigned char *sig = NULL;
size_t slen = 0;
EVP_PKEY_CTX *kctx;
EVP_PKEY *key = getPkey(pkcs12_path);
kctx = EVP_PKEY_CTX_new(key, NULL);
if (!kctx) goto err;
if (EVP_PKEY_sign_init(kctx) <= 0) goto err;
if (EVP_PKEY_CTX_set_rsa_padding(kctx, RSA_PKCS1_PADDING) <= 0) goto err;
if (EVP_PKEY_CTX_set_signature_md(kctx, EVP_sha256()) <= 0) goto err;
/* Determine buffer length */
if (EVP_PKEY_sign(kctx, NULL, &slen, hash, hlen) <= 0) goto err;
sig = (unsigned char *) OPENSSL_malloc(slen);
if (!sig) goto err;
if (EVP_PKEY_sign(kctx, sig, &slen, hash, hlen) <= 0) goto err;
signature_b64 = base64_encode(sig, (unsigned int)slen, true);
return signature_b64;
err:
/* Clean up */
EVP_cleanup();
signature_b64.clear();
return signature_b64;
}
我收到的只是
{
"error" : "invalid_grant"
}
所以,如果有人能给我指引正确的方向,那就太好了。如果有人能指示我通过从openssl命令中手动生成jwt请求来实现这一点,这也会有所帮助。
我正在使用VS2013
我发现我的错误-只是一个打字错误:(
jwt_claim_set["iad"] = std::to_string(t); /* issued time */
需要
jwt_claim_set["iat"] = std::to_string(t); /* issued time */
该代码工作并生成有效的令牌请求。
我已经在C++上创建了一个用于身份验证的类,将把它留在这里,可能有人需要它。
// YOU SHOULD GO TO Credentials SECTION FOR YOUR PROJECT AT https://console.developers.google.com/
// MAKE Service Account AND GET AUTHENTICATION JSON FROM IT,
// PLACE IT TO BUILD FOLDER AND CALL IT google_service_account.json
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
// SSL INCLUDES
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
// https://github.com/nlohmann/json
#include <nlohmann/json.hpp>
using json = nlohmann::json;
class TGoogleAuthCpp {
json serviceAccountJSON;
bool serviceAccountExists;
void readServiceAccountJson();
RSA* createPrivateRSA(std::string key);
bool RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc);
std::string signMessage(std::string privateKey, std::string plainText);
std::string url_encode(const std::string &value);
std::string base64_encode(const std::string &in);
public:
TGoogleAuthCpp();
int createRequest();
};
TGoogleAuthCpp::TGoogleAuthCpp() {
serviceAccountExists = false;
readServiceAccountJson();
}
RSA* TGoogleAuthCpp::createPrivateRSA(std::string key) {
RSA *rsa = NULL;
const char* c_string = key.c_str();
BIO * keybio = BIO_new_mem_buf((void*)c_string, -1);
if (keybio==NULL) {
return 0;
}
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
return rsa;
}
bool TGoogleAuthCpp::RSASign( RSA* rsa,
const unsigned char* Msg,
size_t MsgLen,
unsigned char** EncMsg,
size_t* MsgLenEnc) {
EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
EVP_PKEY* priKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(priKey, rsa);
if (EVP_DigestSignInit(m_RSASignCtx,NULL, EVP_sha256(), NULL,priKey)<=0) {
return false;
}
if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
return false;
}
if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) {
return false;
}
*EncMsg = (unsigned char*)malloc(*MsgLenEnc);
if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
return false;
}
EVP_MD_CTX_cleanup(m_RSASignCtx);
return true;
}
std::string TGoogleAuthCpp::signMessage(std::string privateKey, std::string plainText) {
RSA* privateRSA = createPrivateRSA(privateKey);
unsigned char* encMessage;
size_t encMessageLength;
RSASign(privateRSA, (unsigned char*) plainText.c_str(), plainText.length(), &encMessage, &encMessageLength);
std::string str1((char *)(encMessage), encMessageLength);
free(encMessage);
return base64_encode(str1);
}
void TGoogleAuthCpp::readServiceAccountJson() {
std::string fname = "google_service_account.json";
std::string line;
std::ifstream myfile (fname);
if (myfile.good()) {
std::stringstream ss;
if (myfile.is_open()) {
while (getline(myfile, line)) {
ss << line << 'n';
}
myfile.close();
serviceAccountJSON = json::parse(ss.str());
serviceAccountExists = true;
}
}
}
std::string TGoogleAuthCpp::base64_encode(const std::string &in) {
std::string out;
std::string base64_encode_b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//=
int val=0, valb=-6;
for (unsigned char c : in) {
val = (val<<8) + c;
valb += 8;
while (valb>=0) {
out.push_back(base64_encode_b[(val>>valb)&0x3F]);
valb-=6;
}
}
if (valb>-6) out.push_back(base64_encode_b[((val<<8)>>(valb+8))&0x3F]);
while (out.size()%4) out.push_back('=');
return out;
}
std::string TGoogleAuthCpp::url_encode(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << std::uppercase;
escaped << '%' << std::setw(2) << int((unsigned char) c);
escaped << std::nouppercase;
}
return escaped.str();
}
int TGoogleAuthCpp::createRequest() {
if (!serviceAccountExists) return 0;
json jwt_header;
json jwt_claim_set;
std::time_t t = std::time(NULL);
// Create jwt header
jwt_header["alg"] = "RS256";
jwt_header["typ"] = "JWT";
// Create jwt claim set
jwt_claim_set["iss"] = serviceAccountJSON["client_email"]; /* service account email address */
jwt_claim_set["scope"] = "https://www.googleapis.com/auth/androidpublisher" /* scope of requested access token */;
jwt_claim_set["aud"] = serviceAccountJSON["token_uri"]; /* intended target of the assertion for an access token */
jwt_claim_set["iat"] = t; /* issued time */
jwt_claim_set["exp"] = t+3600; /* expire time*/
// web token
std::stringstream jwt_ss;
// header
jwt_ss << base64_encode(jwt_header.dump());
jwt_ss << ".";
// claim set
jwt_ss << base64_encode(jwt_claim_set.dump());
// signature
std::string signed_msg = signMessage(serviceAccountJSON["private_key"], jwt_ss.str());
jwt_ss << "." << signed_msg;
std::stringstream post_body_ss;
post_body_ss << "curl -d '";
post_body_ss << "grant_type=" << url_encode("urn:ietf:params:oauth:grant-type:jwt-bearer");
post_body_ss << "&assertion=" << url_encode(jwt_ss.str());
post_body_ss << "' https://oauth2.googleapis.com/token";
std::string post_body = post_body_ss.str();
std::cout << post_body << std::endl;
return 1;
}
int main() {
TGoogleAuthCpp auth;
int res = auth.createRequest();
}