tencent cloud

All product documents
API Gateway
C++ (Key Pair Authentication)
Last updated: 2023-12-22 10:09:13
C++ (Key Pair Authentication)
Last updated: 2023-12-22 10:09:13

Operation Scenarios

This document describes how to authenticate and manage your APIs through key pair authentication in C++.

Directions

1. In the API Gateway Console, create an API and select the authentication type as "key pair" (for more information, please see API Creation Overview).
2. Publish the service where the API resides to the release environment (for more information, please see Service Release and Deactivation).
3. Create a key pair on the key management page in the console.
4. Create a usage plan on the usage plan page in the console and bind it to the created key pair (for more information, please see Sample Usage Plan).
5. Bind the usage plan to the API or the service where the API resides.
6. Generate signing information in C++ by referring to the Sample Code.

Environment Dependencies

In this demo, libcurl is used to initiate HTTP requests, so the compiler machine needs to have the libcurl library installed.

Notes

The eventually delivered HTTP request contains at least two headers: Date or X-Date and Authorization. More optional headers can be added in the request. If Date is used, the server will not check the time; if X-Date is used, the server will check the time.
The value of Date header is the construction time of the HTTP request in GMT format, such as Fri, 09 Oct 2015 00:00:00 GMT.
The value of X-Date header is the construction time of the HTTP request in GMT format, such as Mon, 19 Mar 2018 12:08:40 GMT. It cannot deviate from the current time for more than 15 minutes.
If it is a microservice API, you need to add two fields in the header: X-NameSpace-Code and X-MicroService-Name. They are not needed for general APIs and are included in the demo by default.

Directory Structure

This demo contains 7 files in total, and the directory structure is as follows:
├─AuthenticationDemo.cpp
├─request.cpp
├─base64.h
├─base64.cpp
├─hmac.h
├─sha1.h
└─sha1.cpp

Compilation Command

g++ -o AuthenticationDemo AuthenticationDemo.cpp request.cpp base64.cpp sha1.cpp -lcurl

Sample Code

AuthenticationDemo.cpp

/*In this Demo, libcurl is used to initiate HTTP requests, so the compiler machine needs to have the libcurl library installed*/
/*Compilation command: g++ -o AuthenticationDemo AuthenticationDemo.cpp request.cpp base64.cpp sha1.cpp -lcurl*/

#include <iostream>
#include <stdio.h>
#include"hmac.h"
#include"sha1.h"
#include"base64.h"

extern void get_request(const string &defaultDomain, const string &source, const string &dateTime, const string &sign, const string &reqUrl);// Implemented in `request.cpp`
extern void post_request(const string &defaultDomain, const string &source, const string &dateTime, const string &sign, const string &reqUrl);// Implemented in `request.cpp`

using namespace std;

void GetGmtTime(string &szGmtTime)
{
time_t rawTime;
struct tm* timeInfo;
char szTemp[30]={0};
time(&rawTime);
timeInfo = gmtime(&rawTime);
strftime(szTemp,sizeof(szTemp),"%a, %d %b %Y %H:%M:%S GMT",timeInfo);
szGmtTime = szTemp;
}

int calcAuthorization(const string &source, const string &secretId, const string &secretKey,string &sign, string &dateTime)
{
GetGmtTime(dateTime);
sign = "x-date: " + dateTime + "\nsource: " + source;
sign = hmac<SHA1>(sign, secretKey);
string binDight;
HexToBin(sign , binDight);
BinToBase64(binDight , sign);
char tempauth[1024] = {0};
snprintf(tempauth,sizeof(tempauth)-1,"hmac id=\"%s\", algorithm=\"hmac-sha1\", headers=\"x-date source\", signature=\"%s\"", secretId.c_str(), sign.c_str());
sign = tempauth;

return 0;
}


/*Enter the `secretId`, `secretKey`, `defaultDomain`, and `reqUrl` in the code below according to your business situation*/

int main()
{
const string secretId = "your secretId";// `SecretId` in key pair
const string secretKey = "your secretKey";// `SecretKey` in key pair
const string source = "xxxxxx"; // Arbitrary signature watermark value
string sign, dateTime;
calcAuthorization(source, secretId, secretKey, sign, dateTime);
const string defaultDomain = "service-xxxxxxxx-1234567890.ap-guangzhou.apigateway.myqcloud.com"; // Service domain name of API
const string reqUrl = "https://service-xxxxxxxx-1234567890.ap-guangzhou.apigateway.myqcloud.com/release/xxapi"; // API access path

get_request(defaultDomain, source, dateTime, sign, reqUrl);
//post_request(defaultDomain, source, dateTime, sign, reqUrl);

return 0;
}

request.cpp

#include <iostream>
#include <cstring>
#include "curl/curl.h"

using namespace std;

size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream)
{
((std::string*)stream)->append((char*)ptr, size*nmemb);
return size * nmemb;
}

void get_request(const string &defaultDomain, const string &source, const string &dateTime, const string &sign, const string &reqUrl)
{
CURL* curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, reqUrl.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
struct curl_slist * slist = NULL;
slist = curl_slist_append(slist, "Accept:*/*");
slist = curl_slist_append(slist, "Accept-Charset:utf-8;");
string headDomain = "Host:" + defaultDomain;
slist = curl_slist_append(slist, headDomain.c_str());
string headSource = "Source:" + source;
slist = curl_slist_append(slist, headSource.c_str());
string headDatetime = "X-Date:" + dateTime;
slist = curl_slist_append(slist, headDatetime.c_str());
string headAuthorization = "Authorization:" + sign;
slist = curl_slist_append(slist, headAuthorization.c_str());
// If it is a microservice API, you need to add two fields in the header: 'X-NameSpace-Code' and 'X-MicroService-Name'. They are not needed for general APIs.
slist = curl_slist_append(slist, "x-NameSpace-Code:testmic");
slist = curl_slist_append(slist, "x-MicroService-Name:provider-demo");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
std::string response_data;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);

CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else
{
// get response code
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
printf("response code %d \n", response_code);
printf("response data : %s\n ",response_data.c_str());
}
curl_slist_free_all(slist);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}

void post_request(const string &defaultDomain, const string &source, const string &dateTime, const string &sign, const string &reqUrl)
{
CURL* curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, reqUrl.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_POST, 1);
struct curl_slist * slist = NULL;
slist = curl_slist_append(slist, "Accept:*/*");
slist = curl_slist_append(slist, "Accept-Charset:utf-8;");
string headDomain = "Host:" + defaultDomain;
slist = curl_slist_append(slist, headDomain.c_str());
string headSource = "Source:" + source;
slist = curl_slist_append(slist, headSource.c_str());
string headDatetime = "X-Date:" + dateTime;
slist = curl_slist_append(slist, headDatetime.c_str());
string headAuthorization = "Authorization:" + sign;
slist = curl_slist_append(slist, headAuthorization.c_str());
// If it is a microservice API, you need to add two fields in the header: 'X-NameSpace-Code' and 'X-MicroService-Name'. They are not needed for general APIs.
slist = curl_slist_append(slist, "x-NameSpace-Code:testmic");
slist = curl_slist_append(slist, "x-MicroService-Name:provider-demo");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);

// set body
std::string body = "{\
\"title\":\"post title\",\
\"body\" : \"post body\",\
\"userId\" : 1}";
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
std::string response_data;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);

CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else
{
// get response code
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
printf("response code %d \n", response_code);
printf("response data : %s\n ",response_data.c_str());
}
curl_slist_free_all(slist);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}

base64.h

// Base64 encoding table
#include<string>
using namespace std;
const char Base64EncodeMap[64] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};

int BinToDecInt(string strBin);
void BinToBase64(string binStr , string &base64Str);
void HexToBin(string hexDight , string& binDight);

base64.cpp

#include"base64.h"

int BinToDecInt(string strBin){
int num = 0;
int b = 0;
for(int i = 0; i < strBin.length() ;i++){
num = num * 2;
b = static_cast<int>(strBin[i]-'0');
num = num + b;
}
return num;
}

void BinToBase64(string binStr , string &base64Str)
{
while(binStr.length() % 6 != 0){
binStr = binStr + "0";
}
base64Str = "";
string tmp = "";
int index = 0;
int num = 0;
while(index < binStr.length()){
tmp = binStr.substr(index , 6);
index = index + 6;
num = BinToDecInt(tmp);
base64Str = base64Str + Base64EncodeMap[num];
}
base64Str = base64Str + "=";
}



