S3 signature mismatch for presigned URL - perl

I'm trying to create a pre signed URL for Amazon S3.
The below code generates the same signature as the signature in the example in the S3 docs.
(https://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html)
However, when I plug in my own credentials etc, I get an error from AWS:
"The request signature we calculated does not match the signature you provided. Check your key and signing method."
Given the code below seems to work, I presume my signing method is ok. Also, the credentials used are the ones
used to upload the file, so they are good too. They are root credentials, not an IAM user.
I have tried:
1) A file name with and without a period.
2) A file in US standard as well as Ireland.
3) HTTP and HTTPS
4) SHA256 (although this breaks the example in the docs.)
5) Different web clients: wget, curl and Firefox.
I seem to be doing exactly the same as the php given here:
Creating a signed S3 URL with Javascript
This is the URL generated for the US standard. Bucket is "buck1bar", file is "foo.txt"
https://s3.amazonaws.com/buck1bar/foo.txt?AWSAccessKeyId=AKIAJG5VR56AB5USIG5Q&Expires=1467893299&Signature=emdimX%2FWKYrcdhtPtE7eQ9ErWv30HNlAHaa9vlDODCY%3D
So, what am I doing wrong?
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use MIME::Base64;
use Digest::HMAC;
use Digest::SHA;
use URI::Escape;
my $hmac3 = Digest::HMAC->new( 'OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV', 'Digest::SHA');
$hmac3->add("GET\n\n\n1141889120\n/quotes/nelson");
my $signature = uri_escape( encode_base64 $hmac3->digest, '');
print "vjbyPxybdZaNmGa%2ByT272YEAiv4%3D\n$signature\n";
# vjbyPxybdZaNmGa%2ByT272YEAiv4%3D
# vjbyPxybdZaNmGa%2ByT272YEAiv4%3D

After further hackery, it turns out that the module I was using already does it.
my $client = Net::Amazon::S3::Client->new( s3 => $s3 );
my $bucket = $client->bucket( name => $bucket_name );
my $o = $bucket->object( key => 'key1', expires => '2016-07-08' );
my $uri = $o->query_string_authentication_uri();
print "$uri\n";

Related

OTRS getting internal server error when creating ticket

I'm having a problem with creating a ticket with the generic interface. I'm using Perl via a SOAP api. I created a webservice with the GenericTicketConnector.yml.
I looked at the debugger of the webservice and the only data what is not provided is that of TicketCreate.
I uploaded the script to the remote (Unix) where the files are stored with the use of WinSCP and i run the script with the use of Putty SSH connection.
Error message: 500 Internal Server Error
# this is the URL for the web service
# the format is
# <HTTP_TYPE>:://<OTRS_FQDN>/nph-genericinterface.pl/Webservice/<WEB_SERVICE_NAME>
# or
# <HTTP_TYPE>:://<OTRS_FQDN>/nph-genericinterface.pl/WebserviceID/<WEB_SERVICE_ID>
my $URL = 'http://localhost/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnector';
# this name space should match the specified name space in the SOAP transport for the web service
my $NameSpace = 'http://www.otrs.org/TicketConnector/';
# this is operation to execute, it could be TicketCreate, TicketUpdate, TicketGet, TicketSearch
# or SessionCreate. and they must to be defined in the web service.
my $Operation = 'TicketCreate';
# this variable is used to store all the parameters to be included on a request in XML format, each
# operation has a determined set of mandatory and non mandatory parameters to work correctly, please
# check OTRS Admin Manual in order to get the complete list
my $XMLData = '
<UserLogin>pp</UserLogin>
<Password>********</Password>
<Ticket>
<Title>some title</Title>
<CustomerUser>Jan2804</CustomerUser>
<Queue>raw</Queue>
<State>open</State>
<Type>Incident</Type>
<Priority>3 normal</Priority>
</Ticket>
<Article>
<Subject>some subject</Subject>
<Body>some body</Body>
<ContentType>text/plain; charset=utf8</ContentType>
</Article>
';
# create a SOAP::Lite data structure from the provided XML data structure
my $SOAPData = SOAP::Data
->type( 'xml' => $XMLData );
my $SOAPObject = SOAP::Lite->uri($NameSpace)->proxy($URL)
->$Operation($SOAPData);
# check for a fault in the soap code
if ( $SOAPObject->fault() ) {
print $SOAPObject->faultcode(), " ", $SOAPObject->faultstring(), "\n";
}
# otherwise print the results
else {
# get the XML response part from the SOAP message
my $XMLResponse = $SOAPObject->context()->transport()->proxy()->http_response()->content();
# deserialize response (convert it into a perl structure)
my $Deserialized = eval {
SOAP::Deserializer->deserialize($XMLResponse);
};
# remove all the headers and other not needed parts of the SOAP message
my $Body = $Deserialized->body();
# just output relevant data and no the operation name key (like TicketCreateResponse)
for my $ResponseKey ( sort keys %{$Body} ) {
print Dumper( $Body->{$ResponseKey} ); ## no critic
}
}
Try to open the webservice in your browser. Is it working?
I recently did it for myself. I used the REST yml file. I also called my webservice GenericTicketConnectorREST. But you should also get some answer aswell
Following URI is accessable for me via browser
http://otrs-test.company.local/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/Ticket?UserLogin=username&Password=testtesttest

