Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Perl and Encrypted SAML Token

by SquirrelHead (Initiate)
on Jul 22, 2015 at 00:05 UTC ( #1135738=perlquestion: print w/replies, xml ) Need Help??

SquirrelHead has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, We are trying to decrypt the following SAML token using Perl.
<?xml version="1.0" encoding="UTF-8"?> <trust:RequestSecurityTokenResponseCollection xmlns:trust="http://docs"> <trust:RequestSecurityTokenResponse> <trust:Lifetime> <wsu:Created xmlns:wsu=" +4/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2015-07-07T16:09:50 +.137Z</wsu:Created> <wsu:Expires xmlns:wsu=" +4/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2015-07-07T17:09:50 +.137Z</wsu:Expires> </trust:Lifetime> <wsp:AppliesTo xmlns:wsp=" +9/policy"> <EndpointReference xmlns=" +sing"> <Address></Address> </EndpointReference> </wsp:AppliesTo> <trust:RequestedSecurityToken> <EncryptedAssertion xmlns="urn:oasis:names:tc:SAML:2.0:ass +ertion"> <xenc:EncryptedData xmlns:xenc=" +/04/xmlenc#" Type=""> <xenc:EncryptionMethod Algorithm="http://www.w3.or +g/2001/04/xmlenc#aes256-cbc" /> <KeyInfo xmlns=" +"> <e:EncryptedKey xmlns:e=" +1/04/xmlenc#"> <e:EncryptionMethod Algorithm="http://www."> <DigestMethod Algorithm="http://www.w3" /> </e:EncryptionMethod> <KeyInfo> <o:SecurityTokenReference xmlns:o="htt +p:// +t-1.0.xsd"> <X509Data> <X509IssuerSerial> <X509IssuerName>CN=SeeMyDa +ta, OU=Web Security, O=SomeCompany, C=GB</X509IssuerName> <X509SerialNumber>12698354 +7625965664326654654</X509SerialNumber> </X509IssuerSerial> </X509Data> </o:SecurityTokenReference> </KeyInfo> <e:CipherData> <e:CipherValue>encrypted string_1 goes + here</e:CipherValue> </e:CipherData> </e:EncryptedKey> </KeyInfo> <xenc:CipherData> <xenc:CipherValue>encrypted string_2 goes here +</xenc:CipherValue> </xenc:CipherData> </xenc:EncryptedData> </EncryptedAssertion> </trust:RequestedSecurityToken> <trust:RequestedAttachedReference> <SecurityTokenReference xmlns=" +wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:d4p1="h +ttp://" d4 +p1:TokenType=" +file-1.1#SAMLV2.0"> <KeyIdentifier ValueType=" +ss/oasis-wss-saml-token-profile-1.1#SAMLID">_8fad4766-e906-4f78-b8aa- +b1abdfe3f621</KeyIdentifier> </SecurityTokenReference> </trust:RequestedAttachedReference> <trust:RequestedUnattachedReference> <SecurityTokenReference xmlns=" +wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:d4p1="h +ttp://" d4 +p1:TokenType=" +file-1.1#SAMLV2.0"> <KeyIdentifier ValueType=" +ss/oasis-wss-saml-token-profile-1.1#SAMLID">_8fad4766-e906-4f78-b8aa- +b1abdfe3f621</KeyIdentifier> </SecurityTokenReference> </trust:RequestedUnattachedReference> <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust: +TokenType> <trust:RequestType> +00512/Issue</trust:RequestType> <trust:KeyType> +2/Bearer</trust:KeyType> </trust:RequestSecurityTokenResponse> </trust:RequestSecurityTokenResponseCollection>
If our understanding is correct we need to first extract the signature key value from the encrypted string held at string_1 above. We have created the following code to achieve this.
#!/usr/bin/perl ## Remove this when errors are resolved use diagnostics -verbose; #print warning diagnostics use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use Convert::PEM; use Crypt::OpenSSL::RSA; use MIME::Base64; use strict; my $private_key = '/path/to/privatekey.pem'; my $encrypted_string =q(encrypted string_1 value); my $password = 'OurPassword'; my $key = decryptPrivate($private_key,$password,$encrypted_string); print "Content-type: text/html\n\n"; print "$key<br>"; exit; sub decryptPrivate { my ($private_key,$password,$string) = @_; my $key_string = readPrivateKey($private_key,$password); return(undef) unless ($key_string); # Decrypt failed. my $private = Crypt::OpenSSL::RSA->new_private_key($key_string) || die "$!"; $private->decrypt(decode_base64($string)); #$private->decrypt($string); } sub readPrivateKey { my ($file,$password) = @_; my $key_string; $key_string = decryptPEM($file,$password); } sub decryptPEM { my ($file,$password) = @_; my $pem = Convert::PEM->new( Name => 'RSA PRIVATE KEY', ASN => qq( RSAPrivateKey SEQUENCE { version INTEGER, n INTEGER, e INTEGER, d INTEGER, p INTEGER, q INTEGER, dp INTEGER, dq INTEGER, iqmp INTEGER } )); my $pkey = $pem->read(Filename => $file, Password => $password); return(undef) unless ($pkey); # Decrypt failed. $pem->encode(Content => $pkey); }
Despite trying every combination we can think of we just cannot seem to crack the decryption here. I am now starting to wonder if our logic is flawed and we are processing the SAML token incorrectly or there is something else that needs to be done. I've tied uri_decoding without success, different cipher values and we now have most of the Crypt modules installed on our server including Crypt::RSA that I thought looked promising. That generates an error though. We been struggling with this for over a week now so it is time to seek help. Thanks in advance to anyone who is able to help us with this. Probably also worth adding that the output that we get when running the above script appears as: �axl������Q� ��&�;�݉B6,�}Y�h9r If I change the encrypted string in any way then I get a "oaep encoding error" instead so it looks like the code works but the cipher or hash or something is wrong perhaps?

Replies are listed 'Best First'.
Re: Perl and Encrypted SAML Token
by GrandFather (Saint) on Jul 22, 2015 at 10:07 UTC

    I went to run your script which at first glance looks like a reasonably stripped down test script, but then realised I needed a private key file which wasn't provided. It would make the "sample" code easier to use it it embedded a private key or generated a private key file.

    At a first glance the output string you show suggests some encoding/decoding (not encryption/decryption) issue.

    Premature optimization is the root of all job security


      Thank you for taking the time to reply to me. I'm guessing I shouldn't upload the original private key file here so will explaining the commands that I used to create the key file suffice or do I need to create a separate file in order to test. I'm listing the commands first as I am starting to wonder if I have done something wrong in the creation of the certificate.

      On Linux I ran the following command.

      openssl genrsa -des3 -out private_key.pem 2048

      I selected a passphrase and this is the same string that goes into the code above in the $password string.

      I then created the self signed certificate with the next command.

      openssl req -new -x509 -key private_key.pem -out private_cert.pem -days 9125

      At this point I entered details for the certificate DN information.

      Finally, I created the public certificate from the private certificate.

      openssl x509 -inform PEM -in private_cert.pem -outform DER -out public.cer

      The resultant public.cer certificate is what we passed to the third party and they are using this file as the basis of the encryption for teh SAML token that is sent to us.

      If the above looks to be correct then I'll create a test private key and look to add the value to the code.

      Thanks again.

Re: Perl and Encrypted SAML Token
by codiac (Beadle) on Jul 22, 2015 at 10:13 UTC

      Thank you for pointing us to this code, we are going through it to see if it can help us but some bits certainly look interesting at this stage.

Re: Perl and Encrypted SAML Token
by rdfield (Priest) on Jun 12, 2017 at 13:01 UTC
    Not sure if this helps, since the SAML2 Assertion XML is somewhat different, but here it is anyway:

    1. generate a public/private key pair for encryption use

    2. went to and generated an encrypted SAML2 Assertion, using RSA_OAEP_MGF1P for "Encrypted Method for key", "AES128_CBC" for "Encrypted Method for the data" and the public cert from step 1. Saved the output to a file, encrypted_assertion.xml

    3. My private key was in PKCS#8 format, so generated a PKCS#1 version of it using openssl rsa -in myenc.key -out myenc1.key (the Perl code I use only accepts PKCS#1 format)

    4. from the SAML2 spec I see that the first 128bits of the encrypted data is actually the IV, (section 5.2.2)

    5. using the code from to retrieve the encrypted key from the XML (hence the PKCS#1 version of the private key in step 3, to use in the CBC decoding of the data, I came up with the following code (after much trial and error with the Crypt::CBC parameters):

    use strict; use warnings; use Crypt::OpenSSL::RSA; use XML::Simple; use File::Slurp; use MIME::Base64 qw(decode_base64); use Convert::PEM; use Crypt::OpenSSL::AES; use Crypt::CBC; my $xml = read_file("encrypted_assertion.xml"); my $pem = Convert::PEM->new( Name => 'RSA + PRIVATE KEY', ASN => qq( RSAPrivateKey SEQUENCE { version INTEGER, n INTEGER, e INTEGER, d INTEGER, p INTEGER, q INTEGER, dp INTEGER, dq INTEGER, iqmp INTEGER } )); my $pemkey = $pem->read(Filename => "myenc1.key"); my $key = Crypt::OpenSSL::RSA->new_private_key($pem->encode(Content => + $pemkey)); my $xml_data = XMLin($xml); my $CipherKey = decode_base64($xml_data->{"dsig:KeyInfo"}{"xenc:Encryp +tedKey"}{"xenc:CipherData"}{"xenc:CipherValue"}); my $aeskey = $key->decrypt($CipherKey); my $CipherData = decode_base64($xml_data->{"xenc:CipherData"}{"xenc:Ci +pherValue"}); my $cipher = Crypt::CBC->new(-key => $aeskey, -cipher => "Crypt::OpenSSL::AES", -iv => substr($CipherData,0,16), -literal_key => 1, -header => 'none', -keysize => length($aeskey)); print $cipher->decrypt ( substr($CipherData ,16) );
    There are some junk characters at the end of the output, I guess it's some padding.

    This takes 0.455s to run.

    Using perl -e 'print `xmlsec1 --decrypt --privkey-pem myenc.key encrypted_assertion.xml`' takes 0.015s, and outputs no junk.


Re: Perl and Encrypted SAML Token
by Anonymous Monk on Jul 22, 2015 at 00:11 UTC
    Hmm, have you tried using something SAML from CPAN? Or maybe compare/contrast to how they do the decryption part ?


      Thanks for replying. We're able to extract the information from the SAML token but it's the actual decryption of the encrypted string that is causing us issues.

      From what I have seen of things like NET::SAML they just allow you to manipulate the SAML, there is no encryption or decryption ability. I may easily be wrong though as although we've worked with Perl for years this is our first time of working with an encrypted SAML token. Are you aware of any SAML modules that include encryption and decryption abilities? Thanks.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1135738]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2021-04-19 10:29 GMT
Find Nodes?
    Voting Booth?

    No recent polls found