XML-Sig-OO

 view release on metacpan or  search on metacpan

lib/XML/Sig/OO.pm  view on Meta::CPAN

=head1 NAME

XML::Sig::OO - Modern XML Signatured validation

=head1 SYNOPSIS

  use XML::Sig::OO;

  # Sign our xml
  my $s=new XML::Sig::OO(
    xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>',
    key_file=>'rsa_key.pem'
    cert_file=>'cert.pem',
  );
  my $result=$s->sign;
  die "Failed to sign the xml, error was: $result" unless $result;

  my $xml=$result->get_data;
  # Example checking a signature
  my $v=new XML::Sig::OO(xml=>$xml);

  # validate our xml
  my $result=$v->validate;

  if($result) {
    print "everything checks out!\n";
  } else {
    foreach my $chunk (@{$result->get_data}) {
      my ($nth,$signature,$digest)=@{$chunk}{qw(nth signature digest)};

      print "Results for processing chunk $nth\n";
      print "Signature State: ".($signature ? "OK\n" : "Failed, error was $signature\n");
      print "Digest State: ".($digest ? "OK\n" : "Failed, error was $digest\n");
    }
  }

=head1 DESCRIPTION

L<XML::Sig::OO> is a project to create a stand alone perl module that does a good job creating and validating xml signatures.  At its core  This module is written around libxml2 better known as L<XML::LibXML>.

=head1 Multiple signatures and keys

In the case of signing multiple //@ID elements, it is possible to sign each chunk with a different key, in fact you can even use completly different key types.

  use Modern::Perl;
  use XML::Sig::OO;
  use File::Spec;
  use FindBin qw($Bin);
  use Crypt::OpenSSL::DSA;
  use Crypt::OpenSSL::RSA;

  # create our signign object
  my $s=new XML::Sig::OO(
    xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>',
  );

  my $x=$s->build_xpath;

  # sign our first xml chunk with our rsa key!
  my $rsa_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_key.pem'))->getlines;
  my $rsa=Crypt::OpenSSL::RSA->new_private_key($rsa_str);
  $rsa->use_pkcs1_padding();
  my $cert_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_cert.pem'))->getlines;
  $s->sign_cert($rsa);
  $s->key_type('rsa');
  $s->cert_string($cert_str);
  my $result=$s->sign_chunk($x,1);
  die $result unless $result;

  # Sign our 2nd chunk with our dsa key
  my $dsa = Crypt::OpenSSL::DSA->read_priv_key(File::Spec->catfile($Bin,'dsa_priv.pem'));
  $s->cert_string(undef);
  $s->sign_cert($dsa);
  $s->key_type('dsa');
  $result=$s->sign_chunk($x,2);
  die $result unless $result;

  my ($node)=$x->findnodes($s->xpath_Root);
  my $xml=$node->toString;

  print "Our Signed XML IS: \n",$xml,"\n";
  # Example checking a signature
  my $v=new XML::Sig::OO(xml=>$xml);

  $result=$v->validate;
  die $result unless $result;

  print "Our signed and xml passes validation\n";

=head2 Working with Net::SAML2

L<Net::SAML2> has many problems when it comes to signature validation of xml strings.  This section documents how to use this module in place of the Net::SAML2 built ins.

  use Net::SAML2::Protocol::Assertion;
  use XML::Sig::OO;
  use MIME::Base64;

  # Lets assume we have a post binding response
  my $saml_response=.....

  my $xml=decode_base64($saml_response);

  my $v=XML::Sig::OO->new(xml=>$xml,cacert=>'idp_cert.pem');
  my $result=$v->validate;
  die $result unless $result;

  # we can now use the asertion knowing it was from our idp
  my $assertion=Net::SAML2::Protocol::Assertion->new_from_xml(xml=>$xml)

=head2 Encrypted keys

