1.前言

云计算是当下非常流行的技术,各互联网公司基本上都在使用着不同云厂商的资源。我们要查看或者操作购买的云计算资源,一般都通过控制台登录直接操作。但是如果我们要批量操作资源,或者是开发自己的平台调用资源,那么我们该怎么办呢?其实各云计算厂家都提供了资源操作的接口,api和sdk。其中,api接口需要我们自行实现接口的签名认证,再调用厂商的各种方法,然后二次开发;sdk是厂商已经封装好的操作接口,我们可以直接调用,再在此基础上做开发。

2.代码实现

本文主要用go语言实现了金山云的api签名,并使用生成的签名调用金山云提供的DescribeInstance查看云主机的详细信息。

package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

func sign(key []byte, msg string) []byte {
	h := hmac.New(sha256.New, key)
	h.Write([]byte(msg))
	return h.Sum(nil)
}

func get_signature_key(key string, dateStamp string, regionName string, serviceName string) []byte {
	kDate := sign([]byte("AWS4"+key), dateStamp)
	kRegion := sign(kDate, regionName)
	kService := sign(kRegion, serviceName)
	kSigning := sign(kService, "aws4_request")
	return kSigning
}

func get_auth(access_key, secret_key, action, version, host, method, region, service string,
	req_params map[string]interface{}) (int, string, string) {
	var request_parameters string
	endpoint := "http://" + host
	params := url.Values{}
	params.Add("Action", action)
	params.Add("Version", version)
	if req_params != nil {
		for key := range req_params {
			v := fmt.Sprintf("%v", req_params[key])
			params.Add(key, v)
		}
	}
	request_parameters += params.Encode()
	if access_key == "" || secret_key == "" {
		fmt.Println("No access key is available.")
		return 400, "", ""
	}
	t := time.Now().UTC()
	amzdate := t.Format("20060102T150405Z")
	datestamp := t.Format("20060102")

	canonical_uri := "/"

	canonical_querystring := request_parameters

	canonical_headers := "content-type:application/json" + "\n" + "host:" + host + "\n" + "x-amz-date:" + amzdate + "\n"

	signed_headers := "content-type;host;x-amz-date"

	h := sha256.New()
	h.Write([]byte(""))
	payload_hash := fmt.Sprintf("%x", h.Sum(nil))

	canonical_request := method + "\n" + canonical_uri + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + signed_headers + "\n" + payload_hash

	algorithm := "AWS4-HMAC-SHA256"
	credential_scope := datestamp + "/" + region + "/" + service + "/" + "aws4_request"
	ha := sha256.New()

	ha.Write([]byte(canonical_request))
	hash := fmt.Sprintf("%x", ha.Sum(nil))
	string_to_sign := algorithm + "\n" + amzdate + "\n" + credential_scope + "\n" + hash

	signing_key := get_signature_key(secret_key, datestamp, region, service)

	s := hmac.New(sha256.New, signing_key)
	s.Write([]byte(string_to_sign))
	signature := fmt.Sprintf("%x", s.Sum(nil))

	authorization_header := algorithm + " " + "Credential=" + access_key + "/" + credential_scope + ", " + "SignedHeaders=" + signed_headers + ", " + "Signature=" + signature

	request_url := endpoint + "?" + canonical_querystring

	return 200, authorization_header, request_url
}

func Request(access_key, secret_key, action, version, host, method, region, service string,
	req_params map[string]interface{}) (int, string) {
	t := time.Now().UTC()
	amzdate := t.Format("20060102T150405Z")

	code, authorization_header, request_url := get_auth(access_key, secret_key, action, version, host, method, region, service,
		req_params)
	if code != 200 {
		fmt.Println("auth error!")
		return code, ""
	}
	client := &http.Client{}
	req, _ := http.NewRequest(method, request_url, strings.NewReader(""))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Amz-Date", amzdate)
	req.Header.Set("Authorization", authorization_header)
	req.Header.Set("Accept", "application/json")
	resp, _ := client.Do(req)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	code = resp.StatusCode

	return code, string(body)
}

func main() {
	ak := "your ak"
	sk := "your sk"
	region := "cn-beijing-6"
	action := "DescribeInstances"
	version := "2016-03-04"
	host := "kec.api.ksyun.com"
	methods := "GET"
	service := "kec"
	var req_params map[string]interface{}
	code, text := Request(ak, sk, action, version, host, methods, region, service, req_params)
	if code != 200 {
		fmt.Println("describe kec error!")

	}
	var str bytes.Buffer
	json.Indent(&str, []byte(text), "", " ")
	fmt.Println(str.String())
}