Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Encrypt using AES(block size 128-bit) in CBC

by mikemc24 (Novice)
on Jun 14, 2015 at 09:11 UTC ( [id://1130351]=perlquestion: print w/replies, xml ) Need Help??

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

I am trying to encrypt a string

The string should then be encrypted using AES(block size 128-bit) in CBC mode with PKCS#5 padding using the provided password as both the key and initialisation vector and encode the result in hex (making sure the letters are in upper case).
Prepend the ‘@’ sign to the beginning of the encoded result.

my code is

#!/usr/bin/perl print "content-type: text/html \n\n"; use strict; use warnings; use Crypt::CBC; use Crypt::Cipher::AES; my $prepend = '@'; my $user_string = 'VendorTxCode=TxCode-1310917599-223087284&Amount=36. +95&Currency=GBP&Description=description&CustomerName=FnameSurname&Cus +tomerEMail=customer@example.com&BillingSurname=Surname&BillingFirstna +mes=Fname&BillingAddress1=BillAddress Line1&BillingCity=BillCity&Bill +ingPostCode=W1A1BL&BillingCountry=GB&BillingPhone=447933000000&Delive +ryFirstnames=Fname&DeliverySurname=Surname&DeliveryAddress1=BillAddre +ssLine 1&DeliveryCity=BillCity&DeliveryPostCode=W1A1BL&DeliveryCountr +y=GB&DeliveryPhone=447933000000&SuccessURL=https://example.com/succes +s&FailureURL=https://example.com/failure'; my $key = '55a51621a6648525'; my $iv = '55a51621a6648525'; my $cipher = Crypt::CBC->new( -cipher => 'Crypt::Cipher::AES', -key => $key, -iv => $iv, -padding => 'standard', -header => 'none', -blocksize => '16' ); my $encrypted = uc $cipher->encrypt_hex($user_string); my $decrypted = $cipher->decrypt_hex($encrypted); print $prepend,$encrypted, "\n"; print $decrypted, "\n";

The encrypted result should be

2DCD27338114D4C39A14A855702FBAB2EF40BCAC2D76A3ABC0F660A07E9C1C921C2C75 +5BA9B59C39F882FBF6DFED114F23141D94E50A01A665B1E31A86C07CACD1BB8EF5B6C +F2C23D495CD 79F9C0F678D61773E7A1AA30AA5B23D56503FC0B52AC0694A8C341263D2C5FE1BAD93B +DB94726761E155E900448F644AF1F67BE1AC77E852B9D90809A44F258E9478B6D8C1C +4ED58759263E7DBF 8871C6592287C0358F36F4EEC326CEDDD440DA2FED8AB35F1B630A5C6FA671E4D78CC8 +CACECF9DFDC31D6C5EC8270FB21E297E2C2E14F99A04223EFFD4F0006D440E78A3D2C +7140EC8F123D24 7B75E7482AE98858DA34D37EDE6D7C69AA74391F559305CF675ADB3615244A107ABBB6 +AF26E29A2FFA059B12688D90FE09E0DE069325BFF3587A695F5DA36E4809B69CC9A37 +034F166B63B5A62 B986F4DA34E9AC9516AFDE70642EC7DAD1AEBA93A1F347D6AC7046E967DCBFE7ACFCEE +5DAFC0B29F1765032B3060EBE565CBD57D092075D15CF12725199C688605B2E0F1056 +98CE3ADD04361C A9D620C187B90E3F9849445B5C3C0FDF1768BFFD61F97E51316826F4F10E0E3E668F0A +9F5ED9CCDA6F2C7CC957F12DB48F9041482E3D035E7A91852C404BFA35FED947E71F5 +7B871DFAC6AF4FF2 9F4513A4A80B2D7ECC9D19D47ED04FA99CDFC881DFA771E1EA4F3F9B2C5AC673EF3DA2 +699A309CC8522993A63CB8D45D3CDF09B1DFDC573CD19679B250AD672450B5042F201 +670B464505DCAE F59E2C67ABACC9AE2EEE793CE191FEBF66B8FAF4204EFFB359246B9C99FB52805C4637 +5FF35140F74707FBC73C7731A28A2C883A
but I get
@4AA9DF4F8981CCD08D468C3E4468BB9D770A429A2BBA4053AC59B03B7BCC2EB1604B2 +13489AB4DF1AD39D6A0050806AB667945BFE18173C769773DAA957A0950F7AAAB8BBC +E90AB63B836A96D2B91842AD43662CED25175E83C57B689E3A2FAB5BB502329434F73 +5847CDBF6C98719E5A24F4E89B529DE86B714BC0BA5F4AE6BC1DE1C089DFFF088F03A +2831891373FBAA6D5E0CA96AD3111DB1B4EBE04CC581F54963360CB67055359B1FC86 +06AD5435A27D6474747FA44402879D2A18C20ABC9639D07C31B5AFC2CB015268955EC +C897651094DC8C34C764D2E74EE23FDD9892DCB7A6F260D0C8999E9F403F0917B5EE4 +16D56A61434DE1FD2F3357461C3B09E3892D5EE0D4D1459ACD667DBD0B98A40550C8C +73815451FE5A24FA63325EF0D2A3E89560462957EDA6862808FFABE0F50ED425DA64E +9625FD2374FD2EA4BAF0FCCE868FDEEC2D87058C8E0F8E19DF01FBBD252F10FFA2F7E +C09C0256A3BE36EC4D6D22B79424AB2F5CD500B611C669712DB3A70AABE50B2BC494E +72C9F9A229C01CE6156DFDC3CA1838AF2C8153174A0C8ED685BC9A72DC35B51CEBAA9 +498F1F1D5A8D8B6AD2BAE7645E739D0D05C094F7349881FE4B1E2A5ACD57CB223B675 +B6C0509A3675B874B562B71D3A07127950BF17B3579C32496002ED25DD901CF3E6F96 +45AB38113032873AF55D45BDEEABE291869121416466AB804A069E1B902E821797563 +46C61431CEE7527C44A209C7FDA3DDF723C7DE98A9CD2DBE1B1BCE2D169EF074989B0 +201BF1F6E6936EE8C47ACC374DE145A4FBBC569427DA6DCA
am I over simplifying the encryption method or am I misunderstanding what has to be done.
Advice and help please.
mikemc24

Replies are listed 'Best First'.
(solved) Re: Encrypt using AES(block size 128-bit) in CBC
by wrog (Friar) on Jun 14, 2015 at 21:41 UTC
    Ok, nailed it. There were two problems:
    1. I was correct in that you were feeding the wrong key, but incorrect about why.

      It turns out you need to set -literal_key => 1 because otherwise, as per Crypt::CBC, it helpfully uses a hash of the key you provide rather than the key itself.

      And then you also need to set -keysize => 16, because otherwise it complains about you not providing a 32-byte key (and apparently once you set keysize, setting -blocksize => ... makes no difference at all).

    2. You were getting the plaintext wrong. Going back to the source (page 37 of FORM_Integration_and_Protocol_Guidelines_130515.pdf which I assume is what you're working from), which I'll quote
      VendorTxCode=TxCode-1310917599-223087284&Amount=36.95&Currency=GBP&Des +cription=description&CustomerName=Fname Surname&CustomerEMail=customer@example.com&BillingSurname=Surname&Bill +ingFirstnames=Fname&BillingAddress1=BillAddress Line 1&BillingCity=BillCity&BillingPostCode=W1A 1BL&BillingCountry=GB&BillingPhone=447933000000&DeliveryFirstnames=Fna +me&DeliverySurname=Surname&DeliveryAddress1=BillAddress Line 1&DeliveryCity=BillCity&DeliveryPostCode=W1A 1BL&DeliveryCountry=GB&DeliveryPhone=447933000000&SuccessURL=https://e +xample.com/success&FailureURL=https://example.com/failur e
      and the question you want to ask when you see shit like this is why are the line-breaks where they are.

      Sometimes it's because it's running all the way to the end of the (however many characters wide) space provided, as is clearly happening with the word "failure" on the last line, but other places it's clearly doing something else, and the other thing that it's doing is breaking at places where there's a space available to break at because whatever idiot composed this document just did a regular paragraph fill, which helpfully left the spaces at the ends of the lines where you can't seem them unless you drag the mouse over the text in the original .pdf and watch what happens with the highlighting (and then Adobe or Windows helpfully strips out the trailing spaces when you try to paste to another application).

      In particular, every place where the postcode "W1A1BL" appeared, it's actually supposed to be "W1A 1BL" (and if the last time I'd been to Britain were more recently than 1997, I might have noticed this sooner).

      Also "CustomerName=Fname Surname", and so on. Putting it all together we get
      my $user_string = 'VendorTxCode=TxCode-1310917599-223087284&Amount=36. +95&Currency=GBP&Description=description&CustomerName=Fname Surname&Cu +stomerEMail=customer@example.com&BillingSurname=Surname&BillingFirstn +ames=Fname&BillingAddress1=BillAddress Line 1&BillingCity=BillCity&Bi +llingPostCode=W1A 1BL&BillingCountry=GB&BillingPhone=447933000000&Del +iveryFirstnames=Fname&DeliverySurname=Surname&DeliveryAddress1=BillAd +dress Line 1&DeliveryCity=BillCity&DeliveryPostCode=W1A 1BL&DeliveryC +ountry=GB&DeliveryPhone=447933000000&SuccessURL=https://example.com/s +uccess&FailureURL=https://example.com/failure';

    With those two changes, I'm getting the right ciphertext. (It took a bit of doing but with block ciphers used in one of the chaining modes, if you're getting something that's correct up to a point, that tells you which block your mistake is in and you go from there...)

    (and apparently, yes, they really are using 16-byte keys consisting entirely of bytes in the hex-digit range, which effectively means they're using 8-byte keys. Just for grins, I looked at their sample .php source and there's no provision for providing arbitrary keys and packing them. These people are screaming to get hacked.)

      Many thanks for correcting the code and also for teaching me to never copy and paste and assume its correct.

      Very Good wrog,

      ++ Well Done!

      Regards...Ed

      "Well done is better than well said." - Benjamin Franklin

      I finally managed to figure out what to do to migrate a Perl shopping cart to SagePay version 3.0. I went down a ton of blind alleys and this was an excellent article but I had problems installing Crypt::Cipher::AES. The solution I found that works avoiding a bunch of SagePay Gotchas is:

      use Crypt::CBC; # The Sagepay test login is at: # https://test.sagepay.com/mysagepay # Login using your administrator username and password. If # you don't know what they are contact SagePay support and # they will send a reset link to the registered email # address of the administrator. After you login you # will see Encryption Password: <16 character long string> my $id = <16 char long Encryption Password>; # e.g. AbcdeFghiJkLmNoP my $iv = $id; my $cipher = Crypt::CBC->new( -key => $id, -iv => $iv, -cipher => 'OpenSSL::AES', -literal_key => 1, -header => "none", -padding => "standard", -keysize => 16 ); $crypt = uc $cipher->encrypt_hex($crypt); $crypt = "@".$crypt;

      $crypt is what you submit via your shopping cart CGI form to SagePay. For the test environment the URL to submit your form to is:

      https://test.sagepay.com/gateway/service/vspform-register.vsp

      To go live you need the live Encryption Password. Its not the same as the test Encryption Password. You can ask SagePay support where to find this.

      For the live environment the URL to submit your form to is:

      https://live.sagepay.com/gateway/service/vspform-register.vsp

      I hope this helps someone else avoid spending ages working through this like I did.

Re: Encrypt using AES(block size 128-bit) in CBC
by wrog (Friar) on Jun 14, 2015 at 12:21 UTC
    (Update: no, this isn't it. see my other answer)

    Looks like you're providing the key and the iv in hex format, whereas I'm pretty sure you need to provide the actual key/iv bytes directly (that your encrypted string is coming out in hex format is because you're using encrypt_hex) which means you instead need something like

    -key => pack('H32',$key), -iv => pack('H32',$iv),
    but I don't know for sure since the hex you've provided for your key/iv only specifies 8 bytes and AES requires 16.

      But aren't they just strings of 16 one-byte chars?

        strings of 16 bytes, yes. But the question is which 16 bytes? Each byte can be anything in the {0..255} range; you're not limited to those that are printable or those that are hexdigits (i.e., [0-9a-f] = {48..57,97..102})

        So when you supply a key that starts with '55..', as the code was originally written, that specifies a key whose first two bytes are both asc('5') = 53, but if what was intended was a key whose first byte is 0x55 = 85, then you need to pack and you also need to find out what the other 8 bytes of the key were supposed to be (if the goal is to produce a particular ciphertext).

        And I could be wrong about what was intended, but it's suspicious to me that they're using a key consisting entirely of hex-digit bytes (which, for one thing, would be a horrible choice of key from a security point of view; even if it is only an example you don't want to give people the idea that that's how you choose your keys)

Re: Encrypt using AES(block size 128-bit) in CBC
by flexvault (Monsignor) on Jun 14, 2015 at 15:57 UTC

    Welcome mikemc24,

    I downloaded your code and verified your results. I made some small changes:

    my $encrypted = $cipher->encrypt_hex($user_string); my $decrypted = $cipher->decrypt_hex($encrypted); print $encrypted, "\n\n"; print $decrypted, "\n\n"; if ( $user_string eq $decrypted ) { print "Good!\n"; }
    I got the 'Good!' result, so I think your Perl script is working.

    The way it looks to me, you need to verify that the exact '-key' and '-iv' are correct. If they are correct, then your source string for comparison should be verified as being correct.

    Good Luck...Ed

    "Well done is better than well said." - Benjamin Franklin

      I'm really confused now, the result I need is what the provider requires, so is it the hex encryption that's wrong, is there another form of Perl hex encryption decryption I need to use.

        Dear Monks,

          "...using the provided password as both the key and initialisation vector..."

        That looks like your problem. and we can't verify the results without knowing the exact inputs to the conversion. I suspect, as others have stated, that you are taking the 8 byte password and converting to hex to get the 16 bytes that AES requires. That will definitely give you a wrong answer. The way encryption works is: change any character and you get an entirely different encrypted result.

        Ask you client for the exact '-key' and '-iv' and then see if it works. If he gave you the password, then the '-key' and '-iv' values should be 'no problem'.

        Regards...Ed

        "Well done is better than well said." - Benjamin Franklin

Re: Encrypt using AES(block size 128-bit) in CBC
by u65 (Chaplain) on Jun 14, 2015 at 10:10 UTC

    I assume you are taking into account the newlines in the input string?

      It's what the provider (Sagepay)says should be the result of the encryption. When I decrypt the expected result using my encryption method there is nothing recognisable in the output so I must be doing something wrong somewhere.

        Can you provide a link to your provider's instruction ref the situation? I don't see you doing anything wrong in your use of the modules.

      Yes, It is one row in the query.

        Okay, I see the string is one line now. And I get the same results as you. Why do you think that is wrong? How do you get the other result?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1130351]
Approved by tye
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (3)
As of 2024-04-19 22:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found