Introduction#
GitHub this note shows how to use AWS Signature V4 to sign a request
Setup Project#
Let create a new typescript project
npm init
Install typescript
npm install typescript --save-dev
Install ambient Node.js types for typescript
npm install @types/node --save-dev
Create a tsconfig.json as following
{"compilerOptions": {"target": "es5","module": "commonjs","lib": ["es6"],"allowJs": true,"outDir": "build","rootDir": "src","strict": true,"noImplicitAny": true,"esModuleInterop": true,"resolveJsonModule": true}}
Create Signature#
There are different ways or tools to create a signature v4
- step by step following the docs
- use @aws-sdk/signature-v4 or @aws-sdk/signature-v4-crt here
- use python lib here
Install dependencies
npm install @aws-sdk/signature-v4 @aws-sdk/protocol-http @aws-crypto/sha256-js axios
Create a signature signer
const signer = new SignatureV4({credentials: {accessKeyId: config.accessKeyId,secretAccessKey: config.secretAccessKey},region: 'us-east-1',service: 's3',sha256: Sha256})
Create a request
const request = new HttpRequest({path: new URL(config.endpoint).pathname,hostname: new URL(config.endpoint).hostname,protocol: 'https',method: 'GET',headers: {host: new URL(config.endpoint).hostname}})
Sign the request
signer.sign(request, {signingRegion: 'us-east-1',signingService: 's3',signingDate: new Date()}).then(response => {console.log(response)axios.get(config.endpoint, {headers: response.headers,responseType: 'stream'}).then(response => {console.log(response)response.data.pipe(fs.createWriteStream('./tree.jpg'))}).catch(error => {console.log(error)})})
Send Request#
After signed request, we can send the request to an S3 endpoint to get an image object
signer.sign(request, {signingRegion: 'us-east-1',signingService: 's3',signingDate: new Date()}).then(response => {console.log(response)axios.get(config.endpoint, {headers: response.headers,responseType: 'stream'}).then(response => {console.log(response)response.data.pipe(fs.createWriteStream('./tree.jpg'))}).catch(error => {console.log(error)})})
Smithy SignatureV4#
Let update using the Smithy SignatureV4
{"name": "smithy-signature-v4","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"@aws-crypto/sha256-js": "^5.2.0","@aws-sdk/credential-providers": "^3.515.0","@aws-sdk/protocol-http": "^3.374.0","@smithy/signature-v4": "^2.1.1","axios": "^1.6.7"},"devDependencies": {"@types/node": "^20.11.19","typescript": "^5.3.3"}}
Here is the detail code
smithy-signature-v4.ts
import { SignatureV4 } from '@smithy/signature-v4'import { HttpRequest } from '@aws-sdk/protocol-http'import { config } from './config'import { Sha256 } from '@aws-crypto/sha256-js'import { fromIni } from '@aws-sdk/credential-providers'import axios from 'axios'import * as fs from 'fs'const request = new HttpRequest({path: new URL(config.endpoint).pathname,hostname: new URL(config.endpoint).hostname,protocol: 'https',method: 'GET',headers: {host: new URL(config.endpoint).hostname}})const signer = new SignatureV4({// credentials: {// accessKeyId: config.accessKeyId,// secretAccessKey: config.secretAccessKey,// sessionToken: config.sessionToken,// },credentials: fromIni({profile: 'default'}),region: 'us-east-1',service: 's3',sha256: Sha256})signer.sign(request, {signingRegion: 'us-east-1',signingService: 's3',signingDate: new Date()}).then(response => {console.log(response)axios.get(config.endpoint, {headers: response.headers,responseType: 'stream'}).then(response => {console.log(response)response.data.pipe(fs.createWriteStream('./tree.jpg'))}).catch(error => {console.log(error)})})
Golang#
Similarly, let create a SignatureV4 using Go SDK
- Create a http request
- Sign the request
- Send the request to s3
Here is detail script
main.go
// https://github.com/bmizerany/aws4/blob/master/sign.go// https://github.com/mousedownmike/go-lambda-v4-signaturepackage mainimport ("context""fmt""io""log""net/http""os""time""github.com/aws/aws-sdk-go-v2/aws"v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4""github.com/aws/aws-sdk-go/aws/session")const imageUrl = "https://s3.ap-southeast-1.amazonaws.com/BUCKET-NAME/whale.jpg"func main() {ctx := context.TODO()request1, _ := http.NewRequest(http.MethodGet, imageUrl, nil)awsSess, err := session.NewSessionWithOptions(session.Options{SharedConfigState: session.SharedConfigEnable,})if err != nil {log.Fatalf("failed creating session: %s", err)}value, _ := awsSess.Config.Credentials.Get()credentials := &aws.Credentials{AccessKeyID: value.AccessKeyID,SecretAccessKey: value.SecretAccessKey,SessionToken: value.SessionToken,}signer := v4.NewSigner()error := signer.SignHTTP(ctx,*credentials,request1,"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",*aws.String("s3"),*aws.String("ap-southeast-1"),time.Now(),)// hot fix as x-amz-content-sha256 not inserted in the headerrequest1.Header.Set("X-Amz-Content-Sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")fmt.Println(request1.Header)if error != nil {log.Println(error)}client := &http.Client{}response, error := client.Do(request1)if error != nil {log.Println(error)}fmt.Println(response)bytes, error := io.ReadAll(response.Body)if (error != nil){fmt.Println(error)}f, _ := os.Create("hehe.jpg")_,errorx := f.Write(bytes)if (errorx != nil){fmt.Println(errorx)}_ = f.Close()}