Net-SAML2

 view release on metacpan or  search on metacpan

lib/Net/SAML2/Binding/Redirect.pm  view on Meta::CPAN

    elsif ($self->param eq 'SAMLResponse') {
        croak("Need to have a cert specified") unless $self->has_cert;
    }
}

# BUILDARGS

# Earlier versions expected the cert to be a string.  However, metadata
# can include multiple signing certificates so the $idp->cert is now
# expected to be an arrayref to the certificates.  To avoid breaking existing
# applications this changes the the cert to an arrayref if it is not
# already an array ref.

around BUILDARGS => sub {
    my $orig = shift;
    my $self = shift;

    my %params = @_;
    if ($params{cert} && ref($params{cert}) ne 'ARRAY') {
            $params{cert} = [$params{cert}];
    }

    return $self->$orig(%params);
};


sub get_redirect_uri {
    my $self    = shift;
    my $request = shift;

    if (!defined $request) {
        croak("Unable to create redirect URI without a request");
    }

    my $relaystate = shift;

    my $input  = "$request";
    my $output = '';

    rawdeflate \$input => \$output;
    my $req = encode_base64($output, '');

    my $uri = URI->new($self->url);
    $uri->query_param($self->param, $req);
    $uri->query_param('RelayState', $relaystate) if defined $relaystate;

    return $uri->as_string if $self->insecure;
    return $self->_sign_redirect_uri($uri);
}

sub _sign_redirect_uri {
    my $self = shift;
    my $uri  = shift;

    my $key_string = read_text($self->key);
    my $pk = Crypt::PK::RSA->new();
    my $rsa_priv = $pk->import_key(\$key_string);

    $uri->query_param('SigAlg',
        $self->sig_hash eq 'sha1'
        ? 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
        : 'http://www.w3.org/2001/04/xmldsig-more#rsa-' . $self->sig_hash);

    my $to_sign = $uri->query;
    my $sig = encode_base64($rsa_priv->sign_message($to_sign, uc($self->sig_hash), 'v1.5'), '');
    $uri->query_param('Signature', $sig);
    return $uri->as_string;
}


sub sign {
    my $self = shift;

    if ($self->insecure) {
        croak("Cannot sign an insecure request!");
    }

    return $self->get_redirect_uri(@_);
}


sub verify {
    my $self = shift;
    my $query_string = shift;

    $query_string =~ s#^.*\?##;

    my %params = map { split(/=/, $_, 2) } split(/&/, $query_string);

    my $sigalg = uri_unescape($params{SigAlg});

    my $encoded_sig = uri_unescape($params{Signature});
    my $sig = decode_base64($encoded_sig);

    my @signed_parts;
    for my $p ($self->param, qw(RelayState SigAlg)) {
        push @signed_parts, join('=', $p, $params{$p}) if exists $params{$p};
    }
    my $signed = join('&', @signed_parts);

    $self->_verify($sigalg, $signed, $sig);

    # unpack the SAML request
    my $deflated = decode_base64(uri_unescape($params{$self->param}));
    my $request = '';
    rawinflate \$deflated => \$request;

    # return the relaystate if it is defined
    return ($request, defined $params{RelayState} ?  uri_unescape($params{RelayState}) : undef);
}

sub _verify {
    my ($self, $sigalg, $signed, $sig) = @_;

    foreach my $crt (@{$self->cert}) {
        my $cert = Crypt::OpenSSL::X509->new_from_string($crt);
        my $pk = Crypt::PK::RSA->new();
        my $rsa_pub = $pk->import_key(\$cert->pubkey);

        my $hash_name;
        if ($sigalg eq 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256') {
            $hash_name = 'SHA256';
        } elsif ($sigalg eq 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha224') {
            $hash_name = 'SHA224';
        } elsif ($sigalg eq 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384') {
            $hash_name = 'SHA384';
        } elsif ($sigalg eq 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512') {
            $hash_name = 'SHA512';
        } elsif ($sigalg eq 'http://www.w3.org/2000/09/xmldsig#rsa-sha1') {
            $hash_name = 'SHA1';
        }
        else {
            warn "Unsupported Signature Algorithim: $sigalg, defaulting to sha256" if $self->debug;
        }

        return 1 if $rsa_pub->verify_message($sig, $signed, $hash_name, 'v1.5');

        warn "Unable to verify with " . $cert->subject if $self->debug;
    }

    croak("Unable to verify the XML signature");
}

__PACKAGE__->meta->make_immutable;

__END__

=pod

=encoding UTF-8

=head1 NAME

Net::SAML2::Binding::Redirect - HTTP Redirect binding for SAML

=head1 VERSION

version 0.85

=head1 SYNOPSIS

  my $redirect = Net::SAML2::Binding::Redirect->new(
    key     => '/path/to/SPsign-nopw-key.pem',		# Service Provider (SP) private key
    url     => $sso_url,							# Service Provider Single Sign Out URL
    param   => 'SAMLRequest' OR 'SAMLResponse',		# Type of request
    cert    => $idp->cert('signing')				# Identity Provider (IdP) certificate
    sig_hash => 'sha256', 'sha224', 'sha384', 'sha512' or 'sha1'  # Signature to sign request
  );

  my $url = $redirect->sign($authnreq);

  my $ret = $redirect->verify($url);

=head1 METHODS

=head2 new( ... )

Constructor. Creates an instance of the Redirect binding.

Arguments:

=over

=item B<key>

The SP's (Service Provider) also known as your application's signing key
that your application uses to sign the AuthnRequest.  Some IdPs may not
verify the signature.



( run in 0.685 second using v1.01-cache-2.11-cpan-ceb78f64989 )