This document describes how to authenticate and manage your APIs through key pair authentication in C++.
In this demo, libcurl is used to initiate HTTP requests, so the compiler machine needs to have the libcurl library installed.
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.Date
header is the construction time of the HTTP request in GMT format, such as Fri, 09 Oct 2015 00:00:00 GMT.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.X-NameSpace-Code
and X-MicroService-Name
. They are not needed for general APIs and are included in the demo by default.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
g++ -o AuthenticationDemo AuthenticationDemo.cpp request.cpp base64.cpp sha1.cpp -lcurl
/*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;
}
#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 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);
#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;
}
}
}
}
}
#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());
}
#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];
};
#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?