AWS-Signature4
view release on metacpan or search on metacpan
lib/AWS/Signature4.pm view on Meta::CPAN
If a security token is provided, it overrides any values given for
-access_key or -secret_key.
If the environment variables EC2_ACCESS_KEY and/or EC2_SECRET_KEY are
set, their contents are used as defaults for -acccess_key and
-secret_key.
=cut
sub new {
my $self = shift;
my %args = @_;
my ($id,$secret,$token);
if (ref $args{-security_token} && $args{-security_token}->can('access_key_id')) {
$id = $args{-security_token}->accessKeyId;
$secret = $args{-security_token}->secretAccessKey;
}
$id ||= $args{-access_key} || $ENV{EC2_ACCESS_KEY}
lib/AWS/Signature4.pm view on Meta::CPAN
$secret ||= $args{-secret_key} || $ENV{EC2_SECRET_KEY}
or croak "Please provide -secret_key or define environment variable EC2_SECRET_KEY";
return bless {
access_key => $id,
secret_key => $secret,
(defined($args{-security_token}) ? (security_token => $args{-security_token}) : ()),
},ref $self || $self;
}
sub access_key { shift->{access_key } }
sub secret_key { shift->{secret_key } }
=item $signer->sign($request [,$region] [,$payload_sha256_hex])
Given an HTTP::Request object, add the headers required by AWS and
then sign it with a version 4 signature by adding an "Authorization"
header.
The request must include a URL from which the AWS endpoint and service
can be derived, such as "ec2.us-east-1.amazonaws.com." In some cases
(e.g. S3 bucket operations) the endpoint does not indicate the
lib/AWS/Signature4.pm view on Meta::CPAN
=item $url = $signer->signed_url($request)
This method will generate a signed GET URL for the request. The URL
will include everything needed to perform the request.
=back
=cut
sub sign {
my $self = shift;
my ($request,$region,$payload_sha256_hex) = @_;
$self->_add_date_header($request);
$self->_sign($request,$region,$payload_sha256_hex);
}
=item my $url $signer->signed_url($request_or_uri [,$expires])
Pass an HTTP::Request, a URI object, or just a plain URL string
containing the proper endpoint and parameters needed for an AWS REST
lib/AWS/Signature4.pm view on Meta::CPAN
URI object, which can be shared with non-AWS users for the purpose of,
e.g., accessing an object in a private S3 bucket.
Pass an optional $expires argument to indicate that the URL will only
be valid for a finite period of time. The value of the argument is in
seconds.
=cut
sub signed_url {
my $self = shift;
my ($arg1,$expires) = @_;
my ($request,$uri);
if (ref $arg1 && UNIVERSAL::isa($arg1,'HTTP::Request')) {
$request = $arg1;
$uri = $request->uri;
my $content = $request->content;
$uri->query($content) if $content;
lib/AWS/Signature4.pm view on Meta::CPAN
$self->_sign($request);
}
my ($algorithm,$credential,$signedheaders,$signature) =
$request->header('Authorization') =~ /^(\S+) Credential=(\S+), SignedHeaders=(\S+), Signature=(\S+)/;
$uri->query_param_append('X-Amz-Signature' => $signature);
return $uri;
}
sub _add_date_header {
my $self = shift;
my $request = shift;
my $datetime;
unless ($datetime = $request->header('x-amz-date')) {
$datetime = $self->_zulu_time($request);
$request->header('x-amz-date'=>$datetime);
}
}
sub _scope {
my $self = shift;
my ($request,$region) = @_;
my $host = $request->uri->host;
my $datetime = $self->_datetime($request);
my ($date) = $datetime =~ /^(\d+)T/;
my $service;
if ($host =~ /^([\w.-]+)\.s3\.amazonaws.com/) { # S3 bucket virtual host
$service = 's3';
$region ||= 'us-east-1';
} elsif ($host =~ /^[\w-]+\.s3-([\w-]+)\.amazonaws\.com/) {
lib/AWS/Signature4.pm view on Meta::CPAN
$region ||= $2;
} elsif ($host =~ /^([\w-]+)\.amazonaws\.com/) {
$service = $1;
$region = 'us-east-1';
}
$service ||= 's3';
$region ||= 'us-east-1'; # default
return "$date/$region/$service/aws4_request";
}
sub _parse_scope {
my $self = shift;
my $scope = shift;
return split '/',$scope;
}
sub _datetime {
my $self = shift;
my $request = shift;
return $request->header('x-amz-date') || $self->_zulu_time($request);
}
sub _algorithm { return 'AWS4-HMAC-SHA256' }
sub _sign {
my $self = shift;
my ($request,$region,$payload_sha256_hex) = @_;
return if $request->header('Authorization'); # don't overwrite
my $datetime = $self->_datetime($request);
unless ($request->header('host')) {
my $host = $request->uri->host;
$request->header(host=>$host);
}
lib/AWS/Signature4.pm view on Meta::CPAN
my $secret_key = $self->secret_key;
my $access_key = $self->access_key;
my $algorithm = $self->_algorithm;
my ($hashed_request,$signed_headers) = $self->_hash_canonical_request($request,$payload_sha256_hex);
my $string_to_sign = $self->_string_to_sign($datetime,$scope,$hashed_request);
my $signature = $self->_calculate_signature($secret_key,$service,$region,$date,$string_to_sign);
$request->header(Authorization => "$algorithm Credential=$access_key/$scope, SignedHeaders=$signed_headers, Signature=$signature");
}
sub _zulu_time {
my $self = shift;
my $request = shift;
my $date = $request->header('Date');
my @datetime = $date ? gmtime(str2time($date)) : gmtime();
return strftime('%Y%m%dT%H%M%SZ',@datetime);
}
sub _hash_canonical_request {
my $self = shift;
my ($request,$hashed_payload) = @_; # (HTTP::Request,sha256_hex($content))
my $method = $request->method;
my $uri = $request->uri;
my $path = $uri->path || '/';
my @params = $uri->query_form;
my $headers = $request->headers;
$hashed_payload ||= sha256_hex($request->content);
# canonicalize query string
lib/AWS/Signature4.pm view on Meta::CPAN
$canonical_headers .= "\n";
my $signed_headers = join ';',sort map {lc} keys %signed_fields;
my $canonical_request = join("\n",$method,$path,$canonical_query_string,
$canonical_headers,$signed_headers,$hashed_payload);
my $request_digest = sha256_hex($canonical_request);
return ($request_digest,$signed_headers);
}
sub _string_to_sign {
my $self = shift;
my ($datetime,$credential_scope,$hashed_request) = @_;
return join("\n",'AWS4-HMAC-SHA256',$datetime,$credential_scope,$hashed_request);
}
=item $signing_key = AWS::Signature4->signing_key($secret_access_key,$service_name,$region,$date)
Return just the signing key in the event you wish to roll your own signature.
=cut
sub signing_key {
my $self = shift;
my ($kSecret,$service,$region,$date) = @_;
my $kDate = hmac_sha256($date,'AWS4'.$kSecret);
my $kRegion = hmac_sha256($region,$kDate);
my $kService = hmac_sha256($service,$kRegion);
my $kSigning = hmac_sha256('aws4_request',$kService);
return $kSigning;
}
sub _calculate_signature {
my $self = shift;
my ($kSecret,$service,$region,$date,$string_to_sign) = @_;
my $kSigning = $self->signing_key($kSecret,$service,$region,$date);
return hmac_sha256_hex($string_to_sign,$kSigning);
}
1;
=head1 SEE ALSO
( run in 0.299 second using v1.01-cache-2.11-cpan-4d50c553e7e )