func main() { var accessKey string var secretKey string // if we werent provided these arguments, pull from environment if opts.AccessKey == "" { if os.Getenv("HMACURL_ACCESS_KEY") == "" { fmt.Println("Please provide access key via argument or environment variable HMACURL_ACCESS_KEY") os.Exit(3) } else { accessKey = os.Getenv("HMACURL_ACCESS_KEY") } } else { accessKey = opts.AccessKey } if opts.SecretKey == "" { if os.Getenv("HMACURL_SECRET_KEY") == "" { fmt.Println("Please provide secret key via argument or environment variable HMACURL_SECRET_KEY") os.Exit(3) } else { secretKey = os.Getenv("HMACURL_SECRET_KEY") } } else { secretKey = opts.SecretKey } if validation.Method(opts.Request) == false { fmt.Printf("method %s is invalid\n", opts.Request) os.Exit(1) } urlString, err := url.Parse(opts.Args.Url) if err != nil { fmt.Printf("Invalid url %s\n", opts.Args.Url) os.Exit(2) } var payload string if opts.Request == "POST" { if opts.Data != "" { payload = opts.Data } else if opts.File != "" { fileContents, err := ioutil.ReadFile(opts.File) if err != nil { panic(err) } // reading from file seems to put a newline at end - trim this payload = strings.TrimSuffix(string(fileContents[:]), "\n") } } requestTime := time.Now().UTC() host, _, err := net.SplitHostPort(urlString.Host) // likely no port if err != nil { host = urlString.Host } credentialScope := opts.CredentialScope if opts.CredentialScope == "" { credentialScope = strings.Split(host, ".")[0] } // setup headers headerMap := map[string]string{"x-amz-date": requestTime.Format("20060102T150405Z")} if opts.SkipHost == false { headerMap["host"] = urlString.Host } // add headers passed in from -H options to headerMap for k, v := range opts.Headers { headerMap[strings.ToLower(k)] = strings.ToLower(v) } // if we were not given a Content-Type, use the default standard if _, ok := headerMap["content-type"]; !ok { headerMap["content-type"] = "application/octet-stream" } // where we start the signing process - pass in http method, url, headers and payload. for GET requests payload should be "" canonicalString := canonicalRequest.FormatCanonicalString(opts.Request, urlString, headerMap, payload) if opts.Debug == true { fmt.Println("Canonical String:") fmt.Println(canonicalString) fmt.Println("================") } canonicalStringHashed := utilities.DataToSha256Encoded([]byte(canonicalString)) if opts.Debug == true { fmt.Println("Canonical String Hashed:") fmt.Println(canonicalStringHashed) fmt.Println("================") } stringToSign := signString.StringToSign(requestTime, canonicalStringHashed, opts.Region, credentialScope) if opts.Debug == true { fmt.Println("String to sign:") fmt.Println(stringToSign) fmt.Println("================") } signature := signature.CalculateSignature(requestTime, stringToSign, opts.Region, credentialScope, secretKey) headerMap["Authorization"] = utilities.GenerateSignedHeader(accessKey, signature, opts.Region, credentialScope, requestTime.Format("20060102"), canonicalRequest.FormatSignedHeaders(headerMap)) if opts.Debug == true { fmt.Println("signature:") fmt.Println(headerMap["Authorization"]) fmt.Println("================") } // signing process is complete start http calls // if we had a flag to only output the curl command, dump that and be done if opts.CurlOnly == true { headerStringBuild := "" for k, v := range headerMap { headerStringBuild += fmt.Sprintf(" %s '%s:%s'", "-H", k, v) } if opts.Request == "POST" { fmt.Printf("curl -X%s %s '%s' -v -d'%s'", opts.Request, headerStringBuild, urlString, payload) } else if opts.Request == "GET" { fmt.Printf("curl -X%s %s '%s' -v", opts.Request, headerStringBuild, urlString) } fmt.Println() os.Exit(0) } var client *http.Client if len(opts.Proxy) > 0 { proxyURL, err := url.Parse(opts.Proxy) if err != nil { fmt.Println("Error parsing proxy: " + err.Error()) os.Exit(1) } client = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}} } else { client = &http.Client{} } // make either a GET Request or POST Request if opts.Request == "GET" { req, err := http.NewRequest("GET", urlString.String(), nil) // add headers to request for k, v := range headerMap { req.Header.Add(k, v) } resp, err := client.Do(req) if err != nil { fmt.Println("error in http call") os.Exit(4) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) fmt.Println(string(body[:])) } else if opts.Request == "POST" { req, err := http.NewRequest("POST", urlString.String(), bytes.NewBufferString(payload)) // add headers to request for k, v := range headerMap { req.Header.Add(k, v) } resp, err := client.Do(req) if err != nil { fmt.Println("error in http call") os.Exit(4) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) fmt.Println(string(body[:])) } }
func FormatCanonicalString(method string, url *url.URL, headerMap map[string]string, payload string) string { // format string in HTTPRequestMethod, CanonicalURI, CanonicalQueryString, CanonicalHeaders, SignedHeaders, HexEncode(Hash(RequestPayload)) canonicalQueryStrings := strings.Replace(url.Query().Encode(), "+", "%20", -1) canonicalString := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, url.Path, canonicalQueryStrings, formatHeaders(headerMap), FormatSignedHeaders(headerMap), utilities.DataToSha256Encoded([]byte(payload))) return canonicalString }