阿里雲簡訊服務介面的c++實現
最近在呼叫阿里雲簡訊服務介面發現並沒有c++的資料,網上查詢發現也是零星描述,由於自身專案基礎累積了C++的太多,不想摻雜太多語言,就自行實現c++的阿里雲簡訊介面,其難點就在於簽名而已,希望能給大家提供參考。
1)我的網頁呼叫介面採用acl_master實現的,acl_master是一個跨平臺c/c++庫,提供了網路通訊庫及伺服器程式設計框架,同時提供更多的實用功能庫及示例。(下載地址:Github: https://github.com/acl-dev/acl),讀者可採用其他庫實現。
2)參考阿里雲的“HTTP協議及簽名”(https://help.aliyun.com/document_detail/56189.html?spm=a2c4g.11186623.6.581.fy2faU)描述文件,先做一些必要的準備:
*註冊阿里雲,有需要的阿里雲產品通用代金券的可以點選下面連結進行領取:
開通簡訊服務後,在你賬戶頭像下AccessKeys選項進入使用者資訊管理介面,獲取到你的AccessKeyId和AccessKeySecret,具體請參考阿里雲簡訊服務的資料完成操作
*根據"http協議及簽名”描述文件,定義了簡訊請求引數結構,初始化中帶*的請自行寫入自己實際引數:
struct HttpMsgArg { HttpMsgArg() : addr_("dysmsapi.aliyuncs.com") , path_("") , port_(80) // , AccessKeyId("********") , AccessKeySecret("***************") , Timestamp("2018-04-13T10:10:10Z") , Format("XML") , SignatureMethod("HMAC-SHA1") , SignatureVersion("1.0") , SignatureNonce("1") , Signature("") // , Action("SendSms") , Version("2017-05-25") , RegionId("cn-hangzhou") , PhoneNumbers("137********") , SignName("簡訊測試") , TemplateCode("SMS_*******") , TemplateParam("") , SmsUpExtendCode("") , OutId("123") { }; // std::string addr_; // web 伺服器地址 std::string path_; // 本地請求的資料檔案 int port_; // std::string AccessKeyId; // std::string AccessKeySecret; // std::string Timestamp; //格式為:yyyy-MM-dd’T’HH:mm:ss’Z’;時區為:GMT std::string Format; //沒傳預設為JSON,可選填值:XML std::string SignatureMethod; //建議固定值:HMAC-SHA1 std::string SignatureVersion; //建議固定值:1.0 std::string SignatureNonce; //用於請求的防重放攻擊,每次請求唯一,JAVA語言建議用:java.util.UUID.randomUUID()生成即可 std::string Signature; //最終生成的簽名結果值 std::string Action; //API的命名,固定值,如傳送簡訊API的值為:SendSms std::string Version; //API的版本,固定值,如簡訊API的值為:2017-05-25 std::string RegionId; //API支援的RegionID,如簡訊API的值為:cn-hangzhou /* *簡訊接收號碼,支援以逗號分隔的形式進行批量呼叫, *批量上限為1000個手機號碼,批量呼叫相對於單條呼叫及時性稍有延遲, *驗證碼型別的簡訊推薦使用單條呼叫的方式 */ std::string PhoneNumbers; std::string SignName; //簡訊簽名 std::string TemplateCode; //簡訊模板ID /* 簡訊模板變數替換JSON串,友情提示:如果JSON中需要帶換行符,請參照標準的JSON協議。 */ std::string TemplateParam; std::string SmsUpExtendCode; //上行簡訊擴充套件碼,無特殊需要此欄位的使用者請忽略此欄位 std::string OutId; //外部流水擴充套件欄位 };
*url格式轉換需求:
unsigned char ToHex(unsigned char x) { return x > 9 ? x + 55 : x + 48; } unsigned char FromHex(unsigned char x) { unsigned char y; if (x >= 'A' && x <= 'Z') y = x - 'A' + 10; else if (x >= 'a' && x <= 'z') y = x - 'a' + 10; else if (x >= '0' && x <= '9') y = x - '0'; else assert(0); return y; } std::string UrlEncode(const std::string& str) { std::string strTemp = ""; size_t length = str.length(); for (size_t i = 0; i < length; i++) { if (isalnum((unsigned char)str[i]) || (str[i] == '-') || (str[i] == '_') || (str[i] == '.') || (str[i] == '~')) strTemp += str[i]; else if (str[i] == ' ') strTemp += "+"; else { strTemp += '%'; strTemp += ToHex((unsigned char)str[i] >> 4); strTemp += ToHex((unsigned char)str[i] % 16); } } return strTemp; } std::string UrlDecode(const std::string& str) { std::string strTemp = ""; size_t length = str.length(); for (size_t i = 0; i < length; i++) { if (str[i] == '+') strTemp += ' '; else if (str[i] == '%') { assert(i + 2 < length); unsigned char high = FromHex((unsigned char)str[++i]); unsigned char low = FromHex((unsigned char)str[++i]); strTemp += high * 16 + low; } else strTemp += str[i]; } return strTemp; }
*UUID的生成實現函式:
std::string getUUID() {
#ifdef WIN32
char buffer[GUID_LEN] = { 0 };
GUID guid;
if (CoCreateGuid(&guid))
{
fprintf(stderr, "create guid error\n");
return "";
}
_snprintf(buffer, sizeof(buffer),
"%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5],
guid.Data4[6], guid.Data4[7]);
printf("guid: %s\n", buffer);
return std::string(buffer);
#else
uuid_t uuid;
uuid_generate(uuid);
char buf[64] = { 0 };
uuid_unparse(uuid, buf);
return std::string(buf);
#endif
};
*用於ascii和utf-8的字元格式轉換,一些其他的格式轉換也一併給出,防止特定需要,主要是阿里引數中文可能需要用到
/////////////hpp////////////////////
#ifdef WIN32
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#endif
#ifndef STRCHANGE_HPP
#define STRCHANGE_HPP
#include <string>
#include <vector>
#ifdef WIN32
#include <iostream>
#include <windows.h>
#endif
//using namespace std;
bool containStr(std::string _str, std::string _strsub);
#ifdef WIN32
//UTF-8 to Unicode
std::wstring Utf82Unicode(const std::string& utf8string);
//unicode to ascii
std::string WideByte2Acsi(std::wstring& wstrcode);
//ascii to Unicode
std::wstring Acsi2WideByte(std::string& strascii);
//Unicode to Utf8
std::string Unicode2Utf8(const std::wstring& widestring);
#endif
#ifdef __linux__
int code_convert(char *from_charset,char *to_charset,char *inbuf,size_t inlen,char *outbuf,size_t outlen);
int u2g(char *inbuf,int inlen,char *outbuf,int outlen);
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen);
int u2a(char *inbuf,int inlen,char *outbuf,int outlen);
int a2u(char *inbuf,int inlen,char *outbuf,int outlen);
int u2k(char *inbuf,int inlen,char *outbuf,int outlen);
int k2u(char *inbuf,int inlen,char *outbuf,int outlen);
#endif
//utf-8 to ascii
std::string UTF_82ASCII(std::string& strUtf8Code);
//ascii to Utf8
std::string ASCII2UTF_8(std::string& strAsciiCode);
#endif //STRCHANGE_HPP
/////////////////////////////////////////////
#include "strchange.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef __linux__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>
#endif
//using namespace std;
bool containStr(std::string _str, std::string _strsub)
{
std::string::size_type pos = _str.find(_strsub);//
if(pos!=std::string::npos){
return true;
}
return false;
};
void strToHex16(char *in,unsigned char *out,int size)
{
unsigned int ch;
int index=0;
while(sscanf(in,"%2x",&ch)!=EOF&&index<size)
{
out[index++]=ch;
in+=2;
}
out[index<size?index:(size-1)]='\0';
};
void strFromHex16(unsigned char *in,char *out,int size)
{
int i=0;
while('\0'!=in[i]&&(2*i<size))
{
//char ch[2]={0};
sprintf(out+i*2,"%02x",in[i]);
i++;
}
out[2*i<size?2*i:(size-1)]='\0';
};
#ifdef WIN32
//UTF-8 to Unicode
std::wstring Utf82Unicode(const std::string& utf8string)
{
int widesize = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, NULL, 0);
if (widesize == ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8 sequence.");
}
if (widesize == 0)
{
throw std::exception("Error in conversion.");
}
std::vector<wchar_t> resultstring(widesize);
int convresult = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, &resultstring[0], widesize);
if (convresult != widesize)
{
throw std::exception("La falla!");
}
return std::wstring(&resultstring[0]);
};
//unicode to ascii
std::string WideByte2Acsi(std::wstring& wstrcode)
{
int asciisize = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, NULL, 0, NULL, NULL);
if (asciisize == ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8 sequence.");
}
if (asciisize == 0)
{
throw std::exception("Error in conversion.");
}
std::vector<char> resultstring(asciisize);
int convresult =::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, &resultstring[0], asciisize, NULL, NULL);
if (convresult != asciisize)
{
throw std::exception("La falla!");
}
return std::string(&resultstring[0]);
};
//////////////////////////////cpp/////////////////////////////////////////
//ascii to Unicode
std::wstring Acsi2WideByte(std::string& strascii)
{
int widesize = MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1, NULL, 0);
if (widesize == ERROR_NO_UNICODE_TRANSLATION)
{
throw std::exception("Invalid UTF-8 sequence.");
}
if (widesize == 0)
{
throw std::exception("Error in conversion.");
}
std::vector<wchar_t> resultstring(widesize);
int convresult = MultiByteToWideChar (CP_ACP, 0, (char*)strascii.c_str(), -1, &resultstring[0], widesize);
if (convresult != widesize)
{
throw std::exception("La falla!");
}
return std::wstring(&resultstring[0]);
};
//Unicode to Utf8
std::string Unicode2Utf8(const std::wstring& widestring)
{
int utf8size = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, NULL, 0, NULL, NULL);
if (utf8size == 0)
{
throw std::exception("Error in conversion.");
}
std::vector<char> resultstring(utf8size);
int convresult = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, &resultstring[0], utf8size, NULL, NULL);
if (convresult != utf8size)
{
throw std::exception("La falla!");
}
return std::string(&resultstring[0]);
};
#endif
#ifdef __linux__
//
int code_convert(char *from_charset,char *to_charset,char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
iconv_t cd;
//int rc;
char **pin = &inbuf;
char **pout = &outbuf;
cd = iconv_open(to_charset,from_charset);
if (cd==0) return -1;
memset(outbuf,0,outlen);
if (-1==(int)iconv(cd,pin,&inlen,pout,&outlen))
return -1;
iconv_close(cd);
return 0;
}
//
int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
char _fbuf[32]="utf-8";
char _tbuf[32]="gb2312";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
//
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
char _fbuf[32]="gb2312";
char _tbuf[32]="utf-8";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
int u2a(char *inbuf,int inlen,char *outbuf,int outlen)
{
char _fbuf[32]="utf-8";
char _tbuf[32]="ascii";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
int a2u(char *inbuf,int inlen,char *outbuf,int outlen)
{
char _fbuf[32]="ascii";
char _tbuf[32]="utf-8";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
int u2k(char *inbuf,int inlen,char *outbuf,int outlen)
{
char _fbuf[32]="utf-8";
char _tbuf[32]="gbk";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
int k2u(char *inbuf,int inlen,char *outbuf,int outlen)
{
char _fbuf[32]="gbk";
char _tbuf[32]="utf-8";
return code_convert(_fbuf,_tbuf,inbuf,inlen,outbuf,outlen);
}
#endif
//utf-8 to ascii
std::string UTF_82ASCII(std::string& strUtf8Code)
{
#ifdef WIN32
std::string strRet("");
//
std::wstring wstr = Utf82Unicode(strUtf8Code);
//
strRet = WideByte2Acsi(wstr);
return strRet;
#endif
#ifdef __linux__
char lpszBuf[256]={0};
u2k(const_cast<char*>(strUtf8Code.c_str()),strUtf8Code.size(),lpszBuf,256);
return std::string(lpszBuf);
// char lpszBuf[256]={0};
// u2a(const_cast<char*>(strUtf8Code.c_str()),strUtf8Code.size(),lpszBuf,256);
// return std::string(lpszBuf);
// return std::string(strUtf8Code);
#endif
};
//ascii to Utf8
std::string ASCII2UTF_8(std::string& strAsciiCode)
{
#ifdef WIN32
std::string strRet("");
//
std::wstring wstr = Acsi2WideByte(strAsciiCode);
//
strRet = Unicode2Utf8(wstr);
return strRet;
#endif
#ifdef __linux__
char lpszBuf[256]={0};
k2u(const_cast<char*>(strAsciiCode.c_str()),strAsciiCode.size(),lpszBuf,256);
return std::string(lpszBuf);
// char lpszBuf[256]={0};
// a2u(const_cast<char*>(strAsciiCode.c_str()),strAsciiCode.size(),lpszBuf,256);
// return std::string(lpszBuf);
// return std::string(strAsciiCode);
#endif
};
*阿里雲簡訊介面引數的時間格式要求:
std::string getCurentTimeDesc()
{
time_t _t = time(NULL);
char bufTime[64] = { 0 };
struct tm* _tt;
//localtime_s(&_tt, &_t);//系統本地時間
_tt = gmtime(&_t);//格林時間
_tt->tm_year += 1900;
_tt->tm_mon += 1;
sprintf(bufTime, "%04d-%02d-%02dT%02d:%02d:%02dZ"
, _tt->tm_year,_tt->tm_mon,_tt->tm_mday,_tt->tm_hour,_tt->tm_min,_tt->tm_sec);
return std::string(bufTime);
}
*重點一,簽名演算法實現,來源網上公開的一些資料,經過實踐實用c語言函式
//////////////////HMACSHA1.h///////////////////
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef __HMACSHA1_H__
#define __HMACSHA1_H__
typedef unsigned long __u32;
typedef unsigned char __u8;
typedef struct
{
__u32 state[5];
__u32 count[2];
__u8 buffer[64];
} SHA1_CTX;
#if defined(rol)
#undef rol
#endif
#define SHA1HANDSOFF
#define __LITTLE_ENDIAN
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#ifdef __LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#else
#define blk0(i) block->l[i]
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(__u32 state[5], __u8 buffer[64]);
void SHA1Init(SHA1_CTX *context);
void SHA1Update(SHA1_CTX *context, unsigned char *data, __u32 len);
void SHA1Final(unsigned char digest[20], SHA1_CTX *context);
//void hmac_sha1(unsigned char *to_mac,unsigned int to_mac_length, unsigned char *key,unsigned int key_length, unsigned char *out_mac);
void hmac_sha
(
char* k, /* 祕鑰 secret key */
int lk, /* 祕鑰長度 length of the key in bytes */
char* d, /* 資料 data */
int ld, /* 資料長度 length of data in bytes */
char* out, /* 輸出的字串 output buffer, at least "t" bytes */
int t
);
#endif
///////////////////////////////HMACSHA1.cpp///////////////////////////////////////////////
#include "HMACSHA1.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#ifndef SHA_DIGESTSIZE
#define SHA_DIGESTSIZE 20
#endif
#ifndef SHA_BLOCKSIZE
#define SHA_BLOCKSIZE 64
#endif
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(__u32 state[5], __u8 buffer[64])
{
__u32 a, b, c, d, e;
typedef union {
unsigned char c[64];
__u32 l[16];
} CHAR64LONG16;
CHAR64LONG16* block;
#ifdef SHA1HANDSOFF
static unsigned char workspace[64];
block = (CHAR64LONG16*)workspace;
// NdisMoveMemory(block, buffer, 64);
memcpy(block, buffer, 64);
#else
block = (CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3);
R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7);
R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11);
R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15);
R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
}
/* SHA1Init - Initialize new context */
void SHA1Init(SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, unsigned char* data, __u32 len)
{
__u32 i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len >> 29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
// NdisMoveMemory(&context->buffer[j], data, (i = 64-j));
memcpy(&context->buffer[j], data, (i = 64 - j));
SHA1Transform(context->state, context->buffer);
for (; i + 63 < len; i += 64) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
}
else i = 0;
// NdisMoveMemory(&context->buffer[j], &data[i], len - i);
memcpy(&context->buffer[j], &data[i], len - i);
}
/* Add padding and return the message digest. */
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
__u32 i, j;
unsigned char finalcount[8];
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
}
SHA1Update(context, (unsigned char *)"\200", 1);
while ((context->count[0] & 504) != 448) {
SHA1Update(context, (unsigned char *)"\0", 1);
}
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
}
/* Wipe variables */
i = j = 0;
// NdisZeroMemory(context->buffer, 64);
// NdisZeroMemory(context->state, 20);
// NdisZeroMemory(context->count, 8);
// NdisZeroMemory(&finalcount, 8);
memset(context->buffer, 0x00, 64);
memset(context->state, 0x00, 20);
memset(context->count, 0x00, 8);
memset(&finalcount, 0x00, 8);
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
SHA1Transform(context->state, context->buffer);
#endif
}
/* Function to print the digest
列印輸出*/
void pr_sha(FILE* fp, unsigned char* s, int t)
{
int i;
for (i = 0; i<t; i++)
printf("%02x", s[i]);
printf("\n");
}
void truncate
(
char* d1, /* data to be truncated */
char* d2, /* truncated data */
int len /* length in bytes to keep */
)
{
int i;
for (i = 0; i < len; i++) d2[i] = d1[i];
}
/* Function to compute the digest
加密演算法的主要操作函式 */
void hmac_sha
(
char* k, /* 祕鑰 secret key */
int lk, /* 祕鑰長度 length of the key in bytes */
char* d, /* 資料 data */
int ld, /* 資料長度 length of data in bytes */
char* out, /* 輸出的字串 output buffer, at least "t" bytes */
int t
)
{
SHA1_CTX ictx, octx;
char isha[SHA_DIGESTSIZE], osha[SHA_DIGESTSIZE];
char key[SHA_DIGESTSIZE];
char buf[SHA_BLOCKSIZE];
int i;
if (lk > SHA_BLOCKSIZE)
{
SHA1_CTX tctx;
SHA1Init(&tctx);
SHA1Update(&tctx, (unsigned char*)k, lk);
SHA1Final((unsigned char*)key, &tctx);
k = key;
lk = SHA_DIGESTSIZE;
}
/**** Inner Digest ****/
SHA1Init(&ictx);
/* Pad the key for inner digest */
for (i = 0; i < lk; ++i) buf[i] = k[i] ^ 0x36;
for (i = lk; i < SHA_BLOCKSIZE; ++i) buf[i] = 0x36;
SHA1Update(&ictx, (unsigned char*)buf, SHA_BLOCKSIZE);
SHA1Update(&ictx, (unsigned char*)d, ld);
SHA1Final((unsigned char*)isha, &ictx);
/**** Outter Digest ****/
SHA1Init(&octx);
/* Pad the key for outter digest */
for (i = 0; i < lk; ++i) buf[i] = k[i] ^ 0x5C;
for (i = lk; i < SHA_BLOCKSIZE; ++i) buf[i] = 0x5C;
SHA1Update(&octx, (unsigned char*)buf, SHA_BLOCKSIZE);
SHA1Update(&octx, (unsigned char*)isha, SHA_DIGESTSIZE);
SHA1Final((unsigned char*)osha, &octx);
/* truncate and print the results */
t = t > SHA_DIGESTSIZE ? SHA_DIGESTSIZE : t;
truncate(osha, out, t);
pr_sha(stdout, (unsigned char*)out, t);
}
*重點二,Base64演算法實現
//////////////////////////ZBase64.h///////////////////////////////////
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef ___BASE_64_H___
#define ___BASE_64_H___
#include <string>
class ZBase64
{
public:
/*編碼
DataByte
[in]輸入的資料長度,以位元組為單位
*/
static std::string Encode(const unsigned char* Data, int DataByte);
/*解碼
DataByte
[in]輸入的資料長度,以位元組為單位
OutByte
[out]輸出的資料長度,以位元組為單位,請不要通過返回值計算
輸出資料的長度
*/
static std::string Decode(const char* Data, int DataByte, int& OutByte);
};
#endif
//////////////////////////////////ZBase64.cpp/////////////////////////////////////////////
#include "Base64.h"
std::string ZBase64::Encode(const unsigned char* Data, int DataByte)
{
//編碼表
const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//返回值
std::string strEncode;
unsigned char Tmp[4] = { 0 };
int LineLength = 0;
for (int i = 0; i<(int)(DataByte / 3); i++)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
Tmp[3] = *Data++;
strEncode += EncodeTable[Tmp[1] >> 2];
strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
strEncode += EncodeTable[Tmp[3] & 0x3F];
if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
}
//對剩餘資料進行編碼
int Mod = DataByte % 3;
if (Mod == 1)
{
Tmp[1] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
strEncode += "==";
}
else if (Mod == 2)
{
Tmp[1] = *Data++;
Tmp[2] = *Data++;
strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
strEncode += "=";
}
return strEncode;
}
std::string ZBase64::Decode(const char* Data, int DataByte, int& OutByte)
{
//解碼錶
const char DecodeTable[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, // '+'
0, 0, 0,
63, // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
0, 0, 0, 0, 0, 0,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
};
//返回值
std::string strDecode;
int nValue;
int i = 0;
while (i < DataByte)
{
if (*Data != '\r' && *Data != '\n')
{
nValue = DecodeTable[*Data++] << 18;
nValue += DecodeTable[*Data++] << 12;
strDecode += (nValue & 0x00FF0000) >> 16;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++] << 6;
strDecode += (nValue & 0x0000FF00) >> 8;
OutByte++;
if (*Data != '=')
{
nValue += DecodeTable[*Data++];
strDecode += nValue & 0x000000FF;
OutByte++;
}
}
i += 4;
}
else// 回車換行,跳過
{
Data++;
i++;
}
}
return strDecode;
}
3)準備工作完成,開始簡訊申請全過程,本人藉助acl-master庫實現,用其他網頁訪問介面的請斟酌參考
acl::string aclurl, acladdr;//定義網站、地址
aclurl.format("http://%s%s", myArgHttp.addr_.c_str(), myArgHttp.path_.c_str());
acladdr.format("%s:%d", myArgHttp.addr_.c_str(), myArgHttp.port_);
acl::http_request req(acladdr);
//請求構造
acl::http_header& header = req.request_header();
//訪問型別為Get
header.set_method(acl::HTTP_METHOD_GET);
header.set_keep_alive(false);//
header.accept_gzip(accept_gzip ? true : false);//
//url
header.set_url(aclurl);
//host
if (header.get_host() == NULL)
{
header.set_host(myArgHttp.addr_.c_str());
printf(">>>set host: %s\r\n", myArgHttp.addr_.c_str());
}
else
printf(">>>host: %s\r\n", header.get_host());
//簽名內容快取
std::string sortQueryStringTmp = "";
sortQueryStringTmp.append("GET&").append(UrlEncode("/"));
//簽名內容項,引數key的字串排序
std::string bufNonce = getUUID();
std::string Timestamp = getCurentTimeDesc();
myArgHttp.TemplateParam = ASCII2UTF_8(templateParam_);
acl::url_coder coder;//注意中文欄位UTF-8表述
coder.set("AccessKeyId", myArgHttp.AccessKeyId.c_str());
coder.set("Action", myArgHttp.Action.c_str());
coder.set("Format", myArgHttp.Format.c_str());
coder.set("OutId", myArgHttp.OutId.c_str());
coder.set("PhoneNumbers", myArgHttp.PhoneNumbers.c_str());
coder.set("RegionId", myArgHttp.RegionId.c_str());
coder.set("SignName", ASCII2UTF_8(myArgHttp.SignName).c_str());
coder.set("SignatureMethod", myArgHttp.SignatureMethod.c_str());
coder.set("SignatureNonce", bufNonce.c_str());
coder.set("SignatureVersion", myArgHttp.SignatureVersion.c_str());
coder.set("TemplateCode", myArgHttp.TemplateCode.c_str());
coder.set("TemplateParam", myArgHttp.TemplateParam.c_str());
coder.set("Timestamp", Timestamp.c_str());
coder.set("Version", myArgHttp.Version.c_str());
//
std::string urlcode = std::string(coder.to_string().c_str());//
printf( "排序引數集:\r\n%s\r\n", urlcode.c_str());
sortQueryStringTmp.append("&").append(UrlEncode(urlcode));
printf( "待簽名的請求字串UrlEncode:\r\n%s\r\n", sortQueryStringTmp.c_str());
//生成簽名,呼叫簽名演算法函式,注意阿里的AccessKeySecret+&才是真正的AccessKeySecret
char hmac_buf[256] = { 0 };
std::string KeySecret_ali = (myArgHttp.AccessKeySecret + "&");
hmac_sha((char*)(ASCII2UTF_8(KeySecret_ali)).c_str(), (int)KeySecret_ali.length()
, (char*)ASCII2UTF_8(sortQueryStringTmp).c_str(), (int)sortQueryStringTmp.length(), hmac_buf, 20);
std::string hmac = std::string(hmac_buf,strlen(hmac_buf));
printf( "金鑰+HmacSHA1演算法:\r\n%s\r\n", hmac.c_str());
//base64演算法呼叫,前面簽名演算法結果作為輸入引數,得到最終簽名
hmac = ZBase64::Encode((const unsigned char*)hmac.c_str(), static_cast<int>(hmac.length()));
printf( "HmacSHA1演算法+Base64:\r\n%s\r\n", hmac.c_str());
//將簽名加入請求欄位
header.add_param("Signature", hmac.c_str());
//其他引數加入,注意中文欄位UTF-8表述
//TemplateParam,例如,"{\"customer\":\"test\"}",
//進行傳輸簡訊的變數與你阿里簡訊服務模版的變數一致,(如都存在”customer“變數)
//如果模版變數個數在 TemplateParam沒有全含括或任意欄位不匹配,都無法正常簡訊傳送
//變數的描述欄位限制20位,例如,test不能超過20個字元長度,否則出錯
header.add_param("AccessKeyId", myArgHttp.AccessKeyId.c_str());
header.add_param("Action", myArgHttp.Action.c_str());
header.add_param("Format", myArgHttp.Format.c_str());
header.add_param("OutId", myArgHttp.OutId.c_str());
header.add_param("PhoneNumbers", myArgHttp.PhoneNumbers.c_str());
header.add_param("RegionId", myArgHttp.RegionId.c_str());
header.add_param("SignName", ASCII2UTF_8(myArgHttp.SignName).c_str());
header.add_param("SignatureMethod", myArgHttp.SignatureMethod.c_str());
header.add_param("SignatureNonce", bufNonce.c_str());
header.add_param("SignatureVersion", myArgHttp.SignatureVersion.c_str());
header.add_param("TemplateCode", myArgHttp.TemplateCode.c_str());
header.add_param("TemplateParam", myArgHttp.TemplateParam.c_str());
header.add_param("Timestamp", Timestamp.c_str());
header.add_param("Version", myArgHttp.Version.c_str());
bool rc = req.request(NULL, 0);//http請求
// 將 build_request 放在 req.request 後面,是因為
// req.request 內部可能會修改請求頭中的欄位
acl::string hdr;
header.build_request(hdr);
printf("request header:\r\n%s\r\n", hdr.c_str());
if (rc == false)
{
printf( "send request error");
return;
}
printf("send request ok!\r\n");
// 取出 HTTP 響應頭的 Content-Type 欄位
const char* p = req.header_value("Content-Type");
if (p == NULL || *p == 0)
{
printf( "no Content-Type");
return;
}
// 分析 HTTP 響應頭的資料型別
acl::http_ctype content_type;
content_type.parse(p);
// 響應頭資料型別的子型別
const char* stype = content_type.get_stype();
bool ret;
if (stype != NULL)
ret = do_plain(req);
if (ret == true)
printf("read ok!");
4)最終呈現效果
圖二的簽名與圖一部一致是因為兩張圖片分別來自兩個請求的截圖
5)阿里雲簡訊介面的http實現關鍵是做好籤名,然後按通用的http訪問請求實現即可,另外需要特別注意的是注意阿里裡面簡訊定義要求,例如變數不能包含限制字元,變數長度不能超過20等等