in reply to Validating XML Signatures / SSL Certificate question (using Net::SAML)

they say the whole point is I need to do whatever it is I need to do with it?! with my own key - presumably my private key - in order to validate the signature.

That doesn't sound right to me. The way PKI works in general is that you verify a signature against the public key of the signer and that is, as you hinted, the certificate (which is in essence a public key) which is transmitted as part of the SAML assertion. You should not have sent the signer your public key for them to use in signing because it will be useless to them. The signer signs with their private key and the recipient validates it with the signer's public key.

Top tip number 1: use the XML::Sig that comes with Net::SAML2. It is in better shape than the standalone version on CPAN.

Top tip number 2: check everything as you go along in your script and add debugging statements at every turn: extracting the cert, extracting the sig, extracting the signed data, validating that the cert is the one you are expecting, validating the sig, etc. It's very easy to get confused with all this so the more debugging you have the better.

I've wrestled with this kind of thing several times and it seems that every implementation of SAML is subtly different so be prepared to massage the XML into some sort of standard form.

Good luck.

  • Comment on Re: Validating XML Signatures / SSL Certificate question (using Net::SAML)

Replies are listed 'Best First'.
Re^2: Validating XML Signatures / SSL Certificate question (using Net::SAML)
by MattP (Novice) on Apr 05, 2017 at 15:22 UTC

    Thank you Hippo for your response.

    As you have probably worked out, I'm pretty new to this sort of thing, it turns out I sent them my key in order that they can check my signatures from my SAML requests - just haven't got to that bit yet.

    So I now understand that they have signed this with their *private* key, and I can do (whatever it is I need to do) by using their public key which is sent as part of the SAML assertion, and I don't need any of my own keys for this. Is that correct? :p

    I have noticed that the XML::Sig module supplied with Net::SAML2 is wildly different from the standalone, thanks. I have been debugging with this, printing out all the bits as I go.

    I can see that it's calling my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($cert->pubkey) - and the public key in $cert->pubkey looks like a perfectly good public key to me - starting with the text -----BEGIN RSA PUBLIC KEY----, ending with the END - etc.

    Then it calls if ($rsa_pub->verify( $canonical,  $bin_signature )) {

    $bin_signature is a base 64 decoded version of the signature node. $canonical appears to be the value of the "digestValue" node. This is also base64 encoded and I am told it is actually a binary value, but the module doesn't b64 decode this - I don't know if it should or not.

    Either way, trying b64 decoded and the regular $canonical value, the response from this verify command is consistently false.

    Does any of this mean anything to you?!

    Thanks,
    Matt

      So I now understand that they have signed this with their *private* key, and I can do (whatever it is I need to do) by using their public key which is sent as part of the SAML assertion, and I don't need any of my own keys for this. Is that correct? :p

      Yes, that is correct.

      I've had problems in the past with XML::Sig failing to extract the signer's certificate correctly. You might try initialising the XML::Sig object with a local copy of that certificate just in case. eg:

      my $verifier = XML::Sig->new ({ cert => '/path/to/signer/cert.pem' }) +; if ($verifier->verify ($saml_string_decoded) { # now do something with it

      Also this specific version of XML::Sig has proven useful in the past, so you might try it as an alternative to see if it helps any. It does sound now like you are on the right road.

        Thanks again Hippo!

        I've gone right down to basics now and made my own script without XML::Sig but nabbed some bits from it, which is working absolutely fine. This will encrypt a string with the public key and decrypt it with the private. Also sign it with the private and verify it with the public. Im using my own keys and so far so good and getting me understanding what's going on.

        One thing has amazed me however - this line - my $cert = Crypt::OpenSSL::X509->new_from_string($certificate);- I can access the public key from the certificate after this line by using $cert->pubkey. It's different to my own public key however... but still works! How and why I have no idea.. wondering if this might be a clue as to what's going wrong with the SAML.. Can there be multiple public keys??! I may try getting the public key directly from the SAML provider tomorrow, also checking what hashing they are using. Another thread I saw suggested a possible platform difference regarding big and little endian data..

        Cheers :)

        #!/usr/bin/perl use Crypt::OpenSSL::Random; use Crypt::OpenSSL::RSA; use Crypt::OpenSSL::X509; use MIME::Base64; use strict; my $debug = 0; local $/ = undef; my $public_key= "/data/pubkey.pem"; open (FILE, $public_key) or die ("Cant open public key file"); my $public_key_string = <FILE>; close(FILE); my $private_key = "/data/private.pem"; open (FILE, $private_key) or die ("Cant open private key file"); my $private_key_string = <FILE>; close(FILE); my $public_cert = "/data/x509-public.pem"; open (FILE, $public_cert) or die ("Cant open certificate file"); my $certificate = <FILE>; close(FILE); print $certificate; my $cert = Crypt::OpenSSL::X509->new_from_string($certificate); print $cert->pubkey; print $public_key_string; # they're different $public_key_string=$cert->pubkey; # it's different to the one loaded f +rom file, but still works with my existing private key in the rest of + the code - remove this line to test the old $public_key_string value print $public_key_string; my $plaintext = "Some text here"; my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($public_key_string) +; my $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key_strin +g); my $ciphertext = $rsa_pub->encrypt($plaintext); # encrypt with th +e public key my $plaintext_back = $rsa_priv->decrypt($ciphertext); # decrypt with +the private key print "Plain text back is " . $plaintext_back . "\n"; # Works fine if ($debug){ # debugging only print "private key is:\n", $rsa_priv->get_private_key_string() +; print "public key (in PKCS1 format) is:\n", $rsa_pub->get_public_key_string(); print "public key (in X509 format) is:\n", $rsa_pub->get_public_key_x509_string(); } $rsa_priv->use_md5_hash(); # use_sha1_hash is the default $rsa_pub->use_md5_hash(); my $signature = $rsa_priv->sign($plaintext); # sign with private key if ($rsa_pub->verify($plaintext, $signature)) { # verify with public k +ey print "Signature Verified successfully"; } else { print " *** Not verified"; }