最近在调用阿里云短信服务接口发现并没有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)描述文档,先做一些必要的准备:
*注册阿里云,有需要的阿里云产品通用代金券的可以点击下面链接进行领取:
https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=pfb80n4a
开通短信服务后,在你账户头像下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等等