Server does not have Digest::SHA 'hmac_sha256_base64' installed, can I use Digest::HMAC_SHA1 'hmac_sha1' instead?

I am preparing a CGI script that needs to confirm that the body of a incoming post request message, when is converted into a HMAC-SHA256 hash, is exactly the same content that also comes in a header tag of the same incoming message.
I have been able to confirm using Python that the procedure is as explained above, but when I do the same functionality on a CGI script I cannot match the contents and probably is because I am not using the correct encryption / hashing library.
My server provider does not have the Digest::SHA library and thus, I can not use the 'hmac_sha256_base64' function. I cannot ask them to install it, I just can use what is already available.
I have checked the available libraries and there is a Digest::HMAC_SHA1 'hmac_sha1' library / function. So I am doing as follows:
my $q = CGI->new;
my %headers = map { $_ => $q->http($_) } $q->http();
# below is the secret key, is an example but I am using the good one
my $channel_secret="abcdabcdabcdabcdabcdabcdabcdabcd"
# Incoming request body string
my $httpRequestBody = $q->param( 'POSTDATA' );
# now, I want to use Digest::SHA hmac_sha256_base64 but this server
# does not have it so I am using the following one...
# because I thought it was the equivalent new function to do the same
# but probably it is not...
use Digest::HMAC_SHA1 'hmac_sha1';
use MIME::Base64 'encode_base64';
$digest = hmac_sha1($httpRequestBody, $channel_secret);
my $signature = encode_base64($digest);
So basically I expect that these two variables contain the same string:
$headers{'A_EXISTING_TAG_OF_THE_HEADER'}
$signature
But they are totally different. I suspect that I am not using the correct algorithm.
So my question is:
If my server provider does not include Digest::SHA 'hmac_sha256_base64' in the available libraries, then what other alternatives do I have to make the same? Is Digest::HMAC_SHA1 'hmac_sha1' the same functionality or not?
Download the tarball for Digest::SHA::PurePerl ( you'll find the download link on this page https://metacpan.org/pod/Digest::SHA::PurePerl )
Create a library folder, something like this
.
|-- library
| `-- Digest
| `-- SHA
| `-- PurePerl.pm
`-- your_script.pl
your_script.pl looks like this, you can implement similarly:
#!/usr/bin/perl
use lib '.';
use lib '/tmp/iadvd/library/';
use Digest::SHA::PurePerl qw(sha1 sha1_hex);
print sha1_hex('Pradeep'),"\n";

Certificates issue with a perl script

I need to use an url to connect with OSTR to create a ticket via webservice, but I have a problem with certificates.
The error (picture here):
500 Can't connect to (certificate verify failed)
What I've tried
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
my $UA = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0, } );
I've also tried to add a new certificate in C:\otrs\Kernel\cpan-lib\Mozilla\CA\cacert.pem.
CODE
Code (Picture here)
Well, please try this code and add/remove the line which sets the env variable, for me this just works.
use strict;
use warnings;
use SOAP::Lite;
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
my $URL = 'https://self-signed.badssl.com/foo';
my $NameSpace = 'https://self-signed.badssl.com/';
my $Operation = 'TicketCreate';
my $XMLData = <<'XML';
<UserLogin>login</UserLogin>
<Password>pass</Password>
<Ticket>
<Title>Title</Title>
</Ticket>
XML
my $SOAPObject = SOAP::Lite->uri($NameSpace)->proxy($URL)->$Operation($XMLData);
Without the $ENV{} line it gives 500 Can't connect ... (certificate verify failed) and with it it gives 405 Not Allowed (since the test URL does not actually run the SOAP web service).
Also, please place actual code snippets on SO, in text and not in a screenshot, and please try and keep them short.
Oh, and by the way, I wrote a small script to create tickets in OTRS using the web service, it's here, maybe it's useful for you -> https://github.com/mbeijen/App-OTRS-CreateTicket

Perl LWP::Simple::get($url) does not work for some urls

