Skip to content

Instantly share code, notes, and snippets.

@foreseaz
Last active March 20, 2023 07:12
Show Gist options
  • Save foreseaz/0da077d6f2b2ac75cfc23946be2c470d to your computer and use it in GitHub Desktop.
Save foreseaz/0da077d6f2b2ac75cfc23946be2c470d to your computer and use it in GitHub Desktop.
Cloudflare R2 request with signature
var crypto = require("crypto-js");
var https = require("https");
var xml = require("xml2js");
main();
// split the code into a main function
function main() {
// this serviceList is unused right now, but may be used in future
const serviceList = ["dynamodb", "ec2", "sqs", "sns", "s3"];
// our variables
var access_key = "ACCESS_KEY";
var secret_key = "SECRET_KEY";
var region = "auto";
var url = "{ACCOUNT_ID}.r2.cloudflarestorage.com";
var myService = "s3";
var myMethod = "GET";
var myPath = "/";
// get the various date formats needed to form our request
var amzDate = getAmzDate(new Date().toISOString());
var authDate = amzDate.split("T")[0];
// we have an empty payload here because it is a GET request
var payload = "";
// get the SHA256 hash value for our payload
var hashedPayload = crypto.SHA256(payload).toString();
// create our canonical request
var canonicalReq =
myMethod +
"\n" +
myPath +
"\n" +
"\n" +
"host:" +
url +
"\n" +
"x-amz-content-sha256:" +
hashedPayload +
"\n" +
"x-amz-date:" +
amzDate +
"\n" +
"\n" +
"host;x-amz-content-sha256;x-amz-date" +
"\n" +
hashedPayload;
// hash the canonical request
var canonicalReqHash = crypto.SHA256(canonicalReq).toString();
// form our String-to-Sign
var stringToSign =
"AWS4-HMAC-SHA256\n" +
amzDate +
"\n" +
authDate +
"/" +
region +
"/" +
myService +
"/aws4_request\n" +
canonicalReqHash;
// get our Signing Key
var signingKey = getSignatureKey(
crypto,
secret_key,
authDate,
region,
myService
);
// Sign our String-to-Sign with our Signing Key
var authKey = crypto.HmacSHA256(stringToSign, signingKey);
// Form our authorization header
var authString =
"AWS4-HMAC-SHA256 " +
"Credential=" +
access_key +
"/" +
authDate +
"/" +
region +
"/" +
myService +
"/aws4_request," +
"SignedHeaders=host;x-amz-content-sha256;x-amz-date," +
"Signature=" +
authKey;
// throw our headers together
headers = {
Authorization: authString,
Host: url,
"x-amz-date": amzDate,
"x-amz-content-sha256": hashedPayload,
};
// call our function
performRequest(url, headers, payload, function (response) {
// parse the response from our function and write the results to the console
xml.parseString(response, function (err, result) {
console.log("=== \n", JSON.stringify(result, null, 2));
console.log("=== \n" + "Contents: ");
for (i = 0; i < result["ListAllMyBucketsResult"]["Buckets"].length; i++) {
console.log(
"=== \n" +
"Name: " +
result["ListAllMyBucketsResult"]["Buckets"][i]["Bucket"][0]["Name"][0] +
"\n" +
"CreationDate: " +
result["ListAllMyBucketsResult"]["Buckets"][i]["Bucket"][0]["CreationDate"][0]
);
}
console.log("=== \n");
});
});
}
// this function gets the Signature Key, see AWS documentation for more details, this was taken from the AWS samples site
function getSignatureKey(Crypto, key, dateStamp, regionName, serviceName) {
var kDate = Crypto.HmacSHA256(dateStamp, "AWS4" + key);
var kRegion = Crypto.HmacSHA256(regionName, kDate);
var kService = Crypto.HmacSHA256(serviceName, kRegion);
var kSigning = Crypto.HmacSHA256("aws4_request", kService);
return kSigning;
}
// this function converts the generic JS ISO8601 date format to the specific format the AWS API wants
function getAmzDate(dateStr) {
var chars = [":", "-"];
for (var i = 0; i < chars.length; i++) {
while (dateStr.indexOf(chars[i]) != -1) {
dateStr = dateStr.replace(chars[i], "");
}
}
dateStr = dateStr.split(".")[0] + "Z";
return dateStr;
}
// the REST API call using the Node.js 'https' module
function performRequest(endpoint, headers, data, success) {
var dataString = data;
var options = {
host: endpoint,
port: 443,
path: "/",
method: "GET",
headers: headers,
};
var req = https.request(options, function (res) {
res.setEncoding("utf-8");
var responseString = "";
res.on("data", function (data) {
responseString += data;
});
res.on("end", function () {
//console.log(responseString);
success(responseString);
});
});
req.write(dataString);
req.end();
}
use sha2::{Sha256, Digest};
const S3_ACCESS: &str = "ACCESS";
const S3_SECRET: &str = "SECRET";
fn sha256_string(input: String) -> String {
let mut hasher = Sha256::new();
hasher.update(input);
let result = hasher.finalize();
format!("{:x}", result)
}
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let datetime = chrono::Utc::now();
let host = "{ACCOUNT_ID}.r2.cloudflarestorage.com";
let url = format!("https://{}", host);
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
"X-Amz-Date",
datetime
.format("%Y%m%dT%H%M%SZ")
.to_string()
.parse()
.unwrap(),
);
headers.insert("host", host.parse().unwrap());
let payload = "".to_string();
let hashed_payload = sha256_string(payload);
headers.insert("x-amz-content-sha256", hashed_payload.parse().unwrap());
let s = aws_sign_v4::AwsSign::new(
"GET",
&url,
&datetime,
&headers,
"auto",
&S3_ACCESS,
&S3_SECRET,
"s3",
""
);
let signature = s.sign();
println!("{:#?}", signature);
headers.insert(reqwest::header::AUTHORIZATION, signature.parse().unwrap());
let client = reqwest::Client::new();
let res = client
.get(url)
.headers(headers.to_owned())
.body("")
.send()
.await?;
println!("Status: {}", res.status());
let body = res.text().await?;
println!("Body:\n\n{}", body);
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment