Amazon-Signature4-Lite
view release on metacpan or search on metacpan
lib/Amazon/Signature4/Lite.pm view on Meta::CPAN
# canonical headers - must include host and x-amz-date at minimum
my %sign_headers = (
%{$headers},
'host' => $host,
'x-amz-date' => $datetime,
'x-amz-content-sha256' => $payload_hash,
);
$sign_headers{'x-amz-security-token'} = $self->{session_token}
if $self->{session_token};
# sort and build canonical headers string
my @header_keys = sort { lc($a) cmp lc($b) } keys %sign_headers;
my $canon_headers = join q{}, map { lc($_) . ':' . $sign_headers{$_} . "\n" } @header_keys;
my $signed_headers = join ';', map { lc($_) } @header_keys;
# canonical query string - sort by encoded key then encoded value
my $canon_query = q{};
if ($query) {
my @pairs = map {
join '=', map { uri_escape_utf8($_) } split /=/xsm, $_, 2
}
sort { $a cmp $b }
split /&/xsm, $query;
$canon_query = join '&', @pairs;
}
# canonical request
my $canon_request = join "\n", $method, _encode_path($path), $canon_query, $canon_headers, $signed_headers, $payload_hash;
# credential scope
my $service = $self->{service};
my $region = $self->{region};
my $scope = "$date/$region/$service/aws4_request";
# string to sign
my $string_to_sign = join "\n", 'AWS4-HMAC-SHA256', $datetime, $scope, sha256_hex($canon_request);
# signing key - HMAC chain
my $signing_key = hmac_sha256( 'aws4_request',
hmac_sha256( $service, hmac_sha256( $region, hmac_sha256( $date, "AWS4${\$self->{secret_key}}" ) ) ) );
# signature
my $signature = hmac_sha256_hex( $string_to_sign, $signing_key );
# authorization header
my $authorization = sprintf
'AWS4-HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s',
$self->{access_key}, $scope, $signed_headers, $signature;
# return merged headers ready for HTTP::Tiny
return { %sign_headers, 'Authorization' => $authorization, };
}
########################################################################
sub _encode_path {
########################################################################
my ($path) = @_;
# encode each segment individually, preserving slashes
return join '/', map { uri_escape_utf8($_) } split m{/}xsm, $path, -1;
}
1;
__END__
=head1 NAME
Amazon::Signature4::Lite - Lightweight AWS Signature Version 4 signing
=head1 SYNOPSIS
use Amazon::Signature4::Lite;
my $signer = Amazon::Signature4::Lite->new(
access_key => $access_key_id,
secret_key => $secret_access_key,
session_token => $session_token, # optional, for STS/IAM roles
region => 'us-east-1',
service => 's3', # default
);
my $signed = $signer->sign(
method => 'PUT',
url => 'https://s3.amazonaws.com/my-bucket/my-key',
headers => { 'Content-Type' => 'application/gzip' },
payload => $content,
);
# $signed is a hashref of headers ready for HTTP::Tiny:
# Authorization, x-amz-date, x-amz-content-sha256,
# x-amz-security-token (if session_token provided), host
=head1 DESCRIPTION
A minimal, dependency-free AWS Signature Version 4 implementation for
signing S3 and other AWS API requests. Unlike L<AWS::Signature4>, this
module does not depend on L<LWP> or L<HTTP::Request> - it works
directly with the plain scalars and hashrefs that L<HTTP::Tiny> uses.
=head1 METHODS
=head2 new(%args)
my $signer = Amazon::Signature4::Lite->new(
access_key => $key,
secret_key => $secret,
region => 'us-east-1',
);
Required: C<access_key>, C<secret_key>, C<region>.
Optional: C<session_token> (for temporary credentials), C<service>
(defaults to C<s3>).
=head2 sign(%args)
my $headers = $signer->sign(
method => 'GET',
url => $url,
headers => \%extra_headers,
( run in 0.718 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )