Python和php签名通讯,Python和PHP对腾讯云签名hmac_sha256算法实现 - 米扑博客

开宗明义,米扑科技在使用腾讯云的API接口签名中,按照官方示例开发PHP、Python的接口,经常会提示签名错误

{

"Response": {

"Error": {

"Code": "InvalidParameter.SignatureFailure",

"Message": "The provided credentials could not be validated. Please check your signature is correct."

},

"RequestId": "1ee6ae98-a971-ad9f-4ecc-abcd69ea1234"

}

}

经过多次尝试探究,发现原因有二:

1)腾讯云官方示例不严谨,没有urlencode()或 urllib.quote()编码导致提示签名错误

2)腾讯官方只提供了PHP示例,没有提供Python示例,两者签名函数有一些细节

直接给出干货,下面示例是

PHP 签名示例

/**

* 签名并获取URL结果,json格式返回

*

* 1. 查询弹性IP列表, DescribeAddresses

* 2. 解绑弹性IP, DisassociateAddress

* 3. 释放弹性IP, ReleaseAddresses

* 4. 公网IP转弹性IP, TransformAddress

*

* @param string $req_action : DescribeAddresses, DisassociateAddress, ReleaseAddresses, TransformAddress

* @param string $params : 以 & 开头, 如 &xxx=yyy

*/

function qcloud_eip_sign($req_action='DescribeAddresses', $req_region='ap-beijing', $req_extra_params='', $retry_NUM=3) {

global $QCloud_SecretId;

global $QCloud_SecretKey;

// $req_action='DescribeAddresses'

// $req_region = 'ap-beijing';// ap-guangzhou

$req_method = 'GET';// GET POST

$req_api = 'eip.api.qcloud.com/v2/index.php';

$req_version = '2017-03-12';

$req_timestamp = strtotime(date('YmdHis'));// 1402992826

$req_nonce = rand(1000, 1000000);// 随机正整数

$req_secretid = $QCloud_SecretId;// 密钥ID,用作参数

$req_secretkey = $QCloud_SecretKey;// 密钥key,用作加密

$req_signature_method = 'HmacSHA256';// HmacSHA1(默认), HmacSHA256

$req_signature = '';

// $req_uri = "https://eip.api.qcloud.com/v2/index.php?Action=DescribeAddresses

// &Version=2017-03-12

// &AddressIds.1=eip-hxlqja90

// &Region=ap-beijing

// &Timestamp=1402992826

// &Nonce=345122

// &Signature=pStJagaKsV2QdkJnBQWYBDByZ9YPBsOi

// &SecretId=AKIDpY8cxBD2GLGK9sT0LaqIczGLFxTsoDF6

// 请求方法 + 请求主机 +请求路径 + ? + 请求字符串

$req_params = sprintf("Action=%s&Region=%s&Version=%s&Timestamp=%s&Nonce=%s&SecretId=%s&SignatureMethod=%s%s", $req_action, $req_region, $req_version, $req_timestamp, $req_nonce, $req_secretid, $req_signature_method, $req_extra_params);

$req_params_array = explode("&", $req_params);

sort($req_params_array);// 以value排序,value值为 Action=DescribeAddresses 、 Region=ap-beijing

$req_params2 = implode("&", $req_params_array);

$req_uri = sprintf("%s%s?%s", $req_method, $req_api, $req_params2);

$req_signature = urlencode(base64_encode(hash_hmac('sha256', $req_uri, $req_secretkey, true)));// urlencode(xxx)

$req_url = sprintf("https://%s?%s&Signature=%s", $req_api, $req_params2, $req_signature);

$res = curl_url($req_url);

$retry_idx = 0;

while(empty($res) && $retry_idx < $retry_NUM) {

$retry_idx += 1;

$res = curl_url($req_url);

}

if(!empty($res)) {

$resJson = json_decode($res, true);

$resJson = $resJson['Response'];

echo sprintf("
+++++ action : %s
resJson: ", $req_action);

print_r($resJson);

return $resJson;

}

else {

return null;

}

}

$req_action_query = 'DescribeAddresses';// 查询弹性IP

$req_action_unbind = 'DisassociateAddress';// 解绑弹性IP

$req_action_release = 'ReleaseAddresses';// 释放弹性IP

$req_action_transform = 'TransformAddress';// 公网IP转弹性IP

$req_region = 'ap-guangzhou';

$req_extra_params = '';

// 1. 查询弹性IP列表

$resJson = qcloud_eip_sign($req_action_query, $req_region);

var_dump($resJson);

运行结果:

req_url: https://eip.api.qcloud.com/v2/index.php?Action=DescribeAddresses&Nonce=585269&Region=ap-guangzhou&SecretId=AKIDSmAAAA2DABCDpTkBBBBMLMFwY0HM1234&SignatureMethod=HmacSHA256&Timestamp=1520429723&Version=2017-03-12&Signature=8U6i3BKBWYWoit3t1egIE9ZC%2BdWtI46QuHLc%2FbhaWWg%3D

array (size=3)

'TotalCount' => int 1

'AddressSet' =>

array (size=1)

0 =>

array (size=11)

'AddressId' => string 'eip-qy123abc' (length=12)

'AddressName' => null

'AddressIp' => string '111.230.123.234' (length=15)

'AddressStatus' => string 'BIND' (length=4)

'InstanceId' => string 'ins-fabc1234' (length=12)

'NetworkInterfaceId' => null

'PrivateAddressIp' => string '10.104.245.26' (length=14)

'IsArrears' => boolean false

'IsBlocked' => boolean false

'IsEipDirectConnection' => boolean false

'CreatedTime' => string '2018-03-07T12:46:26Z' (length=20)

'RequestId' => string 'ad28067e-d1f9-4c47-932e-6bba1d123456' (length=36)

代码说明:

1)函数抽象封装签名方法,方便管理维护,减少开发工作量

2)参数按照升序排列 explode(xxx) —> sort($req_params_array) —> implode(xxx)

3)签名方法,需要添加 urlencode,否则经常提示签名错误,原因是未urlencode会有一些 空格、加号(+)、等号(=)等特殊字符

$req_signature = urlencode(base64_encode(hash_hmac('sha256', $req_uri, $req_secretkey, true)));// urlencode(xxx)

Python 签名示例

#!/usr/bin/env python

# -*- coding:utf-8 -*-

#

# mimvp.com

# 2018-01-08

import time, datetime, os, json

import urllib, urllib2

import hashlib, base64, hmac, random

import logging

import logging.handlers

import sys

reload(sys)

sys.setdefaultencoding('utf-8')

## 腾讯云API接口签名

def qcloud_eip_sign(req_action='DescribeAddresses', req_region='ap-beijing', req_extra_params='', retry_NUM=3):

req_method = 'GET' # GET POST

req_api = 'eip.api.qcloud.com/v2/index.php'

req_version = '2017-03-12'

req_timestamp = int(time.time()) # 1520422452

req_nonce = random.randint(1000, 1000000) # 随机正整数

req_secretid = QCLOUD_SecretId # 密钥ID,用作参数

req_secretkey = QCLOUD_SecretKey # 密钥key,用作加密

req_signature_method = 'HmacSHA256' # HmacSHA1(默认), HmacSHA256

req_signature = ''

# req_uri = "https://eip.api.qcloud.com/v2/index.php?Action=DescribeAddresses

# &Version=2017-03-12

# &AddressIds.1=eip-hxlqja90

# &Region=ap-beijing

# &Timestamp=1402992826

# &Nonce=345122

# &Signature=pStJagaKsV2QdkJnBQWYBDByZ9YPBsOi

# &SecretId=AKIDpY8cxBD2GLGK9sT0LaqIczGLFxTsoDF6

# 请求方法 + 请求主机 +请求路径 + ? + 请求字符串

req_params = "Action=%s&Region=%s&Version=%s&Timestamp=%s&Nonce=%s&SecretId=%s&SignatureMethod=%s%s" % (req_action, req_region, req_version, req_timestamp, req_nonce, req_secretid, req_signature_method, req_extra_params)

req_params_array = req_params.split('&')

req_params_array = sorted(req_params_array) # 以value排序,value值为 Action=DescribeAddresses 、 Region=ap-beijing

req_params2 = '&'.join(req_params_array);

req_uri = "%s%s?%s" % (req_method, req_api, req_params2)

req_signature = urllib.quote( base64.b64encode(hmac.new(req_secretkey, req_uri, digestmod=hashlib.sha256).digest()) )# urllib.quote(xxx)

