Thursday, July 16, 2009

Flex As3crypto + php + rsa + openssl

Flex As3crypto + php + rsa + openssl
Well damn, all I wanted to do was encrypt a little data in my flex application upload it to php and decrypt it there. Of course that turns out to be incredibly complicated. So to all those who will come after me here’s an example of how I generated a 1024 bit RSA key pair with openssl, and used the public key to encrypt a phrase in flex, and the private key to decrypt the phrase in php. First a disclaimer though, I don’t know a lot about encryption or even a lot about what I’m doing but it seems to work and maybe you’ll find it useful.

PHP and openssl
If you want to play along you’re going to need php installed with openssl support. If you make a php file and put this in it:
phpinfo();

and you scroll through the php info page and you don’t see this: Then you don’t have php and openssl working together, if you want to continue you’ll need to figure that out on your own. It’s not too hard though.

As3Crypto Build and Install
I’ll assume you’ve figured out your php and openssl configuration, so now it’s time to get the as3crypto library. DON’T JUST DOWNLOAD THE BINARY VERSION. I can’t stress that enough don’t just go to Hurlant’s website and get he latest binary version. Instead use svn to get the latest code base:

svn checkout http://as3crypto.googlecode.com/svn/trunk/ as3crypto-read-only

If you don’t have svn or you don’t want to compile it yourself… you’ll be sad that you didn’t. Believe me I tried the easy way and it just didn’t work.

Once you get it downloaded get a command line or terminal window and go to the download directory. You can compile with this command:

compc -load-config=build-swc.xml

You’ll probably have to find out where your compc is located if it’s not in your path, and instead run something like:

/pathToCompC/compc –load-config=build-swc.xml

I also ran into the problem of it not knowing where playerglobal.swc was so I ended up editing that line in build-swc.xml to fit my installation. For me it was:

${flexlib}/libs/player/10/playerglobal.swc

When you finally get it to compile it will put the file as3crypto.swc in the bin folder where you found build-swc.xml. Either leave that file there or copy it somewhere you like to keep these things.

Make yourself some keys
The next thing you’re going to want is a key pair. I made mine on my linux server using openssl. For me this is the same server I’m running php on. I’m sure there are a bunch of ways to generate the key pair but I followed the advice found here:

http://www.devco.net/archives/2006/02/13/public_-_private_key_encryption_using_openssl.php

To make a private key just do:

openssl genrsa -out private.pem 1024

Then to extract the public key out of that private key do this:
openssl rsa -in private.pem -out public.pem -outform PEM -pubout

Now you have two files private.pem, and public.pem that we’ll use in the next section.

Flex Code
All right enough BS here’s the code I used to encrypt a small phrase in my Flex-Air application.

            import com.hurlant.crypto.symmetric.ICipher;
            import com.hurlant.crypto.prng.Random;
            import com.hurlant.crypto.Crypto;
            //import com.hurlant.util.Base64;
            import mx.utils.Base64Encoder;
   
            import com.hurlant.util.Hex;
            import com.hurlant.util.der.PEM;
            import com.hurlant.crypto.rsa.RSAKey;


            private function sign():void
            {
             
             var publicKey:String = "-----BEGIN PUBLIC KEY-----\n" +
                 "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIyrrhK7L1JQATG1zu/4ktyDdj\n" +
                 "Y41EIZxa5vUiq+ejUlQXNWmQZw8ATDGV2x/xJEcsZSbPqcxgyqDPQ8kGjQkv1kS/\n" +
                 "5ZP3oru5WSEypVPq1nfMCBoGTspyYIRpS+MjhhhN2X5y0NzR08kQQA81GRvBpmxM\n" +
                 "Ymy1r50CmtcVuIbuKwIDAQAB\n"+
                 "-----END PUBLIC KEY-----\n";
                         

                //var rsa:RSAKey = RSAKey.parsePublicKey(getModulus(), exponent.text);
                var rsaPublicKey:RSAKey = PEM.readRSAPublicKey(publicKey);
                var data:ByteArray = new ByteArray;

                data.writeUTFBytes("fuck this crazy ass crap");
                var dst:ByteArray = new ByteArray;
                rsaPublicKey.encrypt(data, dst, data.length);
               
                var Enc64:Base64Encoder = new Base64Encoder;
                Enc64.encodeBytes(dst);
                trace(Enc64.toString());
    
                //use this on linux side to test
                //openssl rsautl -decrypt -inkey mykey.pem -in test.out -out decrypted.txt
                ///usr/bin/openssl enc -base64 -d -in test.64 -out test.out



            }

The first thing you’ll notice is my public key that I got in the previous step. I have no idea if the \n’s are necessary but I put them there because I saw other people do that so monkey see monkey do.

Next I read the key into an RSAKey.

Then you make yourself a new Byte array to store the phrase you want to encrypt . In this example it’s called data.

Then you write the phrase into the byte array.

Now you make yourself another byte array called dst, this is where the encrypted data will go.

Next you base64 encode the data so you can easily copy and paste it. Base64 encoding just encodes all that encrypted data into normal text characters that you can safely drag around.

Finally you output that data with a trace so you can get it easily. For me the trace outputs:

XTLpqt9mNtyFchsjvFnffEplqaa+LHXCh52DpLi9CxvED4fUq5cWPuTX/1tHeG7deNBiGBiRW02r +LfW3JreVSkkc+sH6rq4ggKWi+1iPZoQd45f2VmwHVTvgC/IQX4eRuyFtSVoheHSLRL8zRO2/M27 K2jx49V+4JFVhtFAbQw=

This is your encrypted base64 encoded data, that we’ll decrypt in the next section.

PHP Time
I don’t know how you plan on getting your encrypted phrase to php, and frankly I don’t care. For me I just wanted to see it work so all I did was copy and paste. Here’s the PHP code I used to decrypt the phrase:

$privkey3 = "-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDIyrrhK7L1JQATG1zu/4ktyDdjY41EIZxa5vUiq+ejUlQXNWmQ
Zw8ATDGV2x/xJEcsZSbPqcxgyqDPQ8kGjQkv1kS/5ZP3oru5WSEypVPq1nfMCBoG
TspyYIRpS+MjhhhN2X5y0NzR08kQQA81GRvBpmxMYmy1r50CmtcVuIbuKwIDAQAB
AoGAbMucEqGQ589SiQfMhRskgw/3Iv0v7/ieNYrqzAyFCDN1qNWiC0IsGKcwPthq
KEjV37I3I1IHkXJ5S5SY+dm1XaSrM0XpycCCz/OYYZMJ4Z5qQNyeCIDt69UXHlaz
AIITK7rLcGIOERVp80ZtTXfVv+2oLo7IQLHxntQaP2sOIQECQQD/v7QdW8Zm2Zmr
jAAcnmvGdctPKZmg2fsembqI8xS3QjsPK4Fky2r4CWFu9V+pfIQH2i6L0P0SDZ5n
wHMcg9LrAkEAyP01wruazceeh2LIMAKsdtOudXCYv/Hm9utdTGTPWDBdAKFFJ2O5
GuicqUXeVqP1DjZTtpQ6MWBEKekXN3YBwQJBAOeW3zFes/Dax08Svd8sjgfvDyYm
U5nXCpGRP9tX08CHVxfLm6Z8leb6B6MEy5WczaqWpmOx4hkBABqPqS1/KfUCQHIh
j0iwPZzC0RuyFVll+0dStwuLT8IXfH8UchtyV2eNtxIngdx3PWKxWlypBzON0Rcr
9GeGTnFBaBOggAWHZkECQCGou7Uu8jWPZZ3tFgtAGV+W5iPdj5LGoGu6L7EYAy6u
6jAj8SDB6UrTPDMx1Jjv87fJPCJy93CJI4PRWTu078Q=
-----END RSA PRIVATE KEY-----";



$result = openssl_pkey_get_private($privkey3);

if ($result)
{
        print "\nPrivate Key OK\n\n";
} else {
        print "\nPrivate key NOT OK\n\n";
}

$crypttext = "XTLpqt9mNtyFchsjvFnffEplqaa+LHXCh52DpLi9CxvED4fUq5cWPuTX/1tHeG7deNBiGBiRW02r
+LfW3JreVSkkc+sH6rq4ggKWi+1iPZoQd45f2VmwHVTvgC/IQX4eRuyFtSVoheHSLRL8zRO2/M27
K2jx49V+4JFVhtFAbQw=";

$crypttext = base64_decode($crypttext);


openssl_private_decrypt($crypttext,$newsource,$result);
echo "
String decrypt : $newsource
"; while ($msg = openssl_error_string()) echo $msg . "
\n";


First you’ll see that I took the private key from the keys we made earlier and put it in this file. Make sure yours looks the same, if you paste your private key without the right new lines it won’t work. At least it didn’t for me so must make sure you format it like you see in the code.

Next we call openssl_pkey_get_private on that private key.

Now you’ll see the encrypted phrase from our Air application in the $crypttext variable.

We base64 decode that to get back to the raw data.

Then we run it through openssl_private_decrypt to decrypt the data.

Finally we just output the data to the screen.

That last little while loop will output any openssl errors that might have occurred, and can be at least a little useful in debugging.

The End
Well I hope that helps someone else who comes along and tries to do the same thing. It was a pain in the ass to figure this all out especially because as3crypto has no documentation. I’m not knocking it though I mean you just got a completely free cryptography library for your flash project right.

One last thing if you, like me, know about as much about cryptography as you do about how to win the lottery, then you may be disappointed to find out you can only use RSA to encrypt a relatively small amount of data (it’s dependant on your key size). So most people use this to either exchange keys, or make a hash of what they’re sending and encrypt that.

Of course if you happen to know a lot about how to win the lottery... drop me an email with some tips.

9 comments:

  1. Very nice tutorial, definitely the most helpful that I came across!

    ReplyDelete
  2. The PHP part (as you said) does not work with long strings. Here's a workaround for it, replace these lines for line 35 above:
    (taken from http://php.net/manual/en/function.openssl-public-encrypt.php)

    $maxlength = 128;
    $newsource = '';
    while ($crypttext) {
    $input = substr($crypttext,0,$maxlength);
    $crypttext = substr($crypttext,$maxlength);
    openssl_private_decrypt($input, $out, $result);
    $newsource .= $out;
    }

    ReplyDelete
  3. hi...
    just a question... when i try to do the same stuff in flex it doesnt work... it says:
    "I DONT KNOW HOW TO HANDLE DER stuff of TYPE 1".... do any knows what can be wrong?

    thanks!

    ReplyDelete
  4. Sergio,

    I think you may be using a "Public Certificate" than a "Public Key".

    ReplyDelete
  5. Jami's example will almost work but the hard coded value of 128 might not work always. Here is a working example:

    $result = openssl_pkey_get_private(file_get_contents('/path/to/private.key'), 'passphrase');
    $details = openssl_pkey_get_details($result);
    $keysize = $details['bits'] / 8;

    $crypttext = base64_decode('Base64 Encoded encrypted string');


    $chunks = str_split($crypttext, $keysize);
    $decrypted = '';
    $errorMessages = array();
    foreach($chunks as $chunk)
    {
    if (!openssl_private_decrypt($chunk, $res, $result))
    {
    while ($message = openssl_error_string())
    {
    $errorMessages[] = $message;
    }
    }
    else
    {
    $decrypted .= $res;
    }
    }

    if ( count($errorMessages) )
    {
    echo "Decryption Failed:
    ";
    print_r($errorMessages);
    }
    else
    {
    echo "
    String decrypted : $decrypted
    ";
    }

    ReplyDelete
  6. Thank you for the explanation! Your code works perfectly. The only thing is that is doesn't work when you try to encrypt using the private key in Actionscript and decrypt using the public key in PHP. Then the following error messages appear:
    error:0906D06C:PEM routines:PEM_read_bio:no start line
    error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01
    error:04067072:rsa routines:RSA_EAY_PUBLIC_DECRYPT:padding check failed

    Any ideas on this? My best guess is a bug in the As3crypto library.

    ReplyDelete
  7. Never mind, you just need to use the "sign" function instead of the "encrypt" function in Actionscript.

    ReplyDelete
  8. If I want to reverse the process, how can I do it? (PHP to Flex)

    Greetings.

    ReplyDelete
  9. this is great tutorial.
    But i hve doubt. If someone decoded swf and get the public key. Is it possible to hack the data? Am a flash developer and using the normal as3-php communication , i found out my data is being hacked by someone..

    ReplyDelete