Although this package does not directly support encrypted keys, it is possible to use encrypted keys by loading and exporting them with the L<Crypt::PK::RSA> and L<Crypt::PK::DSA> packages.

=head1 Constructor options

=cut

=over 4

=item * xml=>'...'

lib/XML/Sig/OO.pm  view on Meta::CPAN


=head2 my $result=$self->load_cert_from_file($filename)

Returns a Data::Result structure, when true it contains a hasref with the following elements:

  type: 'dsa|rsa|x509'
  cert: $cert_object

=cut

sub load_cert_from_file {
  my ($self,$file)=@_;
  return new_false Data::Result("file is not defined") unless defined($file);
  return new_false Data::Result("cannot read: $file") unless -r $file;

  my $io=IO::File->new($file,'r');
  return new_false Data::Result("Cannot open $file, error was $!") unless $io;
  my $text=join '',$io->getlines;
  return $self->detect_cert($text);
}

=head2 my $result=$self->detect_cert($text)

Returns a Data::Result object, when true it contains the following hashref

  type: 'dsa|rsa|x509'
  cert: $cert_object

=cut

sub detect_cert {
  my ($self,$text)=@_;
  if ($text =~ m/BEGIN ([DR]SA) PRIVATE KEY/s ) {

    if($1 eq 'RSA') {
      return $self->load_rsa_string($text);
    } else {
      return $self->load_dsa_string($text);
    }

  } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) {
    return $self->load_rsa_string($text); 
  } elsif ($text =~ m/BEGIN CERTIFICATE/) {
    return $self->load_x509_string($text);
  } else {
    return new_false Data::Result("Unsupported key type");
  }
}

=head2 my $result=$self->load_rsa_string($string)

Returns a Data::Result object, when true it contains the following hashref:

  type: 'rsa'
  cert: $cert_object

=cut

sub load_rsa_string {
  my ($self,$str)=@_;
  my $rsaKey = Crypt::OpenSSL::RSA->new_private_key( $str );
  return new_false Data::Result("Failed to parse rsa key") unless $rsaKey;
  #$rsaKey->use_pkcs1_padding();
  return new_true Data::Result({cert=>$rsaKey,type=>'rsa'});
}

=head2 my $result=$self->load_x509_string($string)

Returns a Data::Result object, when true it contains the following hashref:

  type: 'x509'
  cert: $cert_object

=cut

sub load_x509_string {
  my ($self,$str)=@_;
  my $x509Key = Crypt::OpenSSL::X509->new_from_string( $str );
  return new_false Data::Result("Failed to parse x509 cert") unless $x509Key;
  return new_true Data::Result({cert=>$x509Key,type=>'x509'});
}

=head2 my $result=$self->load_dsa_string($string)

Returns a Data::Result object, when true it contains the following hashref:

  type: 'dsa'
  cert: $cert_object

=cut

sub load_dsa_string {
  my ($self,$str)=@_;
  my $dsa_key = Crypt::OpenSSL::DSA->read_priv_key_str( $str );
  return new_false("Failed to parse dsa key") unless $dsa_key;
  return new_true Data::Result({cert=>$dsa_key,type=>'dsa'});
}

=head2 my $result=$self->get_xml_to_sign($xpath_object,$nth)

Returns a Data::Result object, when true it contains the xml object to sign.

=cut

sub get_xml_to_sign {
  my ($self,$x,$nth)=@_;
  my $xpath=$self->context($self->xpath_ToSign,$nth);
  my ($node)=$x->findnodes($xpath);

  return new_false Data::Result("Failed to find xml to sign in xpath: $xpath") unless defined($node);
  return new_true Data::Result($node);
}

=head2 my $result=$self->get_signer_id($xpath_object,$nth)

Returns a Data::Result object, when true it contains the id value

=cut

sub get_signer_id {
  my ($self,$x,$nth)=@_;



( run in 2.734 seconds using v1.01-cache-2.11-cpan-5837b0d9d2c )