req_url = "https://%s?%s&Signature=%s" % (req_api, req_params2, req_signature)

logger.info('qcloud_eip_sign() - req_url: %s' % (req_url))

res = spider_url(req_url)

retry_idx = 0;

while not res and retry_idx < retry_NUM:

retry_idx += 1

res = spider_url(req_url)

if res :

resJson = json.loads(res)

resJson = resJson['Response']

print "
+++++ action : %s
resJson: " % (req_action,)

return resJson

else:

return None;

if __name__ == "__main__":

req_action_query = 'DescribeAddresses' # 查询弹性IP

req_action_unbind = 'DisassociateAddress' # 解绑弹性IP

req_action_release = 'ReleaseAddresses' # 释放弹性IP

req_action_transform = 'TransformAddress' # 公网IP转弹性IP

req_region='ap-guangzhou'

req_extra_params = '';

# 1. 查询弹性IP列表

resJson = qcloud_eip_sign(req_action_query, req_region)

print json.dumps(resJson)

运行结果:

req_url: https://eip.api.qcloud.com/v2/index.php?Action=DescribeAddresses&Nonce=383782&Region=ap-guangzhou&SecretId=AKIDSmAAAA2DABCDpTkBBBBMLMFwY0HM1234&SignatureMethod=HmacSHA256&Timestamp=1520430569&Version=2017-03-12&Signature=Tsgwx2GV/%2BopDlHiMUg3H/rpIbQ5jPfe9tW3w9Slom4%3D

{

"Response": {

"TotalCount": 1,

"AddressSet": [

{

"AddressId": "eip-qy123abc",

"AddressName": null,

"AddressIp": "111.230.123.234",

"AddressStatus": "BIND",

"InstanceId": "ins-fabc1234",

"NetworkInterfaceId": null,

"PrivateAddressIp": "10.104.245.26",

"IsArrears": false,

"IsBlocked": false,

"IsEipDirectConnection": false,

"CreatedTime": "2018-03-07T12:46:26Z"

}

],

"RequestId": "c2ab3f7f-9796-4ade-afb1-6bba1d123456"

}

}

代码说明:

1)Python改写PHP代码,有一些细节,如 int(time.time())、random.randint(1000, 1000000)、sorted(req_params_array)等

2)参数按照升序排列 xxx.split('&') —> sort($req_params_array) —> '&'.join(xxx)

3)签名方法,需要添加 urllib.quote、base64.b64encode(xxx)、digest()等,否则经常提示签名错误

req_signature = urllib.quote( base64.b64encode(hmac.new(req_secretkey, req_uri, digestmod=hashlib.sha256).digest()) )# urllib.quote(xxx)

Python 代码里,特别要注意 hmac 签名 sha256 后获取的是 digest(),而不是 hexdigest() 这里错了会一直提示签名错误!

总结之PHP和Python的对应关系

1) PHP 签名

// sha1

$hmac_sha1_str = base64_encode(hash_hmac("sha1", $data, $secret_access_key));// HMAC-SHA1加密

$signature = urlencode($hmac_sha1_str);// 编码URL

// sha256

$hmac_sha256_str = base64_encode(hash_hmac("sha256", $data, $secret_access_key));// HMAC-SHA256加密

$signature = urlencode($hmac_sha256_str);// 编码URL

2)Python 签名

import urllib, base54, hashlib, hmac

# sha1

hmac_sha1_str = base64.b64encode( hmac.new(secret_access_key, data, digestmod=hashlib.sha1).digest() )

signature = urllib.quote(hmac_sha1_str)

# sha256

hmac_sha256_str = base64.b64encode( hmac.new(secret_access_key, data, digestmod=hashlib.sha256).digest() )

signature = urllib.quote(hmac_sha256_str)

# sha256

hmac_sha256_str = base64.b64encode( hmac.new(secret_access_key, data, digestmod=hashlib.sha256).hexdigest() )# 16进制,错误

signature = urllib.quote(hmac_sha256_str)

Python hashlib、hmac模块

Python中的用于加密的函数位于hashlib,hmac模块中,都是内置模块,直接导入即可使用

hashlib模块实现了md5,sha1,sha224,sha256,sha384,sha512等算法,可以通过hashlib.algorithms_available查看

hmac模块实现了hmac算法,需要一个key来进行加密

hashlib用法如下:

#导入hashlib模块