void HexToBin(string hexDight , string& binDight){
binDight = "";
int f = 0,c = 0;
char e;
for(int f = 0; f < hexDight.length() ; f++){
e = hexDight[f];
if(e >= 'a' && e <= 'f'){
int a = static_cast<int>(e-'a'+10);
switch(a){
case 10 : binDight = binDight + "1010";
break;
case 11 : binDight = binDight + "1011";
break;
case 12 : binDight = binDight + "1100";
break;
case 13 : binDight = binDight + "1101";
break;
case 14 : binDight = binDight + "1110";
break;
case 15 : binDight = binDight + "1111";
break;
}
}
else if( e >= '0' && e <= '9'){
int b = static_cast<int>(e-'0');
if(f == 0){
switch(b){
case 0: binDight = binDight + "0000";
break;
case 1: binDight = binDight + "0001";
break;
case 2: binDight = binDight + "0010";
break;
case 3: binDight = binDight + "0011";
break;
case 4: binDight = binDight + "0100";
break;
case 5: binDight = binDight + "0101";
break;
case 6: binDight = binDight + "0110";
break;
case 7: binDight = binDight + "0111";
break;
case 8: binDight = binDight + "1000";
break;
case 9: binDight = binDight + "1001";
break;
}
}
else{
switch(b){
case 0 : binDight = binDight + "0000";
break;
case 1: binDight = binDight + "0001";
break;
case 2: binDight = binDight + "0010";
break;
case 3: binDight = binDight + "0011";
break;
case 4: binDight = binDight + "0100";
break;
case 5: binDight = binDight + "0101";
break;
case 6: binDight = binDight + "0110";
break;
case 7: binDight = binDight + "0111";
break;
case 8: binDight = binDight + "1000";
break;
case 9: binDight = binDight + "1001";
break;
}
}
}
}
}

hmac.h

#pragma once


#include <string>
#include <cstring>

/// compute HMAC hash of data and key using MD5, SHA1 or SHA256
template <typename HashMethod>
std::string hmac(const void* data, size_t numDataBytes, const void* key, size_t numKeyBytes)
{
unsigned char usedKey[HashMethod::BlockSize] = {0};

if (numKeyBytes <= HashMethod::BlockSize)
{
memcpy(usedKey, key, numKeyBytes);
}
else
{
HashMethod keyHasher;
keyHasher.add(key, numKeyBytes);
keyHasher.getHash(usedKey);
}

for (size_t i = 0; i < HashMethod::BlockSize; i++)
usedKey[i] ^= 0x36;

unsigned char inside[HashMethod::HashBytes];
HashMethod insideHasher;
insideHasher.add(usedKey, HashMethod::BlockSize);
insideHasher.add(data, numDataBytes);
insideHasher.getHash(inside);

for (size_t i = 0; i < HashMethod::BlockSize; i++)
usedKey[i] ^= 0x5C ^ 0x36;

HashMethod finalHasher;
finalHasher.add(usedKey, HashMethod::BlockSize);
finalHasher.add(inside, HashMethod::HashBytes);

return finalHasher.getHash();
}

template <typename HashMethod>
std::string hmac(const std::string& data, const std::string& key)
{
return hmac<HashMethod>(data.c_str(), data.size(), key.c_str(), key.size());
}

sha1.h

#pragma once

#include <string>

#ifdef _MSC_VER
// Windows
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
// GCC
#include <stdint.h>
#endif


class SHA1 //: public Hash
{
public:
enum { BlockSize = 512 / 8, HashBytes = 20 };

SHA1();

std::string operator()(const void* data, size_t numBytes);

std::string operator()(const std::string& text);

void add(const void* data, size_t numBytes);

std::string getHash();

void getHash(unsigned char buffer[HashBytes]);

void reset();

private:
void processBlock(const void* data);

void processBuffer();

uint64_t m_numBytes;

size_t m_bufferSize;

uint8_t m_buffer[BlockSize];

enum { HashValues = HashBytes / 4 };

uint32_t m_hash[HashValues];
};

sha1.cpp

#include "sha1.h"

#ifndef _MSC_VER
#include <endian.h>
#endif