I am using LWP::Simple::get($url) library to access data from web pages. The problem is that the get function is not working for the below url.
Below is the code snippet:
#!/usr/bin/perl
use LWP::Simple;
use JSON;
use Data::Dumper;
my $url = "https://www.cryptopia.co.nz/api/GetCurrencies";
my $json = get( $url);
die "Could not get $url!" unless defined $json;
my $decoded_json = decode_json($json);
print Dumper($decoded_json);
After running this code it gives the below error:
Could not get https://www.cryptopia.co.nz/api/GetCurrencies!
When I replace the url with : $url = "https://api.coinmarketcap.com/v1/ticker/" it works fine.
Please can you tell me what is the root cause and how I can fix it.
Also the url mentioned in the code snippet worked once and now suddenly it does not work.
This is essentially the same question as this one, only for Perl LWP instead of NodeJS.
The problem is that the target site has a broken setup and is missing an intermediate certificate. This can be seen from the SSLabs report:
This server's certificate chain is incomplete. Grade capped to B.
To work around this you have two secure and one insecure option. The insecure but, unfortunately, often proposed option is to disable all certificate validation. Don't do this because it makes your application vulnerable against man-in-the-middle attacks.
The next option is to have your own trust store which includes all necessary certificates, i.e. both the root CA and also the missing intermediate CA:
use strict;
use warnings;
use LWP::UserAgent;
use IO::Socket::SSL;
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_ca_file => 'myca.pem',
# disable OCSP stapling since it results in problems with this site
SSL_ocsp_mode => SSL_OCSP_NO_STAPLE
);
my $resp = $ua->get('https://www.cryptopia.co.nz/api/GetCurrencies');
print $resp->decoded_content;
myca.pem is in this case the concatenation of the PEM representation for the certificates of the intermediate "COMODO RSA Extended Validation Secure Server CA" and the root "COMODO RSA Certification Authority". I've provided it at Pastebin, here.
The third option is to trust this specific leaf certificate only. When used this will trust a server using this certificate, no matter if the certificate is expired, revoked, the hostname does not match the URL or (as in this case) the trust chain cannot be built because of a missing intermediate certificate:
use strict;
use warnings;
use LWP::UserAgent;
use IO::Socket::SSL;
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_fingerprint => 'sha256$70bca153ac950b8fa92d20f04dceca929852c42dc1d51bdc3c290df256ae05d3',
SSL_ocsp_mode => SSL_OCSP_NO_STAPLE,
);
my $resp = $ua->get('https://www.cryptopia.co.nz/api/GetCurrencies');
print $resp->decoded_content;
The fingerprint you see here is the one you can also see in the browser when looking at the certificate.

Why can't I connect to my CAS server with Perl's AuthCAS?

I'm attempting to use an existing CAS server to authenticate login for a Perl CGI web script and am using the AuthCAS Perl module (v 1.3.1). I can connect to the CAS server to get the service ticket but when I try to connect to validate the ticket my script returns with the following error from the IO::Socket::SSL module:
500 Can't connect to [CAS Server]:443 (Bad hostname '[CAS Server]')
([CAS Server] substituted for real server name)
Symptoms/Tests:
If I type the generated URL for the authentication into the web browser's location bar it returns just fine with the expected XML snippet. So it is not a bad host name.
If I generate a script without using the AuthCAS module but using the IO::Socket::SSL module directly to query the CAS server for validation on the generated service ticket the Perl script will run fine from the command line but not in the browser.
If I add the AuthCAS module into the script in item 2, the script no longer works on the command line and still doesn't work in the browser.
Here is the bare-bones script that produces the error:
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use AuthCAS;
use CGI::Carp qw( fatalsToBrowser );
my $id = $ENV{QUERY_STRING};
my $q = new CGI;
my $target = "http://localhost/cgi-bin/testCAS.cgi";
my $cas = new AuthCAS(casUrl => 'https://cas_server/cas');
if ($id eq ""){
my $login_url = $cas->getServerLoginURL($target);
printf "Location: $login_url\n\n";
exit 0;
} else {
print $q->header();
print "CAS TEST<br>\n";
## When coming back from the CAS server a ticket is provided in the QUERY_STRING
print "QUERY_STRING = " . $id . "</br>\n";
## $ST should contain the received Service Ticket
my $ST = $q->param('ticket');
my $user = $cas->validateST($target, $ST); #### This is what fails
printf "Error: %s\n", &AuthCAS::get_errors() unless (defined $user);
}
Any ideas on where the conflict might be?
The error is coming from the line directly above the snippet Cebjyre quoted namely
$ssl_socket = new IO::Socket::SSL(%ssl_options);
namely the socket creation. All of the input parameters are correct. I had edited the module to put in debug statements and print out all the parameters just before that call and they are all fine. Looks like I'm going to have to dive deeper into the IO::Socket::SSL module.
As usually happens when I post questions like this, I found the problem. It turns out the Crypt::SSLeay module was not installed or at least not up to date. Of course the error messages didn't give me any clues. Updating it and all the problems go away and things are working fine now.
Well, from the module source it looks like that IO::Socket error is coming from get_https2
[...]
unless ($ssl_socket) {
$errors = sprintf "error %s unable to connect https://%s:%s/\n",&IO::Socket::SSL::errstr,$host,$port;
return undef;
}
[...]
which is called by callCAS, which is called by validateST.
One option is to temporarily edit the module file to put some debug statements in if you can, but if I had to guess, I'd say the casUrl you are supplying isn't matching up to the _parse_url regex properly - maybe you have three slashes after the https?