>>> import hashlib

#python可用的加密函数

>>> hashlib.algorithms_available

{'sha384', 'DSA', 'SHA224', 'sha1','sha224', 'SHA384', 'ripemd160', 'MD5', 'whirlpool', 'SHA', 'MD4', 'SHA512','ecdsa-with-SHA1', 'dsaWithSHA', 'md5', 'sha256', 'DSA-SHA', 'SHA1', 'RIPEMD160','sha', 'md4', 'SHA256', 'dsaEncryption', 'sha512'}

#python在所有平台上都可以使用的函数,也就是比较稳定的函数

>>> hashlib.algorithms_guaranteed

{'md5', 'sha256', 'sha384', 'sha1','sha224', 'sha512'}

#创建一个加密函数对象

>>> m = hashlib.md5()

>>> m.update(b'python isinteresting')

>>> m.hexdigest()

'f00243cac6d9aa2d320ed5603061483b'

>>> mySha1 = hashlib.sha1()

>>> mySha1.update(b'python is interesting')

>>> mySha1.hexdigest()

'6ad9d2ccb5fe1d5324092bdac233b4ee49d71cb8'

#如果有中文的话,使用gb2312编码

>>> myMd5 = hashlib.md5('python 真好玩'.encode('gb2312'))

>>> myMd5.hexdigest()

'6c0f33c5f4b96f1aa771bf432ba53002'

hmac用法如下:

>>> import hmac

>>> myhmac = hmac.new(b'mykey')

>>> myhmac.update(b'mimvp_data')

>>> myhmac.hexdigest()

'399146c97e1a58c74877976a460d06c7'

>>> myhmac.digest()

'9\x91F\xc9~\x1aX\xc7Hw\x97jF\r\x06\xc7'

hmac用法对比digest和hexdigest

>>> import hashlib

>>> import hmac

>>> hmac.new(b'mykey', b'mimvp_data', digestmod=hashlib.sha256).hexdigest()

'fbb30b728fd1463ea1e00d4fb3fadf3b1cb5735168a4393ed6b7c29a8352aa3b'

>>> hmac.new(b'mykey', b'mimvp_data', digestmod=hashlib.sha256).digest()

'\xfb\xb3\x0br\x8f\xd1F>\xa1\xe0\rO\xb3\xfa\xdf;\x1c\xb5sQh\xa49>\xd6\xb7\xc2\x9a\x83R\xaa;'

各种语言版本的 HMAC-SHA256的base64加密

语言包含:Javascript ,PHP,Java,Groovy,C#,Objective C,Go,Ruby,Python,Perl,Dart,Swift,Rust,Powershell

Javascript HMAC SHA256

Run the code online with this jsfiddle. Dependent upon an open source js library called http://code.google.com/p/crypto-js/.

var hash = CryptoJS.HmacSHA256("Message", "secret");

var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);

document.write(hashInBase64);

PHP HMAC SHA256

PHP has built in methods for hash_hmac (PHP 5) and base64_encode (PHP 4, PHP 5) resulting in no outside dependencies. Say what you want about PHP but they have the cleanest code for this example.

$s = hash_hmac('sha256', 'Message', 'secret', true);

echo base64_encode($s);

Java HMAC SHA256

Dependent on Apache Commons Codec to encode in base64.

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class ApiSecurityExample {

public static void main(String[] args) {

try {

String secret = "secret";

String message = "Message";

Mac sha256_HMAC = Mac.getInstance("HmacSHA256");

SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");

sha256_HMAC.init(secret_key);

String hash = Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));

System.out.println(hash);

}

catch (Exception e){

System.out.println("Error");

}

}

}

Groovy HMAC SHA256

It is mostly java code but there are some slight differences. Adapted from Dev Takeout - Groovy HMAC/SHA256 representation.

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.security.InvalidKeyException;

def hmac_sha256(String secretKey, String data) {

try {

Mac mac = Mac.getInstance("HmacSHA256")

SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256")

mac.init(secretKeySpec)

byte[] digest = mac.doFinal(data.getBytes())

return digest

} catch (InvalidKeyException e) {

throw new RuntimeException("Invalid key exception while converting to HMac SHA256")

}

}

def hash = hmac_sha256("secret", "Message")

encodedData = hash.encodeBase64().toString()

log.info(encodedData)

C# HMAC SHA256