SHA1::SHA1()
{
reset();
}


void SHA1::reset()
{
m_numBytes = 0;
m_bufferSize = 0;

m_hash[0] = 0x67452301;
m_hash[1] = 0xefcdab89;
m_hash[2] = 0x98badcfe;
m_hash[3] = 0x10325476;
m_hash[4] = 0xc3d2e1f0;
}


namespace
{
inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d)
{
return d ^ (b & (c ^ d));
}

inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d)
{
return b ^ c ^ d;
}

inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d)
{
return (b & c) | (b & d) | (c & d);
}

inline uint32_t rotate(uint32_t a, uint32_t c)
{
return (a << c) | (a >> (32 - c));
}

inline uint32_t swap(uint32_t x)
{
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap32(x);
#endif
#ifdef MSC_VER
return _byteswap_ulong(x);
#endif

return (x >> 24) |
((x >> 8) & 0x0000FF00) |
((x << 8) & 0x00FF0000) |
(x << 24);
}
}

void SHA1::processBlock(const void* data)
{
uint32_t a = m_hash[0];
uint32_t b = m_hash[1];
uint32_t c = m_hash[2];
uint32_t d = m_hash[3];
uint32_t e = m_hash[4];

const uint32_t* input = (uint32_t*) data;

uint32_t words[80];
for (int i = 0; i < 16; i++)
#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN)
words[i] = input[i];
#else
words[i] = swap(input[i]);
#endif

for (int i = 16; i < 80; i++)
words[i] = rotate(words[i-3] ^ words[i-8] ^ words[i-14] ^ words[i-16], 1);

for (int i = 0; i < 4; i++)
{
int offset = 5*i;
e += rotate(a,5) + f1(b,c,d) + words[offset ] + 0x5a827999; b = rotate(b,30);
d += rotate(e,5) + f1(a,b,c) + words[offset+1] + 0x5a827999; a = rotate(a,30);
c += rotate(d,5) + f1(e,a,b) + words[offset+2] + 0x5a827999; e = rotate(e,30);
b += rotate(c,5) + f1(d,e,a) + words[offset+3] + 0x5a827999; d = rotate(d,30);
a += rotate(b,5) + f1(c,d,e) + words[offset+4] + 0x5a827999; c = rotate(c,30);
}

for (int i = 4; i < 8; i++)
{
int offset = 5*i;
e += rotate(a,5) + f2(b,c,d) + words[offset ] + 0x6ed9eba1; b = rotate(b,30);
d += rotate(e,5) + f2(a,b,c) + words[offset+1] + 0x6ed9eba1; a = rotate(a,30);
c += rotate(d,5) + f2(e,a,b) + words[offset+2] + 0x6ed9eba1; e = rotate(e,30);
b += rotate(c,5) + f2(d,e,a) + words[offset+3] + 0x6ed9eba1; d = rotate(d,30);
a += rotate(b,5) + f2(c,d,e) + words[offset+4] + 0x6ed9eba1; c = rotate(c,30);
}

for (int i = 8; i < 12; i++)
{
int offset = 5*i;
e += rotate(a,5) + f3(b,c,d) + words[offset ] + 0x8f1bbcdc; b = rotate(b,30);
d += rotate(e,5) + f3(a,b,c) + words[offset+1] + 0x8f1bbcdc; a = rotate(a,30);
c += rotate(d,5) + f3(e,a,b) + words[offset+2] + 0x8f1bbcdc; e = rotate(e,30);
b += rotate(c,5) + f3(d,e,a) + words[offset+3] + 0x8f1bbcdc; d = rotate(d,30);
a += rotate(b,5) + f3(c,d,e) + words[offset+4] + 0x8f1bbcdc; c = rotate(c,30);
}

for (int i = 12; i < 16; i++)
{
int offset = 5*i;
e += rotate(a,5) + f2(b,c,d) + words[offset ] + 0xca62c1d6; b = rotate(b,30);
d += rotate(e,5) + f2(a,b,c) + words[offset+1] + 0xca62c1d6; a = rotate(a,30);
c += rotate(d,5) + f2(e,a,b) + words[offset+2] + 0xca62c1d6; e = rotate(e,30);
b += rotate(c,5) + f2(d,e,a) + words[offset+3] + 0xca62c1d6; d = rotate(d,30);
a += rotate(b,5) + f2(c,d,e) + words[offset+4] + 0xca62c1d6; c = rotate(c,30);
}

m_hash[0] += a;
m_hash[1] += b;
m_hash[2] += c;
m_hash[3] += d;
m_hash[4] += e;
}

