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

I have a small problem. I am attempting to decode a cookie which appears to be Base64 encoded. However, using MIME::Base64's decode_base64 function is giving me something which cannot be parsed.

Below is what the company security web page tells me about my own cookie. However, I cannot get their "cyphertext" to decode to something which can be parsed into what they have in the table.

Note that they don't show a few fields near the end, and I know for a fact that the last field is an MD5 hash of an RSA key, so it won't be readable. But the rest . . . how am I supposed to parse the result of decode_base64? It's garbage!

Please help!!!
Your WSL certificate/cookie in plain text is:
issue date Fri Mar 16 19:27:12 2001
exp. date Sat Mar 17 05:27:12 2001
issuer issuer here
cdsid JROBISO2
ip addr. 19.82.24.80
aci group NONOVVM
dept no. 1239D709
org. code ENG
employee type V
mr role N
organization organization here
company company here
div. abbr. VP

Your WSL cookie in ciphertext:
OrJpEDqy9bBwcm9mc3B3MkB3ZWJmYXJtLmRlYXJib3JuLmZvcmQuY29tAGpyb2Jpc28yADE5LjgyLjI0LjgwAE5PTk9WVk0AMTIzOUQ3MDkARU5HAFYATgBWSVNURU9OAFZJU1RFT04uQ09SUE9SQVRJT04AVlAANTIwNQBERUFSQk9STgBNSQBVU0EATlVMTC5jZW50b2tzAACKnsXgqAYrukdta7sDd65KLWOchc53KcNMRqeTYX4rLmr/XID4lE2I2cNyIjZApSG9D0JWR1jUzoo3t53alPsL
the code I am using is:

#!/usr/local/bin/perl -w # # this is an attempt to make a cleaner read of the Ford-WSL cookie use strict; use CGI qw/:standard/; use CGI::Carp qw(fatalsToBrowser); use MIME::Base64; use CGI::Cookie; my $query = new CGI; my %cookies = fetch CGI::Cookie; my $cookie = $cookies{'Cookie-Name'}->value; print $query->header; print $query->start_html; print "<h1>Cookie</h1><br>\n"; print "Straight cookie: $cookie\n<br><br>\n"; my $decoded = decode_base64($cookie); #write_file($decoded); print "and Base64 decoded: $decoded <br><br>\n"; print $query->end_html;


What does this little button do . .<Click>; "USER HAS SIGNED OFF FOR THE DAY"

Edit: chipmunk 2001-03-16

Edit: chipmunk 2001-03-16

Replies are listed 'Best First'.
Re: Base64 Encoded cookie is giving me headaches!
by chipmunk (Parson) on Mar 17, 2001 at 01:02 UTC
    Viewing the decoded string in the debugger, it appears that all the data is there...

    The first 8 bytes are probably the two dates, perhaps packed seconds since epoch. unpack those and convert them to dates with localtime. The remaining data is null-separated; split /\0/ to get the remaining fields. The last one does appear to be an MD5 hash, as you said.

    Update: How about a code example? :)

    #!/usr/local/bin/perl -w use strict; use CGI qw/:standard/; use CGI::Carp qw(fatalsToBrowser); use MIME::Base64; use CGI::Cookie; my $query = new CGI; my %cookies = fetch CGI::Cookie; my $cookie = $cookies{'Cookie-Name'}->value; print $query->header; print $query->start_html; print "<h1>Cookie</h1><br>\n"; print "Straight cookie: $cookie\n<br><br>\n"; my $decoded = decode_base64($cookie); my($time1, $time2) = unpack 'NN', $decoded; substr($decoded, 0, 8) = ''; print join "<BR>", "Decoded cookie:", scalar(localtime $time1), scalar(localtime $time2), split /\0/, $decoded; print $query->end_html;
      Here is their web page (at least the part I can release)

      Each cookie contains the following fields:

      Issue Time Expire Time Issuer Userid IP Address ACIGROUP Location Other Data Signature
      Issue Time
      The time when the cookie was issued.
      Expire Time
      The time when the cookie will no longer be valid.
      Issuer
      The name of the issuer of the cookie.
      Userid
      The userid represented by the cookie.
      IP Address
      The IP address for which the cookie is valid.
      ACIGROUP
      The security group from PROFS for the user. This is useful to determine if the user is a dealer or supplier.
      Location
      The location code for suppliers. This is not useful inside the Company.
      Other Data
      Other fields may be defined in the future.
      Signature
      The RSA encrypted MD5 digest of the rest of the cookie.

      Security of the System


      The security of the system resides in the use of public key cryptography to digitally sign the cookie. The issuer of the cookie computes a special checksum of the cookie (using the RSA Data Security, Inc. MD5 Message-Digest Algorithm) and then encrypts this checksum with the issuer's private key. The issuer is the only holder of the private key.

      The receiver looks up the issuer's public key, and decrypts the checksum. The receiver compares this checksum with one that it computes, and compares the two. If they are equal, then:

      • The cookie was issued by the issuer, and
      • The cookie has not been tampered with.

      Web Single Logon's private keys are kept on a secured server. The public key is distributed with the Web Single Logon kit. A trust relationship is established with the issuer. When the cookie is validated, and has not expired, then the identity encoded in the cookie is trusted. This establishes the logon.


      Password Validation


      Centralizing the logon process allows a great flexibility in maintaining a central Web password database. The Web Single Logon Password Server currently uses the PROFS systems or X.500 for non-profs users to validate logons. This gives Web Single Logon secured servers access to a base of over 95,000 users.



      What does this little button do . .<Click>; "USER HAS SIGNED OFF FOR THE DAY"
        Okay, here's some pseudo-code which I think is the right way to use the last value in the null-separated string of values:
        use Some_RSA_Module; use Digest::MD5 qw/ md5 /; # decode base64 as before, then: my $encrypted = substr($cookie, -64); substr($cookie, -65) = ''; # strip the null and the security fiel +d my $checksum = Some_RSA_Module::unencrypt($some_public_key, $encrypted +); my $new_checksum = md5($cookie); if ($new_checksum eq $checksum) { print "Checksums match, cookie data is valid.\n"; } else { print "Checksums don't match, cookie data has been tampered with.\n" +; }
        Obviously, I don't know how to do the actual decryption of the security field. :)

        The other question is how the md5 hash is being generated; they could be used md5_base64 or md5_hex instead of plain md5. The length of the decrypted checksum will tell you which...

        I used substr() to get the security field, instead of splitting on nulls, because I was cautioned in the CB that the security field might contain embedded nulls itself. :)