using System.Security.Cryptography;

namespace Test

{

public class MyHmac

{

private string CreateToken(string message, string secret)

{

secret = secret ?? "";

var encoding = new System.Text.ASCIIEncoding();

byte[] keyByte = encoding.GetBytes(secret);

byte[] messageBytes = encoding.GetBytes(message);

using (var hmacsha256 = new HMACSHA256(keyByte))

{

byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);

return Convert.ToBase64String(hashmessage);

}

}

}

}

Objective C and Cocoa HMAC SHA256

Most of the code required was for converting to bae64 and working the NSString and NSData data types.

#import "AppDelegate.h"

#import

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

NSString* key = @"secret";

NSString* data = @"Message";

const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];

const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];

unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];

CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);

NSData *hash = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

NSLog(@"%@", hash);

NSString* s = [AppDelegate base64forData:hash];

NSLog(s);

}

+ (NSString*)base64forData:(NSData*)theData {

const uint8_t* input = (const uint8_t*)[theData bytes];

NSInteger length = [theData length];

static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];

uint8_t* output = (uint8_t*)data.mutableBytes;

NSInteger i;

for (i=0; i < length; i += 3) {

NSInteger value = 0;

NSInteger j;

for (j = i; j < (i + 3); j++) {

value <<= 8;

if (j < length) { value |= (0xFF & input[j]); } } NSInteger theIndex = (i / 3) * 4; output[theIndex + 0] = table[(value >> 18) & 0x3F];

output[theIndex + 1] = table[(value >> 12) & 0x3F];

output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';

output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';

}

return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; }

@end

Go programming language - Golang HMAC SHA256

package main

import (

"crypto/hmac"

"crypto/sha256"

"encoding/base64"

"fmt"

)

func ComputeHmac256(message string, secret string) string {

key := []byte(secret)

h := hmac.New(sha256.New, key)

h.Write([]byte(message))

return base64.StdEncoding.EncodeToString(h.Sum(nil))

}

func main() {

fmt.Println(ComputeHmac256("Message", "secret"))

}

Ruby HMAC SHA256

require 'openssl'

require "base64"

hash = OpenSSL::HMAC.digest('sha256', "secret", "Message")

puts Base64.encode64(hash)

Python (2.7) HMAC SHA256

import hashlib

import hmac

import base64

message = bytes("Message").encode('utf-8')

secret = bytes("secret").encode('utf-8')

signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest())

print(signature)

Tested with Python 2.7.6. Also, be sure not to name your python demo script the same as one of the imported libraries.

Perl HMAC SHA256

See Digest::SHA documentation. By convention, the Digest modules do not pad their Base64 output. To fix this you can test the length of the hash and append equal signs "=" until it is the length is a multiple of 4. We will use a modulus function below.

use Digest::SHA qw(hmac_sha256_base64);

$digest = hmac_sha256_base64("Message", "secret");

# digest is currently: qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc

# Fix padding of Base64 digests

while (length($digest) % 4) {

$digest .= '=';

}

print $digest;

# digest is now: qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc=

Dart HMAC SHA256

Dependent upon the Dart crypto package.

import 'dart:html';

import 'dart:convert';

import 'package:crypto/crypto.dart';

void main() {

String secret = 'secret';

String message = 'Message';

List secretBytes = UTF8.encode('secret');

List messageBytes = UTF8.encode('Message');

var hmac = new HMAC(new SHA256(), secretBytes);

hmac.add(messageBytes);

var digest = hmac.close();

var hash = CryptoUtils.bytesToBase64(digest);

// output to html page

querySelector('#hash').text = hash;

// hash => qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc=

}

Swift HMAC SHA256

I have not verified but see this stackOverflow post

Rust

Take a look at the alco/rust-digest repository for Rust (lang) guidance. I have not verified yet.

Powershell (Windows) HMAC SHA256

Mostly wrapping of .NET libraries but useful to see it in powershell's befuddling syntax. See code as gist

$message = 'Message'

$secret = 'secret'

$hmacsha = New-Object System.Security.Cryptography.HMACSHA256

$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)

$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))

$signature = [Convert]::ToBase64String($signature)

echo $signature

# Do we get the expected signature?

echo ($signature -eq 'qnR8UCqJggD55PohusaBNviGoOJ67HC6Btry4qXLVZc=')

参考推荐:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值