void SHA1::add(const void* data, size_t numBytes)
{
const uint8_t* current = (const uint8_t*) data;

if (m_bufferSize > 0)
{
while (numBytes > 0 && m_bufferSize < BlockSize)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}

if (m_bufferSize == BlockSize)
{
processBlock((void*)m_buffer);
m_numBytes += BlockSize;
m_bufferSize = 0;
}

if (numBytes == 0)
return;

while (numBytes >= BlockSize)
{
processBlock(current);
current += BlockSize;
m_numBytes += BlockSize;
numBytes -= BlockSize;
}

while (numBytes > 0)
{
m_buffer[m_bufferSize++] = *current++;
numBytes--;
}
}


void SHA1::processBuffer()
{
size_t paddedLength = m_bufferSize * 8;

paddedLength++;

size_t lower11Bits = paddedLength & 511;
if (lower11Bits <= 448)
paddedLength += 448 - lower11Bits;
else
paddedLength += 512 + 448 - lower11Bits;

paddedLength /= 8;

unsigned char extra[BlockSize];

if (m_bufferSize < BlockSize)
m_buffer[m_bufferSize] = 128;
else
extra[0] = 128;

size_t i;
for (i = m_bufferSize + 1; i < BlockSize; i++)
m_buffer[i] = 0;
for (; i < paddedLength; i++)
extra[i - BlockSize] = 0;

uint64_t msgBits = 8 * (m_numBytes + m_bufferSize);

unsigned char* addLength;
if (paddedLength < BlockSize)
addLength = m_buffer + paddedLength;
else
addLength = extra + paddedLength - BlockSize;

*addLength++ = (unsigned char)((msgBits >> 56) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 48) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 40) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 32) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 24) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 16) & 0xFF);
*addLength++ = (unsigned char)((msgBits >> 8) & 0xFF);
*addLength = (unsigned char)( msgBits & 0xFF);

processBlock(m_buffer);

if (paddedLength > BlockSize)
processBlock(extra);
}

std::string SHA1::getHash()
{
unsigned char rawHash[HashBytes];
getHash(rawHash);

std::string result;
result.reserve(2 * HashBytes);
for (int i = 0; i < HashBytes; i++)
{
static const char dec2hex[16+1] = "0123456789abcdef";
result += dec2hex[(rawHash[i] >> 4) & 15];
result += dec2hex[ rawHash[i] & 15];
}

return result;
}


void SHA1::getHash(unsigned char buffer[SHA1::HashBytes])
{
uint32_t oldHash[HashValues];
for (int i = 0; i < HashValues; i++)
oldHash[i] = m_hash[i];

processBuffer();

unsigned char* current = buffer;
for (int i = 0; i < HashValues; i++)
{
*current++ = (m_hash[i] >> 24) & 0xFF;
*current++ = (m_hash[i] >> 16) & 0xFF;
*current++ = (m_hash[i] >> 8) & 0xFF;
*current++ = m_hash[i] & 0xFF;

m_hash[i] = oldHash[i];
}
}

std::string SHA1::operator()(const void* data, size_t numBytes)
{
reset();
add(data, numBytes);
return getHash();
}

std::string SHA1::operator()(const std::string& text)
{
reset();
add(text.c_str(), text.size());
return getHash();
}

Was this page helpful?
You can also Contact Sales or Submit a Ticket for help.
Yes
No

Feedback

Contact Us

Contact our sales team or business advisors to help your business.

Technical Support

Open a ticket if you're looking for further assistance. Our Ticket is 7x24 avaliable.

7x24 Phone Support
Hong Kong, China
+852 800 906 020 (Toll Free)
United States
+1 844 606 0804 (Toll Free)
United Kingdom
+44 808 196 4551 (Toll Free)
Canada
+1 888 605 7930 (Toll Free)
Australia
+61 1300 986 386 (Toll Free)
EdgeOne hotline
+852 300 80699
More local hotlines coming soon