Skip to content

Instantly share code, notes, and snippets.

@borislav-angelov
Last active December 6, 2022 16:53
Show Gist options
  • Save borislav-angelov/b9ba5ce727d03f2be6ef6d9abf23667f to your computer and use it in GitHub Desktop.
Save borislav-angelov/b9ba5ce727d03f2be6ef6d9abf23667f to your computer and use it in GitHub Desktop.
<?php
class AWSV4 {
private $accessKeyID = null;
private $secretAccessKey = null;
private $regionName = null;
private $serviceName = null;
private $httpMethodName = null;
private $canonicalURI = "/";
private $queryParametes = array();
private $awsHeaders = array();
private $payload = "";
/* Other variables */
private $HMACAlgorithm = "AWS4-HMAC-SHA256";
private $aws4Request = "aws4_request";
private $strSignedHeader = null;
private $xAmzDate = null;
private $currentDate = null;
public function __construct() {
$this->accessKeyID = builder.accessKeyID;
$this->secretAccessKey = builder.secretAccessKey;
$this->regionName = builder.regionName;
$this->serviceName = builder.serviceName;
$this->httpMethodName = builder.httpMethodName;
$this->canonicalURI = builder.canonicalURI;
$this->queryParametes = builder.queryParametes;
$this->awsHeaders = builder.awsHeaders;
$this->payload = builder.payload;
/* Get current timestamp value.(UTC) */
$this->xAmzDate = $this->getTimeStamp();
$this->currentDate = $this->getDate();
}
/**
* Task 1: Create a Canonical Request for Signature Version 4.
*
* @return
*/
private function prepareCanonicalRequest() {
$canonicalURL = "";
/* Step 1.1 Start with the HTTP request method (GET, PUT, POST, etc.), followed by a newline character. */
$canonicalURL .= $this->httpMethodName . "\n";
/* Step 1.2 Add the canonical URI parameter, followed by a newline character. */
$canonicalURL .= $this->canonicalURI . "\n";
/* Step 1.3 Add the canonical query string, followed by a newline character. */
$canonicalURL .= http_build_query($this->queryParametes) . "\n";
/* Step 1.4 Add the canonical headers, followed by a newline character. */
$signedHeaders = '';
foreach ($this->awsHeaders as $key => $value) {
$signedHeaders .= $key . ";";
$canonicalURL .= $key . ":" . $value . "\n";
}
$canonicalURL .= "\n";
/* Step 1.5 Add the signed headers, followed by a newline character. */
$this->strSignedHeader = substr($signedHeaders, 0, -1);
$canonicalURL .= $this->strSignedHeader . "\n";
/* Step 1.6 Use a hash (digest) function like SHA256 to create a hashed value from the payload in the body of the HTTP or HTTPS. */
$canonicalURL .= $this->generateHex($this->payload);
return $canonicalURL;
}
/**
* Task 2: Create a String to Sign for Signature Version 4.
*
* @param canonicalURL
* @return
*/
private function prepareStringToSign($canonicalURL) {
$stringToSign = '';
/* Step 2.1 Start with the algorithm designation, followed by a newline character. */
$stringToSign .= $this->HMACAlgorithm . "\n";
/* Step 2.2 Append the request date value, followed by a newline character. */
$stringToSign .= $this->xAmzDate . "\n";
/* Step 2.3 Append the credential scope value, followed by a newline character. */
$stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n";
/* Step 2.4 Append the hash of the canonical request that you created in Task 1: Create a Canonical Request for Signature Version 4. */
$stringToSign .= $this->generateHex($canonicalURL);
return $stringToSign;
}
/**
* Task 3: Calculate the AWS Signature Version 4.
*
* @param stringToSign
* @return
*/
private function calculateSignature($stringToSign) {
/* Step 3.1 Derive your signing key */
$signatureKey = $this->getSignatureKey($this->secretAccessKey, $this->currentDate, $this->regionName, $this->serviceName);
/* Step 3.2 Calculate the signature. */
$signature = hash_hmac("sha256", $stringToSign, $signatureKey, true);
/* Step 3.2.1 Encode signature (byte[]) to Hex */
$strHexSignature = strtolower(bin2hex($signature));
return $strHexSignature;
}
/**
* Task 4: Add the Signing Information to the Request. We'll return Map of
* all headers put this headers in your request.
*
* @return
*/
public function getHeaders() {
$this->awsHeaders['x-amz-date'] = $this->xAmzDate;
/* Execute Task 1: Create a Canonical Request for Signature Version 4. */
$canonicalURL = $this->prepareCanonicalRequest();
/* Execute Task 2: Create a String to Sign for Signature Version 4. */
$stringToSign = $this->prepareStringToSign($canonicalURL);
/* Execute Task 3: Calculate the AWS Signature Version 4. */
$signature = $this->calculateSignature($stringToSign);
if ($signature) {
return array(
'x-amz-date' => $this->xAmzDate,
'Authorization' => $this->buildAuthorizationString($signature),
);
}
}
/**
* Build string for Authorization header.
*
* @param strSignature
* @return
*/
private function buildAuthorizationString($strSignature) {
return $this->HMACAlgorithm . " "
. "Credential=" . $this->accessKeyID . "/" . $this->getDate() . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . ","
. "SignedHeaders=" . $this->strSignedHeader . ","
. "Signature=" . $strSignature;
}
/**
* Generate Hex code of String.
*
* @param data
* @return
*/
private function generateHex($data) {
return strtolower(bin2hex(hash("sha256", $data, true)));
}
/**
* Generate AWS signature key.
*
* @param key
* @param date
* @param regionName
* @param serviceName
* @return
* @throws Exception
* @reference
* http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java
*/
private function getSignatureKey($key, $date, $regionName, $serviceName) {
$kSecret = "AWS4" . $key;
$kDate = hash_hmac("sha256", $date, $kSecret, true);
$kRegion = hash_hmac("sha256", $regionName, $kDate, true);
$kService = hash_hmac("sha256", $serviceName, $kRegion, true);
$kSigning = hash_hmac("sha256", $this->aws4Request, $kService, true);
return $kSigning;
}
/**
* Get timestamp. yyyyMMdd'T'HHmmss'Z'
*
* @return
*/
private function getTimeStamp() {
return gmdate("Ymd\THis\Z");
}
/**
* Get date. yyyyMMdd
*
* @return
*/
private function getDate() {
return gmdate("Ymd");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment