I figured it out. I was encoding the signature wrong, I had to sort my query strings, and the call back URL is not needed in this instance. Here is my working code:
sub Twitter {
my $IN = new CGI;
my $params = {
oauth_consumer_key => $consumer_key,
oauth_nonce => time,
oauth_signature_method => "HMAC-SHA1",
oauth_timestamp => time,
oauth_version => "1.0"
};
my $qs = build_sorted_query($params);
my $signing_key = $IN->escape($consumer_secret)."&";
my $signature_base = "POST&".$IN->escape($request_token_url)."&".$IN->escape($qs);
use Digest::HMAC_SHA1;
use MIME::Base64;
my $hmac = Digest::HMAC_SHA1->new($signing_key);
$hmac->add($signature_base);
$params->{oauth_signature} = $IN->escape(encode_base64($hmac->digest));
$qs = build_sorted_query($params);
use LWP;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(POST => $request_token_url);
$req->content_type('application/x-www-form-urlencoded');
$req->content($qs);
my $res = $ua->request($req);
# Check the outcome of the response
unless ($res->is_success) {
print $IN->header.$res->status_line, "\n";
print $res->content;
exit;
}
print $IN->header.$res->content;
return;
}
sub build_sorted_query {
my $input = shift;
my $qs;
foreach (sort keys %$input) {
$qs .= $_."=".$input->{$_}."&";
}
return substr ($qs, 0, -1);
}
Thanks for looking!