【问题标题】:Generate an OAuth access token with openssl and c++使用 openssl 和 c++ 生成 OAuth 访问令牌
【发布时间】:2015-04-28 13:50:54
【问题描述】:

我需要为 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

【问题讨论】:

  • 无法帮助 c++ 但无效授权通常是两件事之一。时间是关闭时间需要是NTP。或者您正在使用两个许多刷新令牌。您是否尝试过挖掘 C++ 的客户端库以查看它们在做什么? github.com/google/google-api-cpp-client
  • @DalmTo 感谢您将我指向 google-api-cpp-client,对此我一无所知。不幸的是,似乎没有包含服务帐户身份验证的方法。我可以肯定地说这不是 NTP 或刷新令牌计数问题......它似乎与签名过程有关。

标签: c++ oauth google-oauth google-authentication google-api-cpp-client


【解决方案1】:

我发现我的错误 - 只是一个错字:(

jwt_claim_set["iad"] = std::to_string(t); /* issued time */

需要

jwt_claim_set["iat"] = std::to_string(t); /* issued time */

代码工作并生成有效的令牌请求。

【讨论】:

    【解决方案2】:

    我在 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();
    }
    

    【讨论】:

    • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的答案以添加解释并说明适用的限制和假设。 From Review
    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • @GauravRoy 什么链接?
    猜你喜欢
    • 2020-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-14
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 2023-01-03
    相关资源
    最近更新 更多