Last active
March 20, 2023 07:12
-
-
Save foreseaz/0da077d6f2b2ac75cfc23946be2c470d to your computer and use it in GitHub Desktop.
Cloudflare R2 request with signature
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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